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