884638e21e23e1cd4942003af6e7224a294369fe
[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   bool converted;
236
237   for (int i = 0; (i = substitutedReplacement.find(UString("$"), i)) != -1; i++) {
238     if (i+1 < substitutedReplacement.size() && substitutedReplacement[i+1] == '$') {  // "$$" -> "$"
239       substitutedReplacement = substitutedReplacement.substr(0,i) + "$" + substitutedReplacement.substr(i+2);
240       continue;
241     }
242     // Assume number part is one char exactly
243     unsigned backrefIndex = substitutedReplacement.substr(i+1,1).toUInt32(&converted, false /* tolerate empty string */);
244     if (converted && backrefIndex <= (unsigned)reg->subPatterns()) {
245       int backrefStart = ovector[2*backrefIndex];
246       int backrefLength = ovector[2*backrefIndex+1] - backrefStart;
247       substitutedReplacement = substitutedReplacement.substr(0,i)
248         + source.substr(backrefStart, backrefLength)
249         + substitutedReplacement.substr(i+2);
250       i += backrefLength - 1; // -1 offsets i++
251     }
252   }
253
254   return substitutedReplacement;
255 }
256
257 static JSValue *replace(ExecState *exec, const UString &source, JSValue *pattern, JSValue *replacement)
258 {
259   JSObject *replacementFunction = 0;
260   UString replacementString;
261
262   if (replacement->isObject() && replacement->toObject(exec)->implementsCall())
263     replacementFunction = replacement->toObject(exec);
264   else
265     replacementString = replacement->toString(exec);
266
267   if (pattern->isObject() && static_cast<JSObject *>(pattern)->inherits(&RegExpImp::info)) {
268     RegExp *reg = static_cast<RegExpImp *>(pattern)->regExp();
269     bool global = reg->flags() & RegExp::Global;
270
271     RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp());
272
273     int matchIndex = 0;
274     int lastIndex = 0;
275     int startPosition = 0;
276
277     UString::Range *sourceRanges = 0;
278     int sourceRangeCount = 0;
279     int sourceRangeCapacity = 0;
280     UString *replacements = 0;
281     int replacementCount = 0;
282     int replacementCapacity = 0;
283
284     // This is either a loop (if global is set) or a one-way (if not).
285     do {
286       int *ovector;
287       UString matchString = regExpObj->performMatch(reg, source, startPosition, &matchIndex, &ovector);
288       if (matchIndex == -1)
289         break;
290       int matchLen = matchString.size();
291
292       pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, matchIndex - lastIndex));
293
294       if (replacementFunction) {
295           int completeMatchStart = ovector[0];
296           List args;
297
298           args.append(jsString(matchString));
299           
300           for (unsigned i = 0; i < reg->subPatterns(); i++) {
301               int matchStart = ovector[(i + 1) * 2];
302               int matchLen = ovector[(i + 1) * 2 + 1] - matchStart;
303               
304               args.append(jsString(source.substr(matchStart, matchLen)));
305           }
306           
307           args.append(jsNumber(completeMatchStart));
308           args.append(jsString(source));
309
310           replacementString = replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), 
311                                                         args)->toString(exec);
312       }
313       
314       UString substitutedReplacement = substituteBackreferences(replacementString, source, ovector, reg);
315       pushReplacement(replacements, replacementCount, replacementCapacity, substitutedReplacement);
316
317       lastIndex = matchIndex + matchLen;
318       startPosition = lastIndex;
319
320       // special case of empty match
321       if (matchLen == 0) {
322         startPosition++;
323         if (startPosition > source.size())
324           break;
325       }
326     } while (global);
327
328     if (lastIndex < source.size())
329       pushSourceRange(sourceRanges, sourceRangeCount, sourceRangeCapacity, UString::Range(lastIndex, source.size() - lastIndex));
330
331     UString result = source.spliceSubstringsWithSeparators(sourceRanges, sourceRangeCount, replacements, replacementCount);
332
333     delete [] sourceRanges;
334     delete [] replacements;
335
336     return jsString(result);
337   }
338   
339   // First arg is a string
340   UString patternString = pattern->toString(exec);
341   int matchPos = source.find(patternString);
342   int matchLen = patternString.size();
343   // Do the replacement
344   if (matchPos == -1)
345     return jsString(source);
346   
347   if (replacementFunction) {
348       List args;
349       
350       args.append(jsString(source.substr(matchPos, matchLen)));
351       args.append(jsNumber(matchPos));
352       args.append(jsString(source));
353       
354       replacementString = replacementFunction->call(exec, exec->dynamicInterpreter()->globalObject(), 
355                                                     args)->toString(exec);
356   }
357
358   return jsString(source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen));
359 }
360
361 // ECMA 15.5.4.2 - 15.5.4.20
362 JSValue *StringProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
363 {
364   JSValue *result = NULL;
365
366   // toString and valueOf are no generic function.
367   if (id == ToString || id == ValueOf) {
368     if (!thisObj || !thisObj->inherits(&StringInstance::info))
369       return throwError(exec, TypeError);
370
371     return jsString(thisObj->internalValue()->toString(exec));
372   }
373
374   UString u, u2, u3;
375   int pos, p0, i;
376   double dpos;
377   double d = 0.0;
378
379   UString s = thisObj->toString(exec);
380
381   int len = s.size();
382   JSValue *a0 = args[0];
383   JSValue *a1 = args[1];
384
385   switch (id) {
386   case ToString:
387   case ValueOf:
388     // handled above
389     break;
390   case CharAt:
391     // Other browsers treat an omitted parameter as 0 rather than NaN.
392     // That doesn't match the ECMA standard, but is needed for site compatibility.
393     dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
394     if (dpos >= 0 && dpos < len) // false for NaN
395       u = s.substr(static_cast<int>(dpos), 1);
396     else
397       u = "";
398     result = jsString(u);
399     break;
400   case CharCodeAt:
401     // Other browsers treat an omitted parameter as 0 rather than NaN.
402     // That doesn't match the ECMA standard, but is needed for site compatibility.
403     dpos = a0->isUndefined() ? 0 : a0->toInteger(exec);
404     if (dpos >= 0 && dpos < len) // false for NaN
405       result = jsNumber(s[static_cast<int>(dpos)].unicode());
406     else
407       result = jsNaN();
408     break;
409   case Concat: {
410     ListIterator it = args.begin();
411     for ( ; it != args.end() ; ++it) {
412         s += it->toString(exec);
413     }
414     result = jsString(s);
415     break;
416   }
417   case IndexOf:
418     u2 = a0->toString(exec);
419     if (a1->isUndefined())
420       dpos = 0;
421     else {
422       dpos = a1->toInteger(exec);
423       if (dpos >= 0) { // false for NaN
424         if (dpos > len)
425           dpos = len;
426       } else
427         dpos = 0;
428     }
429     result = jsNumber(s.find(u2, static_cast<int>(dpos)));
430     break;
431   case LastIndexOf:
432     u2 = a0->toString(exec);
433     d = a1->toNumber(exec);
434     if (a1->isUndefined() || KJS::isNaN(d))
435       dpos = len;
436     else {
437       dpos = a1->toInteger(exec);
438       if (dpos >= 0) { // false for NaN
439         if (dpos > len)
440           dpos = len;
441       } else
442         dpos = 0;
443     }
444     result = jsNumber(s.rfind(u2, static_cast<int>(dpos)));
445     break;
446   case Match:
447   case Search: {
448     u = s;
449     RegExp *reg, *tmpReg = 0;
450     RegExpImp *imp = 0;
451     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) {
452       reg = static_cast<RegExpImp *>(a0)->regExp();
453     } else { 
454       /*
455        *  ECMA 15.5.4.12 String.prototype.search (regexp)
456        *  If regexp is not an object whose [[Class]] property is "RegExp", it is
457        *  replaced with the result of the expression new RegExp(regexp).
458        */
459       reg = tmpReg = new RegExp(a0->toString(exec), RegExp::None);
460     }
461     RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalInterpreter()->builtinRegExp());
462     UString mstr = regExpObj->performMatch(reg, u, 0, &pos);
463     if (id == Search) {
464       result = jsNumber(pos);
465     } else {
466       // Exec
467       if ((reg->flags() & RegExp::Global) == 0) {
468         // case without 'g' flag is handled like RegExp.prototype.exec
469         if (mstr.isNull()) {
470           result = jsNull();
471         } else {
472           result = regExpObj->arrayOfMatches(exec,mstr);
473         }
474       } else {
475         // return array of matches
476         List list;
477         int lastIndex = 0;
478         while (pos >= 0) {
479           if (mstr.isNull())
480             list.append(jsUndefined());
481           else
482             list.append(jsString(mstr));
483           lastIndex = pos;
484           pos += mstr.isEmpty() ? 1 : mstr.size();
485           mstr = regExpObj->performMatch(reg, u, pos, &pos);
486         }
487         if (imp)
488           imp->put(exec, "lastIndex", jsNumber(lastIndex), DontDelete|DontEnum);
489         if (list.isEmpty()) {
490           // if there are no matches at all, it's important to return
491           // Null instead of an empty array, because this matches
492           // other browsers and because Null is a false value.
493           result = jsNull(); 
494         } else {
495           result = exec->lexicalInterpreter()->builtinArray()->construct(exec, list);
496         }
497       }
498     }
499     delete tmpReg;
500     break;
501   }
502   case Replace:
503     result = replace(exec, s, a0, a1);
504     break;
505   case Slice:
506     {
507       // The arg processing is very much like ArrayProtoFunc::Slice
508       double start = a0->toInteger(exec);
509       double end = a1->isUndefined() ? len : a1->toInteger(exec);
510       double from = start < 0 ? len + start : start;
511       double to = end < 0 ? len + end : end;
512       if (to > from && to > 0 && from < len) {
513         if (from < 0)
514           from = 0;
515         if (to > len)
516           to = len;
517         result = jsString(s.substr(static_cast<int>(from), static_cast<int>(to - from)));
518       } else {
519         result = jsString("");
520       }
521       break;
522     }
523     case Split: {
524     JSObject *constructor = exec->lexicalInterpreter()->builtinArray();
525     JSObject *res = static_cast<JSObject *>(constructor->construct(exec,List::empty()));
526     result = res;
527     u = s;
528     i = p0 = 0;
529     uint32_t limit = a1->isUndefined() ? 0xFFFFFFFFU : a1->toUInt32(exec);
530     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpImp::info)) {
531       RegExp *reg = static_cast<RegExpImp *>(a0)->regExp();
532       if (u.isEmpty() && !reg->match(u, 0).isNull()) {
533         // empty string matched by regexp -> empty array
534         res->put(exec,lengthPropertyName, jsNumber(0));
535         break;
536       }
537       pos = 0;
538       while (static_cast<uint32_t>(i) != limit && pos < u.size()) {
539         // TODO: back references
540         int mpos;
541         int *ovector = 0L;
542         UString mstr = reg->match(u, pos, &mpos, &ovector);
543         delete [] ovector; ovector = 0L;
544         if (mpos < 0)
545           break;
546         pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
547         if (mpos != p0 || !mstr.isEmpty()) {
548           res->put(exec,i, jsString(u.substr(p0, mpos-p0)));
549           p0 = mpos + mstr.size();
550           i++;
551         }
552       }
553     } else {
554       u2 = a0->toString(exec);
555       if (u2.isEmpty()) {
556         if (u.isEmpty()) {
557           // empty separator matches empty string -> empty array
558           put(exec,lengthPropertyName, jsNumber(0));
559           break;
560         } else {
561           while (static_cast<uint32_t>(i) != limit && i < u.size()-1)
562             res->put(exec, i++, jsString(u.substr(p0++, 1)));
563         }
564       } else {
565         while (static_cast<uint32_t>(i) != limit && (pos = u.find(u2, p0)) >= 0) {
566           res->put(exec, i, jsString(u.substr(p0, pos-p0)));
567           p0 = pos + u2.size();
568           i++;
569         }
570       }
571     }
572     // add remaining string, if any
573     if (static_cast<uint32_t>(i) != limit)
574       res->put(exec, i++, jsString(u.substr(p0)));
575     res->put(exec,lengthPropertyName, jsNumber(i));
576     }
577     break;
578   case Substr: {
579     double d = a0->toInteger(exec);
580     double d2 = a1->toInteger(exec);
581     if (!(d >= 0)) { // true for NaN
582       d += len;
583       if (!(d >= 0)) // true for NaN
584         d = 0;
585     }
586     if (isNaN(d2))
587       d2 = len - d;
588     else {
589       if (d2 < 0)
590         d2 = 0;
591       if (d2 > len - d)
592         d2 = len - d;
593     }
594     result = jsString(s.substr(static_cast<int>(d), static_cast<int>(d2)));
595     break;
596   }
597   case Substring: {
598     double start = a0->toNumber(exec);
599     double end = a1->toNumber(exec);
600     if (isNaN(start))
601       start = 0;
602     if (isNaN(end))
603       end = 0;
604     if (start < 0)
605       start = 0;
606     if (end < 0)
607       end = 0;
608     if (start > len)
609       start = len;
610     if (end > len)
611       end = len;
612     if (a1->isUndefined())
613       end = len;
614     if (start > end) {
615       double temp = end;
616       end = start;
617       start = temp;
618     }
619     result = jsString(s.substr((int)start, (int)end-(int)start));
620     }
621     break;
622   case ToLowerCase:
623   case ToLocaleLowerCase: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
624     u = s;
625     u.copyForWriting();
626     uint16_t* dataPtr = reinterpret_cast<uint16_t*>(u.rep()->data());
627     uint16_t* destIfNeeded;
628
629     int len = WTF::Unicode::toLower(dataPtr, u.size(), destIfNeeded);
630     if (len >= 0)
631         result = jsString(UString(reinterpret_cast<UChar *>(destIfNeeded ? destIfNeeded : dataPtr), len));
632     else
633         result = jsString(s);
634
635     free(destIfNeeded);
636     break;
637   }
638   case ToUpperCase:
639   case ToLocaleUpperCase: { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
640     u = s;
641     u.copyForWriting();
642     uint16_t* dataPtr = reinterpret_cast<uint16_t*>(u.rep()->data());
643     uint16_t* destIfNeeded;
644
645     int len = WTF::Unicode::toUpper(dataPtr, u.size(), destIfNeeded);
646     if (len >= 0)
647         result = jsString(UString(reinterpret_cast<UChar *>(destIfNeeded ? destIfNeeded : dataPtr), len));
648     else
649         result = jsString(s);
650
651     free(destIfNeeded);
652     break;
653   }
654 #ifndef KJS_PURE_ECMA
655   case Big:
656     result = jsString("<big>" + s + "</big>");
657     break;
658   case Small:
659     result = jsString("<small>" + s + "</small>");
660     break;
661   case Blink:
662     result = jsString("<blink>" + s + "</blink>");
663     break;
664   case Bold:
665     result = jsString("<b>" + s + "</b>");
666     break;
667   case Fixed:
668     result = jsString("<tt>" + s + "</tt>");
669     break;
670   case Italics:
671     result = jsString("<i>" + s + "</i>");
672     break;
673   case Strike:
674     result = jsString("<strike>" + s + "</strike>");
675     break;
676   case Sub:
677     result = jsString("<sub>" + s + "</sub>");
678     break;
679   case Sup:
680     result = jsString("<sup>" + s + "</sup>");
681     break;
682   case Fontcolor:
683     result = jsString("<font color=\"" + a0->toString(exec) + "\">" + s + "</font>");
684     break;
685   case Fontsize:
686     result = jsString("<font size=\"" + a0->toString(exec) + "\">" + s + "</font>");
687     break;
688   case Anchor:
689     result = jsString("<a name=\"" + a0->toString(exec) + "\">" + s + "</a>");
690     break;
691   case Link:
692     result = jsString("<a href=\"" + a0->toString(exec) + "\">" + s + "</a>");
693     break;
694 #endif
695   }
696
697   return result;
698 }
699
700 // ------------------------------ StringObjectImp ------------------------------
701
702 StringObjectImp::StringObjectImp(ExecState *exec,
703                                  FunctionPrototype *funcProto,
704                                  StringPrototype *stringProto)
705   : InternalFunctionImp(funcProto)
706 {
707   // ECMA 15.5.3.1 String.prototype
708   putDirect(prototypePropertyName, stringProto, DontEnum|DontDelete|ReadOnly);
709
710   putDirectFunction(new StringObjectFuncImp(exec, funcProto, fromCharCodePropertyName), DontEnum);
711
712   // no. of arguments for constructor
713   putDirect(lengthPropertyName, jsNumber(1), ReadOnly|DontDelete|DontEnum);
714 }
715
716
717 bool StringObjectImp::implementsConstruct() const
718 {
719   return true;
720 }
721
722 // ECMA 15.5.2
723 JSObject *StringObjectImp::construct(ExecState *exec, const List &args)
724 {
725   JSObject *proto = exec->lexicalInterpreter()->builtinStringPrototype();
726   if (args.size() == 0)
727     return new StringInstance(proto);
728   return new StringInstance(proto, args.begin()->toString(exec));
729 }
730
731 // ECMA 15.5.1
732 JSValue *StringObjectImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
733 {
734   if (args.isEmpty())
735     return jsString("");
736   else {
737     JSValue *v = args[0];
738     return jsString(v->toString(exec));
739   }
740 }
741
742 // ------------------------------ StringObjectFuncImp --------------------------
743
744 // ECMA 15.5.3.2 fromCharCode()
745 StringObjectFuncImp::StringObjectFuncImp(ExecState*, FunctionPrototype* funcProto, const Identifier& name)
746   : InternalFunctionImp(funcProto, name)
747 {
748   putDirect(lengthPropertyName, jsNumber(1), DontDelete|ReadOnly|DontEnum);
749 }
750
751 JSValue *StringObjectFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
752 {
753   UString s;
754   if (args.size()) {
755     UChar *buf = static_cast<UChar *>(fastMalloc(args.size() * sizeof(UChar)));
756     UChar *p = buf;
757     ListIterator it = args.begin();
758     while (it != args.end()) {
759       unsigned short u = it->toUInt16(exec);
760       *p++ = UChar(u);
761       it++;
762     }
763     s = UString(buf, args.size(), false);
764   } else
765     s = "";
766
767   return jsString(s);
768 }