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