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