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