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