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