Fix build by adding all (hopefully) the missing includes.
[WebKit-https.git] / JavaScriptCore / kjs / StringPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "StringPrototype.h"
23
24 #include "JSArray.h"
25 #include "JSFunction.h"
26 #include "ObjectPrototype.h"
27 #include "PropertyNameArray.h"
28 #include "RegExpConstructor.h"
29 #include "RegExpObject.h"
30 #include <wtf/MathExtras.h>
31 #include <wtf/unicode/Collator.h>
32
33 using namespace WTF;
34
35 namespace KJS {
36
37 static JSValue* stringProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&);
38 static JSValue* stringProtoFuncCharAt(ExecState*, JSObject*, JSValue*, const ArgList&);
39 static JSValue* stringProtoFuncCharCodeAt(ExecState*, JSObject*, JSValue*, const ArgList&);
40 static JSValue* stringProtoFuncConcat(ExecState*, JSObject*, JSValue*, const ArgList&);
41 static JSValue* stringProtoFuncIndexOf(ExecState*, JSObject*, JSValue*, const ArgList&);
42 static JSValue* stringProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue*, const ArgList&);
43 static JSValue* stringProtoFuncMatch(ExecState*, JSObject*, JSValue*, const ArgList&);
44 static JSValue* stringProtoFuncReplace(ExecState*, JSObject*, JSValue*, const ArgList&);
45 static JSValue* stringProtoFuncSearch(ExecState*, JSObject*, JSValue*, const ArgList&);
46 static JSValue* stringProtoFuncSlice(ExecState*, JSObject*, JSValue*, const ArgList&);
47 static JSValue* stringProtoFuncSplit(ExecState*, JSObject*, JSValue*, const ArgList&);
48 static JSValue* stringProtoFuncSubstr(ExecState*, JSObject*, JSValue*, const ArgList&);
49 static JSValue* stringProtoFuncSubstring(ExecState*, JSObject*, JSValue*, const ArgList&);
50 static JSValue* stringProtoFuncToLowerCase(ExecState*, JSObject*, JSValue*, const ArgList&);
51 static JSValue* stringProtoFuncToUpperCase(ExecState*, JSObject*, JSValue*, const ArgList&);
52 static JSValue* stringProtoFuncToLocaleLowerCase(ExecState*, JSObject*, JSValue*, const ArgList&);
53 static JSValue* stringProtoFuncToLocaleUpperCase(ExecState*, JSObject*, JSValue*, const ArgList&);
54 static JSValue* stringProtoFuncLocaleCompare(ExecState*, JSObject*, JSValue*, const ArgList&);
55
56 static JSValue* stringProtoFuncBig(ExecState*, JSObject*, JSValue*, const ArgList&);
57 static JSValue* stringProtoFuncSmall(ExecState*, JSObject*, JSValue*, const ArgList&);
58 static JSValue* stringProtoFuncBlink(ExecState*, JSObject*, JSValue*, const ArgList&);
59 static JSValue* stringProtoFuncBold(ExecState*, JSObject*, JSValue*, const ArgList&);
60 static JSValue* stringProtoFuncFixed(ExecState*, JSObject*, JSValue*, const ArgList&);
61 static JSValue* stringProtoFuncItalics(ExecState*, JSObject*, JSValue*, const ArgList&);
62 static JSValue* stringProtoFuncStrike(ExecState*, JSObject*, JSValue*, const ArgList&);
63 static JSValue* stringProtoFuncSub(ExecState*, JSObject*, JSValue*, const ArgList&);
64 static JSValue* stringProtoFuncSup(ExecState*, JSObject*, JSValue*, const ArgList&);
65 static JSValue* stringProtoFuncFontcolor(ExecState*, JSObject*, JSValue*, const ArgList&);
66 static JSValue* stringProtoFuncFontsize(ExecState*, JSObject*, JSValue*, const ArgList&);
67 static JSValue* stringProtoFuncAnchor(ExecState*, JSObject*, JSValue*, const ArgList&);
68 static JSValue* stringProtoFuncLink(ExecState*, JSObject*, JSValue*, const ArgList&);
69
70 }
71
72 #include "StringPrototype.lut.h"
73
74 namespace KJS {
75
76 const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, ExecState::stringTable };
77
78 /* Source for StringPrototype.lut.h
79 @begin stringTable 26
80   toString              stringProtoFuncToString          DontEnum|Function       0
81   valueOf               stringProtoFuncToString          DontEnum|Function       0
82   charAt                stringProtoFuncCharAt            DontEnum|Function       1
83   charCodeAt            stringProtoFuncCharCodeAt        DontEnum|Function       1
84   concat                stringProtoFuncConcat            DontEnum|Function       1
85   indexOf               stringProtoFuncIndexOf           DontEnum|Function       1
86   lastIndexOf           stringProtoFuncLastIndexOf       DontEnum|Function       1
87   match                 stringProtoFuncMatch             DontEnum|Function       1
88   replace               stringProtoFuncReplace           DontEnum|Function       2
89   search                stringProtoFuncSearch            DontEnum|Function       1
90   slice                 stringProtoFuncSlice             DontEnum|Function       2
91   split                 stringProtoFuncSplit             DontEnum|Function       2
92   substr                stringProtoFuncSubstr            DontEnum|Function       2
93   substring             stringProtoFuncSubstring         DontEnum|Function       2
94   toLowerCase           stringProtoFuncToLowerCase       DontEnum|Function       0
95   toUpperCase           stringProtoFuncToUpperCase       DontEnum|Function       0
96   toLocaleLowerCase     stringProtoFuncToLocaleLowerCase DontEnum|Function       0
97   toLocaleUpperCase     stringProtoFuncToLocaleUpperCase DontEnum|Function       0
98   localeCompare         stringProtoFuncLocaleCompare     DontEnum|Function       1
99
100   big                   stringProtoFuncBig               DontEnum|Function       0
101   small                 stringProtoFuncSmall             DontEnum|Function       0
102   blink                 stringProtoFuncBlink             DontEnum|Function       0
103   bold                  stringProtoFuncBold              DontEnum|Function       0
104   fixed                 stringProtoFuncFixed             DontEnum|Function       0
105   italics               stringProtoFuncItalics           DontEnum|Function       0
106   strike                stringProtoFuncStrike            DontEnum|Function       0
107   sub                   stringProtoFuncSub               DontEnum|Function       0
108   sup                   stringProtoFuncSup               DontEnum|Function       0
109   fontcolor             stringProtoFuncFontcolor         DontEnum|Function       1
110   fontsize              stringProtoFuncFontsize          DontEnum|Function       1
111   anchor                stringProtoFuncAnchor            DontEnum|Function       1
112   link                  stringProtoFuncLink              DontEnum|Function       1
113 @end
114 */
115
116 // ECMA 15.5.4
117 StringPrototype::StringPrototype(ExecState* exec, ObjectPrototype* objProto)
118   : StringObject(exec, objProto)
119 {
120   // The constructor will be added later, after StringConstructor has been built
121   putDirect(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum);
122 }
123
124 bool StringPrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot &slot)
125 {
126   return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), this, propertyName, slot);
127 }
128
129 // ------------------------------ Functions --------------------------
130
131 static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg)
132 {
133   UString substitutedReplacement;
134   int offset = 0;
135   int i = -1;
136   while ((i = replacement.find('$', i + 1)) != -1) {
137     if (i + 1 == replacement.size())
138         break;
139
140     unsigned short ref = replacement[i + 1];
141     if (ref == '$') {
142         // "$$" -> "$"
143         ++i;
144         substitutedReplacement.append(replacement.data() + offset, i - offset);
145         offset = i + 1;
146         substitutedReplacement.append('$');
147         continue;
148     }
149
150     int backrefStart;
151     int backrefLength;
152     int advance = 0;
153     if (ref == '&') {
154         backrefStart = ovector[0];
155         backrefLength = ovector[1] - backrefStart;
156     } else if (ref == '`') {
157         backrefStart = 0;
158         backrefLength = ovector[0];
159     } else if (ref == '\'') {
160         backrefStart = ovector[1];
161         backrefLength = source.size() - backrefStart;
162     } else if (ref >= '0' && ref <= '9') {
163         // 1- and 2-digit back references are allowed
164         unsigned backrefIndex = ref - '0';
165         if (backrefIndex > reg->numSubpatterns())
166             continue;
167         if (replacement.size() > i + 2) {
168             ref = replacement[i + 2];
169             if (ref >= '0' && ref <= '9') {
170                 backrefIndex = 10 * backrefIndex + ref - '0';
171                 if (backrefIndex > reg->numSubpatterns())
172                     backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
173                 else
174                     advance = 1;
175             }
176         }
177         backrefStart = ovector[2 * backrefIndex];
178         backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
179     } else
180         continue;
181
182     if (i - offset)
183         substitutedReplacement.append(replacement.data() + offset, i - offset);
184     i += 1 + advance;
185     offset = i + 1;
186     substitutedReplacement.append(source.data() + backrefStart, backrefLength);
187   }
188
189   if (!offset)
190     return replacement;
191
192   if (replacement.size() - offset)
193     substitutedReplacement.append(replacement.data() + offset, replacement.size() - offset);
194
195   return substitutedReplacement;
196 }
197
198 static inline int localeCompare(const UString& a, const UString& b)
199 {
200     return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.data()), a.size(), reinterpret_cast<const ::UChar*>(b.data()), b.size());
201 }
202
203 JSValue* stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
204 {
205   JSString* sourceVal = thisValue->toThisJSString(exec);
206   const UString& source = sourceVal->value();
207
208   JSValue* pattern = args[0];
209
210   JSValue* replacement = args[1];
211   UString replacementString;
212   CallData callData;
213   CallType callType = replacement->getCallData(callData);
214   if (callType == CallTypeNone)
215     replacementString = replacement->toString(exec);
216
217   if (pattern->isObject(&RegExpObject::info)) {
218     RegExp* reg = static_cast<RegExpObject*>(pattern)->regExp();
219     bool global = reg->global();
220
221     RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor();
222
223     int lastIndex = 0;
224     int startPosition = 0;
225
226     Vector<UString::Range, 16> sourceRanges;
227     Vector<UString, 16> replacements;
228
229     // This is either a loop (if global is set) or a one-way (if not).
230     do {
231       int matchIndex;
232       int matchLen;
233       int* ovector;
234       regExpObj->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector);
235       if (matchIndex < 0)
236         break;
237
238       sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex));
239
240       if (callType != CallTypeNone) {
241           int completeMatchStart = ovector[0];
242           ArgList args;
243
244           for (unsigned i = 0; i < reg->numSubpatterns() + 1; i++) {
245               int matchStart = ovector[i * 2];
246               int matchLen = ovector[i * 2 + 1] - matchStart;
247
248               if (matchStart < 0)
249                 args.append(jsUndefined());
250               else
251                 args.append(jsString(exec, source.substr(matchStart, matchLen)));
252           }
253           
254           args.append(jsNumber(exec, completeMatchStart));
255           args.append(sourceVal);
256
257           replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec));
258       } else
259           replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
260
261       lastIndex = matchIndex + matchLen;
262       startPosition = lastIndex;
263
264       // special case of empty match
265       if (matchLen == 0) {
266         startPosition++;
267         if (startPosition > source.size())
268           break;
269       }
270     } while (global);
271
272     if (lastIndex < source.size())
273       sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex));
274
275     UString result = source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size());
276
277     if (result == source)
278       return sourceVal;
279
280     return jsString(exec, result);
281   }
282   
283   // First arg is a string
284   UString patternString = pattern->toString(exec);
285   int matchPos = source.find(patternString);
286   int matchLen = patternString.size();
287   // Do the replacement
288   if (matchPos == -1)
289     return sourceVal;
290   
291   if (callType != CallTypeNone) {
292       ArgList args;
293       
294       args.append(jsString(exec, source.substr(matchPos, matchLen)));
295       args.append(jsNumber(exec, matchPos));
296       args.append(sourceVal);
297       
298       replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec);
299   }
300
301   return jsString(exec, source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen));
302 }
303
304 JSValue* stringProtoFuncToString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
305 {
306     // Also used for valueOf.
307
308     if (thisValue->isString())
309         return thisValue;
310
311     if (thisValue->isObject(&StringObject::info))
312         return static_cast<StringObject*>(thisValue)->internalValue();
313
314     return throwError(exec, TypeError);
315 }
316
317 JSValue* stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
318 {
319     UString s = thisValue->toThisString(exec);
320     int len = s.size();
321
322     UString u;
323     JSValue* a0 = args[0];
324     double dpos = a0->toInteger(exec);
325     if (dpos >= 0 && dpos < len)
326       u = s.substr(static_cast<int>(dpos), 1);
327     else
328       u = "";
329     return jsString(exec, u);
330 }
331
332 JSValue* stringProtoFuncCharCodeAt(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
333 {
334     UString s = thisValue->toThisString(exec);
335     int len = s.size();
336
337     JSValue* result = 0;
338
339     JSValue* a0 = args[0];
340     double dpos = a0->toInteger(exec);
341     if (dpos >= 0 && dpos < len)
342       result = jsNumber(exec, s[static_cast<int>(dpos)]);
343     else
344       result = jsNaN(exec);
345     return result;
346 }
347
348 JSValue* stringProtoFuncConcat(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
349 {
350     UString s = thisValue->toThisString(exec);
351
352     ArgList::const_iterator end = args.end();
353     for (ArgList::const_iterator it = args.begin(); it != end; ++it) {
354         s += (*it)->toString(exec);
355     }
356     return jsString(exec, s);
357 }
358
359 JSValue* stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
360 {
361     UString s = thisValue->toThisString(exec);
362     int len = s.size();
363
364     JSValue* a0 = args[0];
365     JSValue* a1 = args[1];
366     UString u2 = a0->toString(exec);
367     double dpos = a1->toInteger(exec);
368     if (dpos < 0)
369         dpos = 0;
370     else if (dpos > len)
371         dpos = len;
372     return jsNumber(exec, s.find(u2, static_cast<int>(dpos)));
373 }
374
375 JSValue* stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
376 {
377     UString s = thisValue->toThisString(exec);
378     int len = s.size();
379
380     JSValue* a0 = args[0];
381     JSValue* a1 = args[1];
382     
383     UString u2 = a0->toString(exec);
384     double dpos = a1->toIntegerPreserveNaN(exec);
385     if (dpos < 0)
386         dpos = 0;
387     else if (!(dpos <= len)) // true for NaN
388         dpos = len;
389     return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos)));
390 }
391
392 JSValue* stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
393 {
394     UString s = thisValue->toThisString(exec);
395
396     JSValue* a0 = args[0];
397
398     UString u = s;
399     RefPtr<RegExp> reg;
400     RegExpObject* imp = 0;
401     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpObject::info)) {
402       reg = static_cast<RegExpObject *>(a0)->regExp();
403     } else { 
404       /*
405        *  ECMA 15.5.4.12 String.prototype.search (regexp)
406        *  If regexp is not an object whose [[Class]] property is "RegExp", it is
407        *  replaced with the result of the expression new RegExp(regexp).
408        */
409       reg = RegExp::create(a0->toString(exec));
410     }
411     RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor();
412     int pos;
413     int matchLength;
414     regExpObj->performMatch(reg.get(), u, 0, pos, matchLength);
415     JSValue* result;
416     if (!(reg->global())) {
417       // case without 'g' flag is handled like RegExp.prototype.exec
418       if (pos < 0)
419         result = jsNull();
420       else
421         result = regExpObj->arrayOfMatches(exec);
422     } else {
423       // return array of matches
424       ArgList list;
425       int lastIndex = 0;
426       while (pos >= 0) {
427         list.append(jsString(exec, u.substr(pos, matchLength)));
428         lastIndex = pos;
429         pos += matchLength == 0 ? 1 : matchLength;
430         regExpObj->performMatch(reg.get(), u, pos, pos, matchLength);
431       }
432       if (imp)
433         imp->setLastIndex(lastIndex);
434       if (list.isEmpty()) {
435         // if there are no matches at all, it's important to return
436         // Null instead of an empty array, because this matches
437         // other browsers and because Null is a false value.
438         result = jsNull();
439       } else {
440         result = constructArray(exec, list);
441       }
442     }
443     return result;
444 }
445
446 JSValue* stringProtoFuncSearch(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
447 {
448     UString s = thisValue->toThisString(exec);
449
450     JSValue* a0 = args[0];
451
452     UString u = s;
453     RefPtr<RegExp> reg;
454     if (a0->isObject() && static_cast<JSObject*>(a0)->inherits(&RegExpObject::info)) {
455       reg = static_cast<RegExpObject*>(a0)->regExp();
456     } else { 
457       /*
458        *  ECMA 15.5.4.12 String.prototype.search (regexp)
459        *  If regexp is not an object whose [[Class]] property is "RegExp", it is
460        *  replaced with the result of the expression new RegExp(regexp).
461        */
462       reg = RegExp::create(a0->toString(exec));
463     }
464     RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor();
465     int pos;
466     int matchLength;
467     regExpObj->performMatch(reg.get(), u, 0, pos, matchLength);
468     return jsNumber(exec, pos);
469 }
470
471 JSValue* stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
472 {
473     UString s = thisValue->toThisString(exec);
474     int len = s.size();
475
476     JSValue* a0 = args[0];
477     JSValue* a1 = args[1];
478
479     // The arg processing is very much like ArrayProtoFunc::Slice
480     double start = a0->toInteger(exec);
481     double end = a1->isUndefined() ? len : a1->toInteger(exec);
482     double from = start < 0 ? len + start : start;
483     double to = end < 0 ? len + end : end;
484     if (to > from && to > 0 && from < len) {
485         if (from < 0)
486             from = 0;
487         if (to > len)
488             to = len;
489         return jsString(exec, s.substr(static_cast<int>(from), static_cast<int>(to - from)));
490     }
491
492     return jsString(exec, "");
493 }
494
495 JSValue* stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
496 {
497     UString s = thisValue->toThisString(exec);
498
499     JSValue* a0 = args[0];
500     JSValue* a1 = args[1];
501
502     JSObject* res = constructEmptyArray(exec);
503     JSValue* result = res;
504     UString u = s;
505     int pos;
506     int i = 0;
507     int p0 = 0;
508     uint32_t limit = a1->isUndefined() ? 0xFFFFFFFFU : a1->toUInt32(exec);
509     if (a0->isObject() && static_cast<JSObject *>(a0)->inherits(&RegExpObject::info)) {
510       RegExp *reg = static_cast<RegExpObject *>(a0)->regExp();
511       if (u.isEmpty() && reg->match(u, 0) >= 0) {
512         // empty string matched by regexp -> empty array
513         res->put(exec, exec->propertyNames().length, jsNumber(exec, 0));
514         return result;
515       }
516       pos = 0;
517       while (static_cast<uint32_t>(i) != limit && pos < u.size()) {
518         OwnArrayPtr<int> ovector;
519         int mpos = reg->match(u, pos, &ovector);
520         if (mpos < 0)
521           break;
522         int mlen = ovector[1] - ovector[0];
523         pos = mpos + (mlen == 0 ? 1 : mlen);
524         if (mpos != p0 || mlen) {
525           res->put(exec,i, jsString(exec, u.substr(p0, mpos-p0)));
526           p0 = mpos + mlen;
527           i++;
528         }
529         for (unsigned si = 1; si <= reg->numSubpatterns(); ++si) {
530           int spos = ovector[si * 2];
531           if (spos < 0)
532             res->put(exec, i++, jsUndefined());
533           else
534             res->put(exec, i++, jsString(exec, u.substr(spos, ovector[si * 2 + 1] - spos)));
535         }
536       }
537     } else {
538       UString u2 = a0->toString(exec);
539       if (u2.isEmpty()) {
540         if (u.isEmpty()) {
541           // empty separator matches empty string -> empty array
542           res->put(exec, exec->propertyNames().length, jsNumber(exec, 0));
543           return result;
544         } else {
545           while (static_cast<uint32_t>(i) != limit && i < u.size()-1)
546             res->put(exec, i++, jsString(exec, u.substr(p0++, 1)));
547         }
548       } else {
549         while (static_cast<uint32_t>(i) != limit && (pos = u.find(u2, p0)) >= 0) {
550           res->put(exec, i, jsString(exec, u.substr(p0, pos - p0)));
551           p0 = pos + u2.size();
552           i++;
553         }
554       }
555     }
556     // add remaining string, if any
557     if (static_cast<uint32_t>(i) != limit)
558       res->put(exec, i++, jsString(exec, u.substr(p0)));
559     res->put(exec, exec->propertyNames().length, jsNumber(exec, i));
560     return result;
561 }
562
563 JSValue* stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
564 {
565     UString s = thisValue->toThisString(exec);
566     int len = s.size();
567
568     JSValue* a0 = args[0];
569     JSValue* a1 = args[1];
570
571     double start = a0->toInteger(exec);
572     double length = a1->isUndefined() ? len : a1->toInteger(exec);
573     if (start >= len)
574       return jsString(exec, "");
575     if (length < 0)
576       return jsString(exec, "");
577     if (start < 0) {
578       start += len;
579       if (start < 0)
580         start = 0;
581     }
582     if (length > len)
583       length = len;
584     return jsString(exec, s.substr(static_cast<int>(start), static_cast<int>(length)));
585 }
586
587 JSValue* stringProtoFuncSubstring(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
588 {
589     UString s = thisValue->toThisString(exec);
590     int len = s.size();
591
592     JSValue* a0 = args[0];
593     JSValue* a1 = args[1];
594
595     double start = a0->toNumber(exec);
596     double end = a1->toNumber(exec);
597     if (isnan(start))
598       start = 0;
599     if (isnan(end))
600       end = 0;
601     if (start < 0)
602       start = 0;
603     if (end < 0)
604       end = 0;
605     if (start > len)
606       start = len;
607     if (end > len)
608       end = len;
609     if (a1->isUndefined())
610       end = len;
611     if (start > end) {
612       double temp = end;
613       end = start;
614       start = temp;
615     }
616     return jsString(exec, s.substr((int)start, (int)end-(int)start));
617 }
618
619 JSValue* stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
620 {
621     JSString* sVal = thisValue->toThisJSString(exec);
622     const UString& s = sVal->value();
623     
624     int ssize = s.size();
625     if (!ssize)
626         return sVal;
627     Vector<UChar> buffer(ssize);
628     bool error;
629     int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
630     if (error) {
631         buffer.resize(length);
632         length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
633         if (error)
634             return sVal;
635     }
636     if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
637         return sVal;
638     return jsString(exec, UString(buffer.releaseBuffer(), length, false));
639 }
640
641 JSValue* stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
642 {
643     JSString* sVal = thisValue->toThisJSString(exec);
644     const UString& s = sVal->value();
645     
646     int ssize = s.size();
647     if (!ssize)
648         return sVal;
649     Vector<UChar> buffer(ssize);
650     bool error;
651     int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
652     if (error) {
653         buffer.resize(length);
654         length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
655         if (error)
656             return sVal;
657     }
658     if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
659         return sVal;
660     return jsString(exec, UString(buffer.releaseBuffer(), length, false));
661 }
662
663 JSValue* stringProtoFuncToLocaleLowerCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
664 {
665     // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented.
666
667     JSString* sVal = thisValue->toThisJSString(exec);
668     const UString& s = sVal->value();
669     
670     int ssize = s.size();
671     if (!ssize)
672         return sVal;
673     Vector<UChar> buffer(ssize);
674     bool error;
675     int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
676     if (error) {
677         buffer.resize(length);
678         length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
679         if (error)
680             return sVal;
681     }
682     if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
683         return sVal;
684     return jsString(exec, UString(buffer.releaseBuffer(), length, false));
685 }
686
687 JSValue* stringProtoFuncToLocaleUpperCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
688 {
689     JSString* sVal = thisValue->toThisJSString(exec);
690     const UString& s = sVal->value();
691     
692     int ssize = s.size();
693     if (!ssize)
694         return sVal;
695     Vector<UChar> buffer(ssize);
696     bool error;
697     int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
698     if (error) {
699         buffer.resize(length);
700         length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error);
701         if (error)
702             return sVal;
703     }
704     if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0)
705         return sVal;
706     return jsString(exec, UString(buffer.releaseBuffer(), length, false));
707 }
708
709 JSValue* stringProtoFuncLocaleCompare(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
710 {
711     if (args.size() < 1)
712       return jsNumber(exec, 0);
713
714     UString s = thisValue->toThisString(exec);
715     JSValue* a0 = args[0];
716     return jsNumber(exec, localeCompare(s, a0->toString(exec)));
717 }
718
719 JSValue* stringProtoFuncBig(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
720 {
721     UString s = thisValue->toThisString(exec);
722     return jsString(exec, "<big>" + s + "</big>");
723 }
724
725 JSValue* stringProtoFuncSmall(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
726 {
727     UString s = thisValue->toThisString(exec);
728     return jsString(exec, "<small>" + s + "</small>");
729 }
730
731 JSValue* stringProtoFuncBlink(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
732 {
733     UString s = thisValue->toThisString(exec);
734     return jsString(exec, "<blink>" + s + "</blink>");
735 }
736
737 JSValue* stringProtoFuncBold(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
738 {
739     UString s = thisValue->toThisString(exec);
740     return jsString(exec, "<b>" + s + "</b>");
741 }
742
743 JSValue* stringProtoFuncFixed(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
744 {
745     UString s = thisValue->toThisString(exec);
746     return jsString(exec, "<tt>" + s + "</tt>");
747 }
748
749 JSValue* stringProtoFuncItalics(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
750 {
751     UString s = thisValue->toThisString(exec);
752     return jsString(exec, "<i>" + s + "</i>");
753 }
754
755 JSValue* stringProtoFuncStrike(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
756 {
757     UString s = thisValue->toThisString(exec);
758     return jsString(exec, "<strike>" + s + "</strike>");
759 }
760
761 JSValue* stringProtoFuncSub(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
762 {
763     UString s = thisValue->toThisString(exec);
764     return jsString(exec, "<sub>" + s + "</sub>");
765 }
766
767 JSValue* stringProtoFuncSup(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
768 {
769     UString s = thisValue->toThisString(exec);
770     return jsString(exec, "<sup>" + s + "</sup>");
771 }
772
773 JSValue* stringProtoFuncFontcolor(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
774 {
775     UString s = thisValue->toThisString(exec);
776     JSValue* a0 = args[0];
777     return jsString(exec, "<font color=\"" + a0->toString(exec) + "\">" + s + "</font>");
778 }
779
780 JSValue* stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
781 {
782     UString s = thisValue->toThisString(exec);
783     JSValue* a0 = args[0];
784     return jsString(exec, "<font size=\"" + a0->toString(exec) + "\">" + s + "</font>");
785 }
786
787 JSValue* stringProtoFuncAnchor(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
788 {
789     UString s = thisValue->toThisString(exec);
790     JSValue* a0 = args[0];
791     return jsString(exec, "<a name=\"" + a0->toString(exec) + "\">" + s + "</a>");
792 }
793
794 JSValue* stringProtoFuncLink(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
795 {
796     UString s = thisValue->toThisString(exec);
797     JSValue* a0 = args[0];
798     return jsString(exec, "<a href=\"" + a0->toString(exec) + "\">" + s + "</a>");
799 }
800
801 } // namespace KJS