8ea0a94850f43ae7ea85b9d9c421b5ac5f974816
[WebKit-https.git] / JavaScriptCore / kjs / string_object.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5  *  Copyright (C) 2004 Apple Computer, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #include "config.h"
24 #include "string_object.h"
25
26 #include "value.h"
27 #include "object.h"
28 #include "types.h"
29 #include "interpreter.h"
30 #include "operations.h"
31 #include "reference_list.h"
32 #include "regexp.h"
33 #include "regexp_object.h"
34 #include "error_object.h"
35 #include <stdio.h>
36 #include "string_object.lut.h"
37
38 using namespace KJS;
39
40 // ------------------------------ StringInstance ----------------------------
41
42 const ClassInfo StringInstance::info = {"String", 0, 0, 0};
43
44 StringInstance::StringInstance(JSObject *proto)
45   : JSObject(proto)
46 {
47   setInternalValue(jsString(""));
48 }
49
50 StringInstance::StringInstance(JSObject *proto, const UString &string)
51   : JSObject(proto)
52 {
53   setInternalValue(jsString(string));
54 }
55
56 JSValue *StringInstance::lengthGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot &slot)
57 {
58     return jsNumber(static_cast<StringInstance*>(slot.slotBase())->internalValue()->toString(exec).size());
59 }
60
61 JSValue *StringInstance::indexGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot &slot)
62 {
63     const UChar c = static_cast<StringInstance *>(slot.slotBase())->internalValue()->toString(exec)[slot.index()];
64     return jsString(UString(&c, 1));
65 }
66
67 bool StringInstance::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot)
68 {
69   if (propertyName == lengthPropertyName) {
70     slot.setCustom(this, lengthGetter);
71     return true;
72   }
73
74   bool ok;
75   const unsigned index = propertyName.toArrayIndex(&ok);
76   if (ok) {
77     const UString s = internalValue()->toString(exec);
78     const unsigned length = s.size();
79     if (index < length) {
80     slot.setCustomIndex(this, index, indexGetter);
81     return true;
82     }
83   }
84
85   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
86 }
87
88 void StringInstance::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
89 {
90   if (propertyName == lengthPropertyName)
91     return;
92   JSObject::put(exec, propertyName, value, attr);
93 }
94
95 bool StringInstance::deleteProperty(ExecState *exec, const Identifier &propertyName)
96 {
97   if (propertyName == lengthPropertyName)
98     return false;
99   return JSObject::deleteProperty(exec, propertyName);
100 }
101
102 ReferenceList StringInstance::propList(ExecState *exec, bool recursive)
103 {
104   ReferenceList properties = JSObject::propList(exec,recursive);
105
106   //### FIXME: should avoid duplicates with prototype
107   UString str = internalValue()->toString(exec);
108   for (int i = 0; i < str.size(); i++)
109     properties.append(Reference(this, i));
110   return properties;
111 }
112
113 // ------------------------------ StringPrototype ---------------------------
114 const ClassInfo StringPrototype::info = {"String", &StringInstance::info, &stringTable, 0};
115 /* Source for string_object.lut.h
116 @begin stringTable 26
117   toString              StringProtoFunc::ToString       DontEnum|Function       0
118   valueOf               StringProtoFunc::ValueOf        DontEnum|Function       0
119   charAt                StringProtoFunc::CharAt DontEnum|Function       1
120   charCodeAt            StringProtoFunc::CharCodeAt     DontEnum|Function       1
121   concat                StringProtoFunc::Concat DontEnum|Function       1
122   indexOf               StringProtoFunc::IndexOf        DontEnum|Function       1
123   lastIndexOf           StringProtoFunc::LastIndexOf    DontEnum|Function       1
124   match                 StringProtoFunc::Match  DontEnum|Function       1
125   replace               StringProtoFunc::Replace        DontEnum|Function       2
126   search                StringProtoFunc::Search DontEnum|Function       1
127   slice                 StringProtoFunc::Slice  DontEnum|Function       2
128   split                 StringProtoFunc::Split  DontEnum|Function       2
129   substr                StringProtoFunc::Substr DontEnum|Function       2
130   substring             StringProtoFunc::Substring      DontEnum|Function       2
131   toLowerCase           StringProtoFunc::ToLowerCase    DontEnum|Function       0
132   toUpperCase           StringProtoFunc::ToUpperCase    DontEnum|Function       0
133   toLocaleLowerCase     StringProtoFunc::ToLocaleLowerCase DontEnum|Function    0
134   toLocaleUpperCase     StringProtoFunc::ToLocaleUpperCase DontEnum|Function    0
135 #
136 # Under here: html extension, should only exist if KJS_PURE_ECMA is not defined
137 # I guess we need to generate two hashtables in the .lut.h file, and use #ifdef
138 # to select the right one... TODO. #####
139   big                   StringProtoFunc::Big            DontEnum|Function       0
140   small                 StringProtoFunc::Small  DontEnum|Function       0
141   blink                 StringProtoFunc::Blink  DontEnum|Function       0
142   bold                  StringProtoFunc::Bold   DontEnum|Function       0
143   fixed                 StringProtoFunc::Fixed  DontEnum|Function       0
144   italics               StringProtoFunc::Italics        DontEnum|Function       0
145   strike                StringProtoFunc::Strike DontEnum|Function       0
146   sub                   StringProtoFunc::Sub            DontEnum|Function       0
147   sup                   StringProtoFunc::Sup            DontEnum|Function       0
148   fontcolor             StringProtoFunc::Fontcolor      DontEnum|Function       1
149   fontsize              StringProtoFunc::Fontsize       DontEnum|Function       1
150   anchor                StringProtoFunc::Anchor DontEnum|Function       1
151   link                  StringProtoFunc::Link   DontEnum|Function       1
152 @end
153 */
154 // ECMA 15.5.4
155 StringPrototype::StringPrototype(ExecState*, ObjectPrototype* objProto)
156   : StringInstance(objProto)
157 {
158   // The constructor will be added later, after StringObjectImp has been built
159   putDirect(lengthPropertyName, jsNumber(0), DontDelete|ReadOnly|DontEnum);
160 }
161
162 bool StringPrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot)
163 {
164   return getStaticFunctionSlot<StringProtoFunc, StringInstance>(exec, &stringTable, this, propertyName, slot);
165 }
166
167 // ------------------------------ StringProtoFunc ---------------------------
168
169 StringProtoFunc::StringProtoFunc(ExecState *exec, int i, int len, const Identifier& name)
170   : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
171   , id(i)
172 {
173   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
174 }
175
176 static inline void expandSourceRanges(UString::Range * & array, int& count, int& capacity)
177 {
178   int newCapacity;
179   if (capacity == 0) {
180     newCapacity = 16;
181   } else {
182     newCapacity = capacity * 2;
183   }
184
185   UString::Range *newArray = new UString::Range[newCapacity];
186   for (int i = 0; i < count; i++) {
187     newArray[i] = array[i];
188   }
189
190   delete [] array;
191
192   capacity = newCapacity;
193   array = newArray;
194 }
195
196 static void pushSourceRange(UString::Range * & array, int& count, int& capacity, UString::Range range)
197 {
198   if (count + 1 > capacity)
199     expandSourceRanges(array, count, capacity);
200
201   array[count] = range;
202   count++;
203 }
204
205 static inline void expandReplacements(UString * & array, int& count, int& capacity)
206 {
207   int newCapacity;
208   if (capacity == 0) {
209     newCapacity = 16;
210   } else {
211     newCapacity = capacity * 2;
212   }
213
214   UString *newArray = new UString[newCapacity];
215   for (int i = 0; i < count; i++) {
216     newArray[i] = array[i];
217   }
218   
219   delete [] array;
220
221   capacity = newCapacity;
222   array = newArray;
223 }
224
225 static void pushReplacement(UString * & array, int& count, int& capacity, UString replacement)
226 {
227   if (count + 1 > capacity)
228     expandReplacements(array, count, capacity);
229
230   array[count] = replacement;
231   count++;
232 }
233
234 static inline UString substituteBackreferences(const UString &replacement, const UString &source, int *ovector, RegExp *reg)
235 {
236   UString substitutedReplacement = replacement;
237
238   bool converted;
239
240   for (int i = 0; (i = substitutedReplacement.find(UString("$"), i)) != -1; i++) {
241     if (i+1 < substitutedReplacement.size() && substitutedReplacement[i+1] == '$') {  // "$$" -> "$"
242       substitutedReplacement = substitutedReplacement.substr(0,i) + "$" + substitutedReplacement.substr(i+2);
243       continue;
244     }
245     // Assume number part is one char exactly
246     unsigned backrefIndex = substitutedReplacement.substr(i+1,1).toUInt32(&converted, false /* tolerate empty string */);
247     if (converted && backrefIndex <= (unsigned)reg->subPatterns()) {
248       int backrefStart = ovector[2*backrefIndex];
249       int backrefLength = ovector[2*backrefIndex+1] - backrefStart;
250       substitutedReplacement = substitutedReplacement.substr(0,i)
251         + source.substr(backrefStart, backrefLength)
252         + substitutedReplacement.substr(i+2);
253       i += backrefLength - 1; // -1 offsets i++
254     }
255   }
256
257   return substitutedReplacement;
258 }
259
260 static JSValue *replace(ExecState *exec, const UString &source, JSValue *pattern, JSValue *replacement)
261 {
262   JSObject *replacementFunction = 0;
263   UString replacementString;
264
265   if (replacement->isObject() && replacement->toObject(exec)->implementsCall())
266     replacementFunction = replacement->toObject(exec);
267   else
268     replacementString = replacement->toString(exec);
269
270   if (pattern->isObject() && static_cast<JSObject *>(pattern)->inherits(&RegExpImp::info)) {
271     RegExp *reg = static_cast<RegExpImp *>(pattern)->regExp();
272     bool global = reg->flags() & RegExp::Global;
273
274     RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp());
275
276     int matchIndex = 0;
277     int lastIndex = 0;
278     int startPosition = 0;
279
280     UString::Range *sourceRanges = 0;
281     int sourceRangeCount = 0;
282     int sourceRangeCapacity = 0;
283     UString *replacements = 0;
284     int replacementCount = 0;
285     int replacementCapacity = 0;
286
287     // This is either a loop (if global is set) or a one-way (if not).
288     do {
289       int *ovector;
290       UString matchString = regExpObj->performMatch(reg, source, startPosition, &matchIndex, &ovector);
291       if (matchIndex == -1)
292         break;
293       int matchLen = matchString.size();
294
295       pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, matchIndex - lastIndex));
296
297       if (replacementFunction) {
298           int completeMatchStart = ovector[0];
299           List args;
300
301           args.append(jsString(matchString));
302           
303           for (unsigned i = 0; i < reg->subPatterns(); i++) {
304               int matchStart = ovector[(i + 1) * 2];
305               int matchLen = ovector[(i + 1) * 2 + 1] - matchStart;
306               
307               args.append(jsString(source.substr(matchStart, matchLen)));
308           }
309           
310           args.append(jsNumber(completeMatchStart));
311           args.append(jsString(source));
312
313           replacementString = replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), 
314                                                         args)->toString(exec);
315       }
316       
317       UString substitutedReplacement = substituteBackreferences(replacementString, source, ovector, reg);
318       pushReplacement(replacements, replacementCount, replacementCapacity, substitutedReplacement);
319
320       lastIndex = matchIndex + matchLen;
321       startPosition = lastIndex;
322
323       // special case of empty match
324       if (matchLen == 0) {
325         startPosition++;
326         if (startPosition > source.size())
327           break;
328       }
329     } while (global);
330
331     if (lastIndex < source.size())
332       pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, source.size() - lastIndex));
333
334     UString result = source.spliceSubstringsWithSeparators(sourceRanges, sourceRangeCount, replacements, replacementCount);
335
336     delete [] sourceRanges;
337     delete [] replacements;
338
339     return jsString(result);
340   }
341   
342   // First arg is a string
343   UString patternString = pattern->toString(exec);
344   int matchPos = source.find(patternString);
345   int matchLen = patternString.size();
346   // Do the replacement
347   if (matchPos == -1)
348     return jsString(source);
349   
350   if (replacementFunction) {
351       List args;
352       
353       args.append(jsString(source.substr(matchPos, matchLen)));
354       args.append(jsNumber(matchPos));
355       args.append(jsString(source));
356       
357       replacementString = replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), 
358                                                     args)->toString(exec);
359   }
360
361   return jsString(source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen));
362 }
363
364 // ECMA 15.5.4.2 - 15.5.4.20
365 JSValue *StringProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
366 {
367   JSValue *result = NULL;
368
369   // toString and valueOf are no generic function.
370   if (id == ToString || id == ValueOf) {
371     if (!thisObj || !thisObj->inherits(&StringInstance::info))
372       return throwError(exec, TypeError);
373
374     return jsString(thisObj->internalValue()->toString(exec));
375   }
376
377   UString u, u2, u3;
378   int pos, p0, i;
379   double dpos;
380   double d = 0.0;
381
382   UString s = thisObj->toString(exec);
383
384   int len = s.size();
385   JSValue *a0 = args[0];
386   JSValue *a1 = args[1];
387
388   switch (id) {
389   case ToString:
390   case ValueOf:
391     // handled above
392     break;
393   case CharAt:
394     // Other browsers treat an omitted parameter as 0 rather than NaN.
395     // That doesn't match the ECMA standard, but is needed for site compatibility.
396     dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
397     if (dpos >= 0 && dpos < len) // false for NaN
398       u = s.substr(static_cast<int>(dpos), 1);
399     else
400       u = "";
401     result = jsString(u);
402     break;
403   case CharCodeAt:
404     // Other browsers treat an omitted parameter as 0 rather than NaN.
405     // That doesn't match the ECMA standard, but is needed for site compatibility.
406     dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
407     if (dpos >= 0 && dpos < len) // false for NaN
408       result = jsNumber(s[static_cast<int>(dpos)].unicode());
409     else
410       result = jsNaN();
411     break;
412   case Concat: {
413     ListIterator it = args.begin();
414     for ( ; it != args.end() ; ++it) {
415         s += it->toString(exec);
416     }
417     result = jsString(s);
418     break;
419   }
420   case IndexOf:
421     u2 = a0->toString(exec);
422     if (a1->isUndefined())
423       dpos = 0;
424     else {
425       dpos = a1->toInteger(exec);
426       if (dpos >= 0) { // false for NaN
427         if (dpos > len)
428           dpos = len;
429       } else
430         dpos = 0;
431     }
432     result = jsNumber(s.find(u2, static_cast<int>(dpos)));
433     break;
434   case LastIndexOf:
435     u2 = a0->toString(exec);
436     d = a1->toNumber(exec);
437     if (a1->isUndefined() || KJS::isNaN(d))
438       dpos = len;
439     else {
440       dpos = a1->toInteger(exec);
441       if (dpos >= 0) { // false for NaN
442         if (dpos > len)
443           dpos = len;
444       } else
445         dpos = 0;
446     }
447     result = jsNumber(s.rfind(u2, static_cast<int>(dpos)));
448     break;
449   case Match:
450   case Search: {
451     u = s;
452     RegExp *reg, *tmpReg = 0;
453     RegExpImp *imp = 0;
454     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) {
455       reg = static_cast<RegExpImp *>(a0)->regExp();
456     } else { 
457       /*
458        *  ECMA 15.5.4.12 String.prototype.search (regexp)
459        *  If regexp is not an object whose [[Class]] property is "RegExp", it is
460        *  replaced with the result of the expression new RegExp(regexp).
461        */
462       reg = tmpReg = new RegExp(a0->toString(exec), RegExp::None);
463     }
464     RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp());
465     UString mstr = regExpObj->performMatch(reg, u, 0, &pos);
466     if (id == Search) {
467       result = jsNumber(pos);
468     } else {
469       // Exec
470       if ((reg->flags() & RegExp::Global) == 0) {
471         // case without 'g' flag is handled like RegExp.prototype.exec
472         if (mstr.isNull()) {
473           result = jsNull();
474         } else {
475           result = regExpObj->arrayOfMatches(exec,mstr);
476         }
477       } else {
478         // return array of matches
479         List list;
480         int lastIndex = 0;
481         while (pos >= 0) {
482           if (mstr.isNull())
483             list.append(jsUndefined());
484           else
485             list.append(jsString(mstr));
486           lastIndex = pos;
487           pos += mstr.isEmpty() ? 1 : mstr.size();
488           mstr = regExpObj->performMatch(reg, u, pos, &pos);
489         }
490         if (imp)
491           imp->put(exec, "lastIndex", jsNumber(lastIndex), DontDelete|DontEnum);
492         if (list.isEmpty()) {
493           // if there are no matches at all, it's important to return
494           // Null instead of an empty array, because this matches
495           // other browsers and because Null is a false value.
496           result = jsNull(); 
497         } else {
498           result = exec->lexicalInterpreter()->builtinArray()->construct(exec, list);
499         }
500       }
501     }
502     delete tmpReg;
503     break;
504   }
505   case Replace:
506     result = replace(exec, s, a0, a1);
507     break;
508   case Slice:
509     {
510       // The arg processing is very much like ArrayProtoFunc::Slice
511       double start = a0->toInteger(exec);
512       double end = a1->isUndefined() ? len : a1->toInteger(exec);
513       double from = start < 0 ? len + start : start;
514       double to = end < 0 ? len + end : end;
515       if (to > from && to > 0 && from < len) {
516         if (from < 0)
517           from = 0;
518         if (to > len)
519           to = len;
520         result = jsString(s.substr(static_cast<int>(from), static_cast<int>(to - from)));
521       } else {
522         result = jsString("");
523       }
524       break;
525     }
526     case Split: {
527     JSObject *constructor = exec->lexicalInterpreter()->builtinArray();
528     JSObject *res = static_cast<JSObject *>(constructor->construct(exec,List::empty()));
529     result = res;
530     u = s;
531     i = p0 = 0;
532     uint32_t limit = a1->isUndefined() ? 0xFFFFFFFFU : a1->toUInt32(exec);
533     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) {
534       RegExp *reg = static_cast<RegExpImp *>(a0)->regExp();
535       if (u.isEmpty() && !reg->match(u, 0).isNull()) {
536         // empty string matched by regexp -> empty array
537         res->put(exec,lengthPropertyName, jsNumber(0));
538         break;
539       }
540       pos = 0;
541       while (static_cast<uint32_t>(i) != limit && pos < u.size()) {
542         // TODO: back references
543         int mpos;
544         int *ovector = 0L;
545         UString mstr = reg->match(u, pos, &mpos, &ovector);
546         delete [] ovector; ovector = 0L;
547         if (mpos < 0)
548           break;
549         pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
550         if (mpos != p0 || !mstr.isEmpty()) {
551           res->put(exec,i, jsString(u.substr(p0, mpos-p0)));
552           p0 = mpos + mstr.size();
553           i++;
554         }
555       }
556     } else {
557       u2 = a0->toString(exec);
558       if (u2.isEmpty()) {
559         if (u.isEmpty()) {
560           // empty separator matches empty string -> empty array
561           put(exec,lengthPropertyName, jsNumber(0));
562           break;
563         } else {
564           while (static_cast<uint32_t>(i) != limit && i < u.size()-1)
565             res->put(exec, i++, jsString(u.substr(p0++, 1)));
566         }
567       } else {
568         while (static_cast<uint32_t>(i) != limit && (pos = u.find(u2, p0)) >= 0) {
569           res->put(exec, i, jsString(u.substr(p0, pos-p0)));
570           p0 = pos + u2.size();
571           i++;
572         }
573       }
574     }
575     // add remaining string, if any
576     if (static_cast<uint32_t>(i) != limit)
577       res->put(exec, i++, jsString(u.substr(p0)));
578     res->put(exec,lengthPropertyName, jsNumber(i));
579     }
580     break;
581   case Substr: {
582     double d = a0->toInteger(exec);
583     double d2 = a1->toInteger(exec);
584     if (!(d >= 0)) { // true for NaN
585       d += len;
586       if (!(d >= 0)) // true for NaN
587         d = 0;
588     }
589     if (isNaN(d2))
590       d2 = len - d;
591     else {
592       if (d2 < 0)
593         d2 = 0;
594       if (d2 > len - d)
595         d2 = len - d;
596     }
597     result = jsString(s.substr(static_cast<int>(d), static_cast<int>(d2)));
598     break;
599   }
600   case Substring: {
601     double start = a0->toNumber(exec);
602     double end = a1->toNumber(exec);
603     if (isNaN(start))
604       start = 0;
605     if (isNaN(end))
606       end = 0;
607     if (start < 0)
608       start = 0;
609     if (end < 0)
610       end = 0;
611     if (start > len)
612       start = len;
613     if (end > len)
614       end = len;
615     if (a1->isUndefined())
616       end = len;
617     if (start > end) {
618       double temp = end;
619       end = start;
620       start = temp;
621     }
622     result = jsString(s.substr((int)start, (int)end-(int)start));
623     }
624     break;
625   case ToLowerCase:
626   case ToLocaleLowerCase: // FIXME: To get this 100% right we need to detect Turkish and change I to lowercase i without a dot.
627     u = s;
628     for (i = 0; i < len; i++)
629       u[i] = u[i].toLower();
630     result = jsString(u);
631     break;
632   case ToUpperCase:
633   case ToLocaleUpperCase: // FIXME: To get this 100% right we need to detect Turkish and change i to uppercase I with a dot.
634     u = s;
635     for (i = 0; i < len; i++)
636       u[i] = u[i].toUpper();
637     result = jsString(u);
638     break;
639 #ifndef KJS_PURE_ECMA
640   case Big:
641     result = jsString("<big>" + s + "</big>");
642     break;
643   case Small:
644     result = jsString("<small>" + s + "</small>");
645     break;
646   case Blink:
647     result = jsString("<blink>" + s + "</blink>");
648     break;
649   case Bold:
650     result = jsString("<b>" + s + "</b>");
651     break;
652   case Fixed:
653     result = jsString("<tt>" + s + "</tt>");
654     break;
655   case Italics:
656     result = jsString("<i>" + s + "</i>");
657     break;
658   case Strike:
659     result = jsString("<strike>" + s + "</strike>");
660     break;
661   case Sub:
662     result = jsString("<sub>" + s + "</sub>");
663     break;
664   case Sup:
665     result = jsString("<sup>" + s + "</sup>");
666     break;
667   case Fontcolor:
668     result = jsString("<font color=\"" + a0->toString(exec) + "\">" + s + "</font>");
669     break;
670   case Fontsize:
671     result = jsString("<font size=\"" + a0->toString(exec) + "\">" + s + "</font>");
672     break;
673   case Anchor:
674     result = jsString("<a name=\"" + a0->toString(exec) + "\">" + s + "</a>");
675     break;
676   case Link:
677     result = jsString("<a href=\"" + a0->toString(exec) + "\">" + s + "</a>");
678     break;
679 #endif
680   }
681
682   return result;
683 }
684
685 // ------------------------------ StringObjectImp ------------------------------
686
687 StringObjectImp::StringObjectImp(ExecState *exec,
688                                  FunctionPrototype *funcProto,
689                                  StringPrototype *stringProto)
690   : InternalFunctionImp(funcProto)
691 {
692   // ECMA 15.5.3.1 String.prototype
693   putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
694
695   putDirectFunction(new StringObjectFuncImp(exec, funcProto, fromCharCodePropertyName), DontEnum);
696
697   // no. of arguments for constructor
698   putDirect(lengthPropertyName, jsNumber(1), ReadOnly|DontDelete|DontEnum);
699 }
700
701
702 bool StringObjectImp::implementsConstruct() const
703 {
704   return true;
705 }
706
707 // ECMA 15.5.2
708 JSObject *StringObjectImp::construct(ExecState *exec, const List &args)
709 {
710   JSObject *proto = exec->lexicalInterpreter()->builtinStringPrototype();
711   if (args.size() == 0)
712     return new StringInstance(proto);
713   return new StringInstance(proto, args.begin()->toString(exec));
714 }
715
716 // ECMA 15.5.1
717 JSValue *StringObjectImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
718 {
719   if (args.isEmpty())
720     return jsString("");
721   else {
722     JSValue *v = args[0];
723     return jsString(v->toString(exec));
724   }
725 }
726
727 // ------------------------------ StringObjectFuncImp --------------------------
728
729 // ECMA 15.5.3.2 fromCharCode()
730 StringObjectFuncImp::StringObjectFuncImp(ExecState*, FunctionPrototype* funcProto, const Identifier& name)
731   : InternalFunctionImp(funcProto, name)
732 {
733   putDirect(lengthPropertyName, jsNumber(1), DontDelete|ReadOnly|DontEnum);
734 }
735
736 JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
737 {
738   UString s;
739   if (args.size()) {
740     UChar *buf = static_cast<UChar *>(fastMalloc(args.size() * sizeof(UChar)));
741     UChar *p = buf;
742     ListIterator it = args.begin();
743     while (it != args.end()) {
744       unsigned short u = it->toUInt16(exec);
745       *p++ = UChar(u);
746       it++;
747     }
748     s = UString(buf, args.size(), false);
749   } else
750     s = "";
751
752   return jsString(s);
753 }