tizen beta release
[framework/web/webkit-efl.git] / Source / 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 "Error.h"
27 #include "Executable.h"
28 #include "JSGlobalObjectFunctions.h"
29 #include "JSArray.h"
30 #include "JSFunction.h"
31 #include "JSStringBuilder.h"
32 #include "Lookup.h"
33 #include "ObjectPrototype.h"
34 #include "Operations.h"
35 #include "PropertyNameArray.h"
36 #include "RegExpCache.h"
37 #include "RegExpConstructor.h"
38 #include "RegExpObject.h"
39 #include <wtf/ASCIICType.h>
40 #include <wtf/MathExtras.h>
41 #include <wtf/unicode/Collator.h>
42
43 using namespace WTF;
44
45 namespace JSC {
46
47 ASSERT_CLASS_FITS_IN_CELL(StringPrototype);
48
49 static EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
50 static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
51 static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
52 static EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
53 static EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
54 static EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
55 static EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*);
56 static EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
57 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*);
58 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
59 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*);
60 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
61 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
62 static EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
63 static EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
64 static EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
65 static EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
66 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
67 static EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
68 static EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
69 static EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
70 static EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
71 static EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
72 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
73 static EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
74 static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
75 static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
76 static EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
77 static EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
78 static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
79 static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
80 static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
81
82 }
83
84 #include "StringPrototype.lut.h"
85
86 namespace JSC {
87
88 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, 0, ExecState::stringTable, CREATE_METHOD_TABLE(StringPrototype) };
89
90 /* Source for StringPrototype.lut.h
91 @begin stringTable 26
92     toString              stringProtoFuncToString          DontEnum|Function       0
93     valueOf               stringProtoFuncToString          DontEnum|Function       0
94     charAt                stringProtoFuncCharAt            DontEnum|Function       1
95     charCodeAt            stringProtoFuncCharCodeAt        DontEnum|Function       1
96     concat                stringProtoFuncConcat            DontEnum|Function       1
97     indexOf               stringProtoFuncIndexOf           DontEnum|Function       1
98     lastIndexOf           stringProtoFuncLastIndexOf       DontEnum|Function       1
99     match                 stringProtoFuncMatch             DontEnum|Function       1
100     replace               stringProtoFuncReplace           DontEnum|Function       2
101     search                stringProtoFuncSearch            DontEnum|Function       1
102     slice                 stringProtoFuncSlice             DontEnum|Function       2
103     split                 stringProtoFuncSplit             DontEnum|Function       2
104     substr                stringProtoFuncSubstr            DontEnum|Function       2
105     substring             stringProtoFuncSubstring         DontEnum|Function       2
106     toLowerCase           stringProtoFuncToLowerCase       DontEnum|Function       0
107     toUpperCase           stringProtoFuncToUpperCase       DontEnum|Function       0
108     localeCompare         stringProtoFuncLocaleCompare     DontEnum|Function       1
109
110     # toLocaleLowerCase and toLocaleUpperCase are currently identical to toLowerCase and toUpperCase
111     toLocaleLowerCase     stringProtoFuncToLowerCase       DontEnum|Function       0
112     toLocaleUpperCase     stringProtoFuncToUpperCase       DontEnum|Function       0
113
114     big                   stringProtoFuncBig               DontEnum|Function       0
115     small                 stringProtoFuncSmall             DontEnum|Function       0
116     blink                 stringProtoFuncBlink             DontEnum|Function       0
117     bold                  stringProtoFuncBold              DontEnum|Function       0
118     fixed                 stringProtoFuncFixed             DontEnum|Function       0
119     italics               stringProtoFuncItalics           DontEnum|Function       0
120     strike                stringProtoFuncStrike            DontEnum|Function       0
121     sub                   stringProtoFuncSub               DontEnum|Function       0
122     sup                   stringProtoFuncSup               DontEnum|Function       0
123     fontcolor             stringProtoFuncFontcolor         DontEnum|Function       1
124     fontsize              stringProtoFuncFontsize          DontEnum|Function       1
125     anchor                stringProtoFuncAnchor            DontEnum|Function       1
126     link                  stringProtoFuncLink              DontEnum|Function       1
127     trim                  stringProtoFuncTrim              DontEnum|Function       0
128     trimLeft              stringProtoFuncTrimLeft          DontEnum|Function       0
129     trimRight             stringProtoFuncTrimRight         DontEnum|Function       0
130 @end
131 */
132
133 // ECMA 15.5.4
134 StringPrototype::StringPrototype(ExecState* exec, Structure* structure)
135     : StringObject(exec->globalData(), structure)
136 {
137 }
138
139 void StringPrototype::finishCreation(ExecState* exec, JSGlobalObject*, JSString* nameAndMessage)
140 {
141     Base::finishCreation(exec->globalData(), nameAndMessage);
142     ASSERT(inherits(&s_info));
143
144     // The constructor will be added later, after StringConstructor has been built
145     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
146 }
147
148 bool StringPrototype::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
149 {
150     return getStaticFunctionSlot<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(cell), propertyName, slot);
151 }
152
153 bool StringPrototype::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
154 {
155     return getStaticFunctionDescriptor<StringObject>(exec, ExecState::stringTable(exec), jsCast<StringPrototype*>(object), propertyName, descriptor);
156 }
157
158 // ------------------------------ Functions --------------------------
159
160 // Helper for producing a JSString for 'string', where 'string' was been produced by
161 // calling ToString on 'originalValue'. In cases where 'originalValue' already was a
162 // string primitive we can just use this, otherwise we need to allocate a new JSString.
163 static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const UString& string)
164 {
165     if (originalValue.isString()) {
166         ASSERT(asString(originalValue)->value(exec) == string);
167         return asString(originalValue);
168     }
169     return jsString(exec, string);
170 }
171
172 static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, size_t i)
173 {
174     Vector<UChar> substitutedReplacement;
175     int offset = 0;
176     do {
177         if (i + 1 == replacement.length())
178             break;
179
180         UChar ref = replacement[i + 1];
181         if (ref == '$') {
182             // "$$" -> "$"
183             ++i;
184             substitutedReplacement.append(replacement.characters() + offset, i - offset);
185             offset = i + 1;
186             continue;
187         }
188
189         int backrefStart;
190         int backrefLength;
191         int advance = 0;
192         if (ref == '&') {
193             backrefStart = ovector[0];
194             backrefLength = ovector[1] - backrefStart;
195         } else if (ref == '`') {
196             backrefStart = 0;
197             backrefLength = ovector[0];
198         } else if (ref == '\'') {
199             backrefStart = ovector[1];
200             backrefLength = source.length() - backrefStart;
201         } else if (reg && ref >= '0' && ref <= '9') {
202             // 1- and 2-digit back references are allowed
203             unsigned backrefIndex = ref - '0';
204             if (backrefIndex > reg->numSubpatterns())
205                 continue;
206             if (replacement.length() > i + 2) {
207                 ref = replacement[i + 2];
208                 if (ref >= '0' && ref <= '9') {
209                     backrefIndex = 10 * backrefIndex + ref - '0';
210                     if (backrefIndex > reg->numSubpatterns())
211                         backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
212                     else
213                         advance = 1;
214                 }
215             }
216             if (!backrefIndex)
217                 continue;
218             backrefStart = ovector[2 * backrefIndex];
219             backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
220         } else
221             continue;
222
223         if (i - offset)
224             substitutedReplacement.append(replacement.characters() + offset, i - offset);
225         i += 1 + advance;
226         offset = i + 1;
227         if (backrefStart >= 0)
228             substitutedReplacement.append(source.characters() + backrefStart, backrefLength);
229     } while ((i = replacement.find('$', i + 1)) != notFound);
230
231     if (replacement.length() - offset)
232         substitutedReplacement.append(replacement.characters() + offset, replacement.length() - offset);
233
234     substitutedReplacement.shrinkToFit();
235     return UString::adopt(substitutedReplacement);
236 }
237
238 static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg)
239 {
240     size_t i = replacement.find('$', 0);
241     if (UNLIKELY(i != notFound))
242         return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
243     return replacement;
244 }
245
246 static inline int localeCompare(const UString& a, const UString& b)
247 {
248     return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.characters()), a.length(), reinterpret_cast<const ::UChar*>(b.characters()), b.length());
249 }
250
251 struct StringRange {
252 public:
253     StringRange(int pos, int len)
254         : position(pos)
255         , length(len)
256     {
257     }
258
259     StringRange()
260     {
261     }
262
263     int position;
264     int length;
265 };
266
267 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount)
268 {
269     if (rangeCount == 1) {
270         int sourceSize = source.length();
271         int position = substringRanges[0].position;
272         int length = substringRanges[0].length;
273         if (position <= 0 && length >= sourceSize)
274             return sourceVal;
275         // We could call UString::substr, but this would result in redundant checks
276         return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length)));
277     }
278
279     int totalLength = 0;
280     for (int i = 0; i < rangeCount; i++)
281         totalLength += substringRanges[i].length;
282
283     if (!totalLength)
284         return jsString(exec, "");
285
286     if (source.is8Bit()) {
287         LChar* buffer;
288         const LChar* sourceData = source.characters8();
289         RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
290         if (!impl)
291             return throwOutOfMemoryError(exec);
292
293         int bufferPos = 0;
294         for (int i = 0; i < rangeCount; i++) {
295             if (int srcLen = substringRanges[i].length) {
296                 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
297                 bufferPos += srcLen;
298             }
299         }
300
301         return jsString(exec, impl.release());
302     }
303
304     UChar* buffer;
305     const UChar* sourceData = source.characters16();
306
307     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
308     if (!impl)
309         return throwOutOfMemoryError(exec);
310
311     int bufferPos = 0;
312     for (int i = 0; i < rangeCount; i++) {
313         if (int srcLen = substringRanges[i].length) {
314             StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
315             bufferPos += srcLen;
316         }
317     }
318
319     return jsString(exec, impl.release());
320 }
321
322 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount)
323 {
324     if (rangeCount == 1 && separatorCount == 0) {
325         int sourceSize = source.length();
326         int position = substringRanges[0].position;
327         int length = substringRanges[0].length;
328         if (position <= 0 && length >= sourceSize)
329             return sourceVal;
330         // We could call UString::substr, but this would result in redundant checks
331         return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length)));
332     }
333
334     int totalLength = 0;
335     bool allSeperators8Bit = true;
336     for (int i = 0; i < rangeCount; i++)
337         totalLength += substringRanges[i].length;
338     for (int i = 0; i < separatorCount; i++) {
339         totalLength += separators[i].length();
340         if (separators[i].length() && !separators[i].is8Bit())
341             allSeperators8Bit = false;
342     }
343
344     if (!totalLength)
345         return jsString(exec, "");
346
347     if (source.is8Bit() && allSeperators8Bit) {
348         LChar* buffer;
349         const LChar* sourceData = source.characters8();
350
351         RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
352         if (!impl)
353             return throwOutOfMemoryError(exec);
354
355         int maxCount = max(rangeCount, separatorCount);
356         int bufferPos = 0;
357         for (int i = 0; i < maxCount; i++) {
358             if (i < rangeCount) {
359                 if (int srcLen = substringRanges[i].length) {
360                     StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
361                     bufferPos += srcLen;
362                 }
363             }
364             if (i < separatorCount) {
365                 if (int sepLen = separators[i].length()) {
366                     StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
367                     bufferPos += sepLen;
368                 }
369             }
370         }        
371
372         return jsString(exec, impl.release());
373     }
374
375     UChar* buffer;
376     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
377     if (!impl)
378         return throwOutOfMemoryError(exec);
379
380     int maxCount = max(rangeCount, separatorCount);
381     int bufferPos = 0;
382     for (int i = 0; i < maxCount; i++) {
383         if (i < rangeCount) {
384             if (int srcLen = substringRanges[i].length) {
385                 StringImpl::copyChars(buffer + bufferPos, source.characters() + substringRanges[i].position, srcLen);
386                 bufferPos += srcLen;
387             }
388         }
389         if (i < separatorCount) {
390             if (int sepLen = separators[i].length()) {
391                 StringImpl::copyChars(buffer + bufferPos, separators[i].characters(), sepLen);
392                 bufferPos += sepLen;
393             }
394         }
395     }
396
397     return jsString(exec, impl.release());
398 }
399
400 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
401 {
402     JSValue thisValue = exec->hostThisValue();
403     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
404         return throwVMTypeError(exec);
405     JSString* sourceVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
406     JSValue pattern = exec->argument(0);
407     JSValue replacement = exec->argument(1);
408     JSGlobalData* globalData = &exec->globalData();
409
410     if (pattern.inherits(&RegExpObject::s_info)) {
411         UString replacementString;
412         CallData callData;
413         CallType callType = getCallData(replacement, callData);
414         if (callType == CallTypeNone)
415             replacementString = replacement.toString(exec);
416
417         const UString& source = sourceVal->value(exec);
418         unsigned sourceLen = source.length();
419         if (exec->hadException())
420             return JSValue::encode(JSValue());
421         RegExp* reg = asRegExpObject(pattern)->regExp();
422         bool global = reg->global();
423
424         RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
425
426         // Optimization for substring removal (replace with empty).
427         if (global && callType == CallTypeNone && !replacementString.length()) {
428             int lastIndex = 0;
429             unsigned startPosition = 0;
430
431             Vector<StringRange, 16> sourceRanges;
432
433             while (true) {
434                 int matchIndex;
435                 int matchLen = 0;
436                 int* ovector;
437                 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
438                 if (matchIndex < 0)
439                     break;
440
441                 if (lastIndex < matchIndex)
442                     sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
443
444                 lastIndex = matchIndex + matchLen;
445                 startPosition = lastIndex;
446
447                 // special case of empty match
448                 if (!matchLen) {
449                     startPosition++;
450                     if (startPosition > sourceLen)
451                         break;
452                 }
453             }
454
455             if (!lastIndex)
456                 return JSValue::encode(sourceVal);
457
458             if (static_cast<unsigned>(lastIndex) < sourceLen)
459                 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
460
461             return JSValue::encode(jsSpliceSubstrings(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size()));
462         }
463
464         int lastIndex = 0;
465         unsigned startPosition = 0;
466
467         Vector<StringRange, 16> sourceRanges;
468         Vector<UString, 16> replacements;
469
470         // This is either a loop (if global is set) or a one-way (if not).
471         if (global && callType == CallTypeJS) {
472             // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue
473             int argCount = reg->numSubpatterns() + 1 + 2;
474             JSFunction* func = asFunction(replacement);
475             CachedCall cachedCall(exec, func, argCount);
476             if (exec->hadException())
477                 return JSValue::encode(jsNull());
478             if (source.is8Bit()) {
479                 while (true) {
480                     int matchIndex;
481                     int matchLen = 0;
482                     int* ovector;
483                     regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
484                     if (matchIndex < 0)
485                         break;
486
487                     sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
488
489                     int completeMatchStart = ovector[0];
490                     unsigned i = 0;
491                     for (; i < reg->numSubpatterns() + 1; ++i) {
492                         int matchStart = ovector[i * 2];
493                         int matchLen = ovector[i * 2 + 1] - matchStart;
494
495                         if (matchStart < 0)
496                             cachedCall.setArgument(i, jsUndefined());
497                         else
498                             cachedCall.setArgument(i, jsSubstring8(globalData, source, matchStart, matchLen));
499                     }
500
501                     cachedCall.setArgument(i++, jsNumber(completeMatchStart));
502                     cachedCall.setArgument(i++, sourceVal);
503
504                     cachedCall.setThis(jsUndefined());
505                     JSValue result = cachedCall.call();
506                     if (LIKELY(result.isString()))
507                         replacements.append(asString(result)->value(exec));
508                     else
509                         replacements.append(result.toString(cachedCall.newCallFrame(exec)));
510                     if (exec->hadException())
511                         break;
512
513                     lastIndex = matchIndex + matchLen;
514                     startPosition = lastIndex;
515
516                     // special case of empty match
517                     if (!matchLen) {
518                         startPosition++;
519                         if (startPosition > sourceLen)
520                             break;
521                     }
522                 }
523             } else {
524                 while (true) {
525                     int matchIndex;
526                     int matchLen = 0;
527                     int* ovector;
528                     regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
529                     if (matchIndex < 0)
530                         break;
531
532                     sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
533
534                     int completeMatchStart = ovector[0];
535                     unsigned i = 0;
536                     for (; i < reg->numSubpatterns() + 1; ++i) {
537                         int matchStart = ovector[i * 2];
538                         int matchLen = ovector[i * 2 + 1] - matchStart;
539
540                         if (matchStart < 0)
541                             cachedCall.setArgument(i, jsUndefined());
542                         else
543                             cachedCall.setArgument(i, jsSubstring(globalData, source, matchStart, matchLen));
544                     }
545
546                     cachedCall.setArgument(i++, jsNumber(completeMatchStart));
547                     cachedCall.setArgument(i++, sourceVal);
548
549                     cachedCall.setThis(jsUndefined());
550                     JSValue result = cachedCall.call();
551                     if (LIKELY(result.isString()))
552                         replacements.append(asString(result)->value(exec));
553                     else
554                         replacements.append(result.toString(cachedCall.newCallFrame(exec)));
555                     if (exec->hadException())
556                         break;
557
558                     lastIndex = matchIndex + matchLen;
559                     startPosition = lastIndex;
560
561                     // special case of empty match
562                     if (!matchLen) {
563                         startPosition++;
564                         if (startPosition > sourceLen)
565                             break;
566                     }
567                 }
568             }
569         } else {
570             do {
571                 int matchIndex;
572                 int matchLen = 0;
573                 int* ovector;
574                 regExpConstructor->performMatch(*globalData, reg, source, startPosition, matchIndex, matchLen, &ovector);
575                 if (matchIndex < 0)
576                     break;
577
578                 if (callType != CallTypeNone) {
579                     sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
580
581                     int completeMatchStart = ovector[0];
582                     MarkedArgumentBuffer args;
583
584                     for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) {
585                         int matchStart = ovector[i * 2];
586                         int matchLen = ovector[i * 2 + 1] - matchStart;
587  
588                         if (matchStart < 0)
589                             args.append(jsUndefined());
590                         else
591                             args.append(jsSubstring(exec, source, matchStart, matchLen));
592                     }
593
594                     args.append(jsNumber(completeMatchStart));
595                     args.append(sourceVal);
596
597                     replacements.append(call(exec, replacement, callType, callData, jsUndefined(), args).toString(exec));
598                     if (exec->hadException())
599                         break;
600                 } else {
601                     int replLen = replacementString.length();
602                     if (lastIndex < matchIndex || replLen) {
603                         sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
604  
605                         if (replLen)
606                             replacements.append(substituteBackreferences(replacementString, source, ovector, reg));
607                         else
608                             replacements.append(UString());
609                     }
610                 }
611
612                 lastIndex = matchIndex + matchLen;
613                 startPosition = lastIndex;
614
615                 // special case of empty match
616                 if (!matchLen) {
617                     startPosition++;
618                     if (startPosition > sourceLen)
619                         break;
620                 }
621             } while (global);
622         }
623
624         if (!lastIndex && replacements.isEmpty())
625             return JSValue::encode(sourceVal);
626
627         if (static_cast<unsigned>(lastIndex) < sourceLen)
628             sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
629
630         return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
631     }
632
633     // Not a regular expression, so treat the pattern as a string.
634
635     // 'patternString' (or 'searchValue', as it is referred to in the spec) is converted before the replacement.
636     UString patternString = pattern.toString(exec);
637     if (exec->hadException())
638         return JSValue::encode(jsUndefined());
639
640     UString replacementString;
641     CallData callData;
642     CallType callType = getCallData(replacement, callData);
643     if (callType == CallTypeNone)
644         replacementString = replacement.toString(exec);
645     if (exec->hadException())
646         return JSValue::encode(jsUndefined());
647
648     // Special case for single character patterns without back reference replacement
649     if (patternString.length() == 1 && callType == CallTypeNone && replacementString.find('$', 0) == notFound)
650         return JSValue::encode(sourceVal->replaceCharacter(exec, patternString[0], replacementString));
651
652     const UString& source = sourceVal->value(exec);
653     size_t matchPos = source.find(patternString);
654
655     if (matchPos == notFound)
656         return JSValue::encode(sourceVal);
657
658     int matchLen = patternString.length();
659     if (callType != CallTypeNone) {
660         MarkedArgumentBuffer args;
661         args.append(jsSubstring(exec, source, matchPos, matchLen));
662         args.append(jsNumber(matchPos));
663         args.append(sourceVal);
664
665         replacementString = call(exec, replacement, callType, callData, jsUndefined(), args).toString(exec);
666     }
667     
668     size_t matchEnd = matchPos + matchLen;
669     int ovector[2] = { matchPos, matchEnd };
670     return JSValue::encode(jsString(exec, source.substringSharingImpl(0, matchPos), substituteBackreferences(replacementString, source, ovector, 0), source.substringSharingImpl(matchEnd)));
671 }
672
673 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
674 {
675     JSValue thisValue = exec->hostThisValue();
676     // Also used for valueOf.
677
678     if (thisValue.isString())
679         return JSValue::encode(thisValue);
680
681     if (thisValue.inherits(&StringObject::s_info))
682         return JSValue::encode(asStringObject(thisValue)->internalValue());
683
684     return throwVMTypeError(exec);
685 }
686
687 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
688 {
689     JSValue thisValue = exec->hostThisValue();
690     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
691         return throwVMTypeError(exec);
692     UString s = thisValue.toString(exec);
693     unsigned len = s.length();
694     JSValue a0 = exec->argument(0);
695     if (a0.isUInt32()) {
696         uint32_t i = a0.asUInt32();
697         if (i < len)
698             return JSValue::encode(jsSingleCharacterSubstring(exec, s, i));
699         return JSValue::encode(jsEmptyString(exec));
700     }
701     double dpos = a0.toInteger(exec);
702     if (dpos >= 0 && dpos < len)
703         return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)));
704     return JSValue::encode(jsEmptyString(exec));
705 }
706
707 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
708 {
709     JSValue thisValue = exec->hostThisValue();
710     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
711         return throwVMTypeError(exec);
712     UString s = thisValue.toString(exec);
713     unsigned len = s.length();
714     JSValue a0 = exec->argument(0);
715     if (a0.isUInt32()) {
716         uint32_t i = a0.asUInt32();
717         if (i < len) {
718             if (s.is8Bit())
719                 return JSValue::encode(jsNumber(s.characters8()[i]));
720             return JSValue::encode(jsNumber(s.characters16()[i]));
721         }
722         return JSValue::encode(jsNaN());
723     }
724     double dpos = a0.toInteger(exec);
725     if (dpos >= 0 && dpos < len)
726         return JSValue::encode(jsNumber(s[static_cast<int>(dpos)]));
727     return JSValue::encode(jsNaN());
728 }
729
730 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
731 {
732     JSValue thisValue = exec->hostThisValue();
733     if (thisValue.isString() && (exec->argumentCount() == 1)) {
734         JSValue v = exec->argument(0);
735         return JSValue::encode(v.isString()
736             ? jsString(exec, asString(thisValue), asString(v))
737             : jsString(exec, asString(thisValue), jsString(&exec->globalData(), v.toString(exec))));
738     }
739     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
740         return throwVMTypeError(exec);
741     return JSValue::encode(jsStringFromArguments(exec, thisValue));
742 }
743
744 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
745 {
746     JSValue thisValue = exec->hostThisValue();
747     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
748         return throwVMTypeError(exec);
749     UString s = thisValue.toString(exec);
750     int len = s.length();
751
752     JSValue a0 = exec->argument(0);
753     JSValue a1 = exec->argument(1);
754     UString u2 = a0.toString(exec);
755     int pos;
756     if (a1.isUndefined())
757         pos = 0;
758     else if (a1.isUInt32())
759         pos = min<uint32_t>(a1.asUInt32(), len);
760     else {
761         double dpos = a1.toInteger(exec);
762         if (dpos < 0)
763             dpos = 0;
764         else if (dpos > len)
765             dpos = len;
766         pos = static_cast<int>(dpos);
767     }
768
769     size_t result = s.find(u2, pos);
770     if (result == notFound)
771         return JSValue::encode(jsNumber(-1));
772     return JSValue::encode(jsNumber(result));
773 }
774
775 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
776 {
777     JSValue thisValue = exec->hostThisValue();
778     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
779         return throwVMTypeError(exec);
780     UString s = thisValue.toString(exec);
781     int len = s.length();
782
783     JSValue a0 = exec->argument(0);
784     JSValue a1 = exec->argument(1);
785
786     UString u2 = a0.toString(exec);
787     double dpos = a1.toIntegerPreserveNaN(exec);
788     if (dpos < 0)
789         dpos = 0;
790     else if (!(dpos <= len)) // true for NaN
791         dpos = len;
792
793     size_t result = s.reverseFind(u2, static_cast<unsigned>(dpos));
794     if (result == notFound)
795         return JSValue::encode(jsNumber(-1));
796     return JSValue::encode(jsNumber(result));
797 }
798
799 EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
800 {
801     JSValue thisValue = exec->hostThisValue();
802     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
803         return throwVMTypeError(exec);
804     UString s = thisValue.toString(exec);
805     JSGlobalData* globalData = &exec->globalData();
806
807     JSValue a0 = exec->argument(0);
808
809     RegExp* reg;
810     if (a0.inherits(&RegExpObject::s_info))
811         reg = asRegExpObject(a0)->regExp();
812     else {
813         /*
814          *  ECMA 15.5.4.12 String.prototype.search (regexp)
815          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
816          *  replaced with the result of the expression new RegExp(regexp).
817          *  Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
818          */
819         reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags);
820     }
821     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
822     int pos;
823     int matchLength = 0;
824     regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
825     if (!(reg->global())) {
826         // case without 'g' flag is handled like RegExp.prototype.exec
827         if (pos < 0)
828             return JSValue::encode(jsNull());
829         return JSValue::encode(regExpConstructor->arrayOfMatches(exec));
830     }
831
832     // return array of matches
833     MarkedArgumentBuffer list;
834     while (pos >= 0) {
835         list.append(jsSubstring(exec, s, pos, matchLength));
836         pos += matchLength == 0 ? 1 : matchLength;
837         regExpConstructor->performMatch(*globalData, reg, s, pos, pos, matchLength);
838     }
839     if (list.isEmpty()) {
840         // if there are no matches at all, it's important to return
841         // Null instead of an empty array, because this matches
842         // other browsers and because Null is a false value.
843         return JSValue::encode(jsNull());
844     }
845
846     return JSValue::encode(constructArray(exec, list));
847 }
848
849 EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec)
850 {
851     JSValue thisValue = exec->hostThisValue();
852     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
853         return throwVMTypeError(exec);
854     UString s = thisValue.toString(exec);
855     JSGlobalData* globalData = &exec->globalData();
856
857     JSValue a0 = exec->argument(0);
858
859     RegExp* reg;
860     if (a0.inherits(&RegExpObject::s_info))
861         reg = asRegExpObject(a0)->regExp();
862     else { 
863         /*
864          *  ECMA 15.5.4.12 String.prototype.search (regexp)
865          *  If regexp is not an object whose [[Class]] property is "RegExp", it is
866          *  replaced with the result of the expression new RegExp(regexp).
867          *  Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
868          */
869         reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec), NoFlags);
870     }
871     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
872     int pos;
873     int matchLength = 0;
874     regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
875     return JSValue::encode(jsNumber(pos));
876 }
877
878 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
879 {
880     JSValue thisValue = exec->hostThisValue();
881     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
882         return throwVMTypeError(exec);
883     UString s = thisValue.toString(exec);
884     int len = s.length();
885
886     JSValue a0 = exec->argument(0);
887     JSValue a1 = exec->argument(1);
888
889     // The arg processing is very much like ArrayProtoFunc::Slice
890     double start = a0.toInteger(exec);
891     double end = a1.isUndefined() ? len : a1.toInteger(exec);
892     double from = start < 0 ? len + start : start;
893     double to = end < 0 ? len + end : end;
894     if (to > from && to > 0 && from < len) {
895         if (from < 0)
896             from = 0;
897         if (to > len)
898             to = len;
899         return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
900     }
901
902     return JSValue::encode(jsEmptyString(exec));
903 }
904
905 // ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
906 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
907 {
908     // 1. Call CheckObjectCoercible passing the this value as its argument.
909     JSValue thisValue = exec->hostThisValue();
910     if (thisValue.isUndefinedOrNull())
911         return throwVMTypeError(exec);
912
913     // 2. Let S be the result of calling ToString, giving it the this value as its argument.
914     // 6. Let s be the number of characters in S.
915     UString input = thisValue.toString(exec);
916
917     // 3. Let A be a new array created as if by the expression new Array()
918     //    where Array is the standard built-in constructor with that name.
919     JSArray* result = constructEmptyArray(exec);
920
921     // 4. Let lengthA be 0.
922     unsigned resultLength = 0;
923
924     // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
925     JSValue limitValue = exec->argument(1);
926     unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
927
928     // 7. Let p = 0.
929     size_t position = 0;
930
931     // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
932     //    otherwise let R = ToString(separator).
933     JSValue separatorValue = exec->argument(0);
934     if (separatorValue.inherits(&RegExpObject::s_info)) {
935         JSGlobalData* globalData = &exec->globalData();
936         RegExp* reg = asRegExpObject(separatorValue)->regExp();
937
938         // 9. If lim == 0, return A.
939         if (!limit)
940             return JSValue::encode(result);
941
942         // 10. If separator is undefined, then
943         if (separatorValue.isUndefined()) {
944             // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
945             //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
946             result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
947             // b. Return A.
948             return JSValue::encode(result);
949         }
950
951         // 11. If s == 0, then
952         if (input.isEmpty()) {
953             // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
954             // b. If z is not failure, return A.
955             // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
956             //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
957             // d. Return A.
958             if (reg->match(*globalData, input, 0) < 0)
959                 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
960             return JSValue::encode(result);
961         }
962
963         // 12. Let q = p.
964         size_t matchPosition = 0;
965         // 13. Repeat, while q != s
966         while (matchPosition < input.length()) {
967             // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
968             Vector<int, 32> ovector;
969             int mpos = reg->match(*globalData, input, matchPosition, &ovector);
970             // b. If z is failure, then let q = q + 1.
971             if (mpos < 0)
972                 break;
973             matchPosition = mpos;
974
975             // c. Else, z is not failure
976             // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
977             size_t matchEnd = ovector[1];
978
979             // ii. If e == p, then let q = q + 1.
980             if (matchEnd == position) {
981                 ++matchPosition;
982                 continue;
983             }
984             // iii. Else, e != p
985
986             // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
987             //    through q (exclusive).
988             // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
989             //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
990             result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
991             // 3. Increment lengthA by 1.
992             // 4. If lengthA == lim, return A.
993             if (++resultLength == limit)
994                 return JSValue::encode(result);
995
996             // 5. Let p = e.
997             // 8. Let q = p.
998             position = matchEnd;
999             matchPosition = matchEnd;
1000
1001             // 6. Let i = 0.
1002             // 7. Repeat, while i is not equal to the number of elements in cap.
1003             //  a Let i = i + 1.
1004             for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
1005                 // b Call the [[DefineOwnProperty]] internal method of A with arguments
1006                 //   ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
1007                 //   true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1008                 int sub = ovector[i * 2];
1009                 result->methodTable()->putByIndex(result, exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, input, sub, ovector[i * 2 + 1] - sub));
1010                 // c Increment lengthA by 1.
1011                 // d If lengthA == lim, return A.
1012                 if (++resultLength == limit)
1013                     return JSValue::encode(result);
1014             }
1015         }
1016     } else {
1017         UString separator = separatorValue.toString(exec);
1018
1019         // 9. If lim == 0, return A.
1020         if (!limit)
1021             return JSValue::encode(result);
1022
1023         // 10. If separator is undefined, then
1024         JSValue separatorValue = exec->argument(0);
1025         if (separatorValue.isUndefined()) {
1026             // a.  Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1027             //     Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1028             result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
1029             // b.  Return A.
1030             return JSValue::encode(result);
1031         }
1032
1033         // 11. If s == 0, then
1034         if (input.isEmpty()) {
1035             // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
1036             // b. If z is not failure, return A.
1037             // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1038             //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1039             // d. Return A.
1040             if (!separator.isEmpty())
1041                 result->methodTable()->putByIndex(result, exec, 0, jsStringWithReuse(exec, thisValue, input));
1042             return JSValue::encode(result);
1043         }
1044
1045         // Optimized case for splitting on the empty string.
1046         if (separator.isEmpty()) {
1047             limit = std::min(limit, input.length());
1048             // Zero limt/input length handled in steps 9/11 respectively, above.
1049             ASSERT(limit);
1050
1051             do {
1052                 result->methodTable()->putByIndex(result, exec, position, jsSingleCharacterSubstring(exec, input, position));
1053             } while (++position < limit);
1054
1055             return JSValue::encode(result);
1056         }
1057
1058         // 12. Let q = p.
1059         size_t matchPosition;
1060         // 13. Repeat, while q != s
1061         //   a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1062         //   b. If z is failure, then let q = q+1.
1063         //   c. Else, z is not failure
1064         while ((matchPosition = input.find(separator, position)) != notFound) {
1065             // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1066             //    through q (exclusive).
1067             // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1068             //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1069             result->methodTable()->putByIndex(result, exec, resultLength, jsSubstring(exec, input, position, matchPosition - position));
1070             // 3. Increment lengthA by 1.
1071             // 4. If lengthA == lim, return A.
1072             if (++resultLength == limit)
1073                 return JSValue::encode(result);
1074
1075             // 5. Let p = e.
1076             // 8. Let q = p.
1077             position = matchPosition + separator.length();
1078         }
1079     }
1080
1081     // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1082     //     through s (exclusive).
1083     // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
1084     //     {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1085     result->methodTable()->putByIndex(result, exec, resultLength++, jsSubstring(exec, input, position, input.length() - position));
1086
1087     // 16. Return A.
1088     return JSValue::encode(result);
1089 }
1090
1091 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1092 {
1093     JSValue thisValue = exec->hostThisValue();
1094     unsigned len;
1095     JSString* jsString = 0;
1096     UString uString;
1097     if (thisValue.isString()) {
1098         jsString = static_cast<JSString*>(thisValue.asCell());
1099         len = jsString->length();
1100     } else if (thisValue.isUndefinedOrNull()) {
1101         // CheckObjectCoercible
1102         return throwVMTypeError(exec);
1103     } else {
1104         uString = thisValue.toString(exec);
1105         if (exec->hadException())
1106             return JSValue::encode(jsUndefined());
1107         len = uString.length();
1108     }
1109
1110     JSValue a0 = exec->argument(0);
1111     JSValue a1 = exec->argument(1);
1112
1113     double start = a0.toInteger(exec);
1114     double length = a1.isUndefined() ? len : a1.toInteger(exec);
1115     if (start >= len || length <= 0)
1116         return JSValue::encode(jsEmptyString(exec));
1117     if (start < 0) {
1118         start += len;
1119         if (start < 0)
1120             start = 0;
1121     }
1122     if (start + length > len)
1123         length = len - start;
1124     unsigned substringStart = static_cast<unsigned>(start);
1125     unsigned substringLength = static_cast<unsigned>(length);
1126     if (jsString)
1127         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1128     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1129 }
1130
1131 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1132 {
1133     JSValue thisValue = exec->hostThisValue();
1134     int len;
1135     JSString* jsString = 0;
1136     UString uString;
1137     if (thisValue.isString()) {
1138         jsString = static_cast<JSString*>(thisValue.asCell());
1139         len = jsString->length();
1140     } else if (thisValue.isUndefinedOrNull()) {
1141         // CheckObjectCoercible
1142         return throwVMTypeError(exec);
1143     } else {
1144         uString = thisValue.toString(exec);
1145         if (exec->hadException())
1146             return JSValue::encode(jsUndefined());
1147         len = uString.length();
1148     }
1149
1150     JSValue a0 = exec->argument(0);
1151     JSValue a1 = exec->argument(1);
1152
1153     double start = a0.toNumber(exec);
1154     double end;
1155     if (!(start >= 0)) // check for negative values or NaN
1156         start = 0;
1157     else if (start > len)
1158         start = len;
1159     if (a1.isUndefined())
1160         end = len;
1161     else { 
1162         end = a1.toNumber(exec);
1163         if (!(end >= 0)) // check for negative values or NaN
1164             end = 0;
1165         else if (end > len)
1166             end = len;
1167     }
1168     if (start > end) {
1169         double temp = end;
1170         end = start;
1171         start = temp;
1172     }
1173     unsigned substringStart = static_cast<unsigned>(start);
1174     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1175     if (jsString)
1176         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1177     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1178 }
1179
1180 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1181 {
1182     JSValue thisValue = exec->hostThisValue();
1183     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1184         return throwVMTypeError(exec);
1185     JSString* sVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
1186     const UString& s = sVal->value(exec);
1187
1188     int sSize = s.length();
1189     if (!sSize)
1190         return JSValue::encode(sVal);
1191
1192     StringImpl* ourImpl = s.impl();   
1193     RefPtr<StringImpl> lower = ourImpl->lower();
1194     if (ourImpl == lower.get())
1195         return JSValue::encode(sVal);
1196     return JSValue::encode(jsString(exec, UString(lower.release())));
1197 }
1198
1199 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1200 {
1201     JSValue thisValue = exec->hostThisValue();
1202     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1203         return throwVMTypeError(exec);
1204     JSString* sVal = thisValue.isString() ? asString(thisValue) : jsString(exec, thisValue.toString(exec));
1205     const UString& s = sVal->value(exec);
1206
1207     int sSize = s.length();
1208     if (!sSize)
1209         return JSValue::encode(sVal);
1210
1211     const UChar* sData = s.characters();
1212     Vector<UChar> buffer(sSize);
1213
1214     UChar ored = 0;
1215     for (int i = 0; i < sSize; i++) {
1216         UChar c = sData[i];
1217         ored |= c;
1218         buffer[i] = toASCIIUpper(c);
1219     }
1220     if (!(ored & ~0x7f))
1221         return JSValue::encode(jsString(exec, UString::adopt(buffer)));
1222
1223     bool error;
1224     int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error);
1225     if (error) {
1226         buffer.resize(length);
1227         length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error);
1228         if (error)
1229             return JSValue::encode(sVal);
1230     }
1231     if (length == sSize) {
1232         if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0)
1233             return JSValue::encode(sVal);
1234     } else
1235         buffer.resize(length);
1236     return JSValue::encode(jsString(exec, UString::adopt(buffer)));
1237 }
1238
1239 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1240 {
1241     if (exec->argumentCount() < 1)
1242       return JSValue::encode(jsNumber(0));
1243
1244     JSValue thisValue = exec->hostThisValue();
1245     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1246         return throwVMTypeError(exec);
1247     UString s = thisValue.toString(exec);
1248
1249     JSValue a0 = exec->argument(0);
1250     return JSValue::encode(jsNumber(localeCompare(s, a0.toString(exec))));
1251 }
1252
1253 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1254 {
1255     JSValue thisValue = exec->hostThisValue();
1256     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1257         return throwVMTypeError(exec);
1258     UString s = thisValue.toString(exec);
1259     return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1260 }
1261
1262 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1263 {
1264     JSValue thisValue = exec->hostThisValue();
1265     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1266         return throwVMTypeError(exec);
1267     UString s = thisValue.toString(exec);
1268     return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1269 }
1270
1271 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1272 {
1273     JSValue thisValue = exec->hostThisValue();
1274     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1275         return throwVMTypeError(exec);
1276     UString s = thisValue.toString(exec);
1277     return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1278 }
1279
1280 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1281 {
1282     JSValue thisValue = exec->hostThisValue();
1283     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1284         return throwVMTypeError(exec);
1285     UString s = thisValue.toString(exec);
1286     return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1287 }
1288
1289 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1290 {
1291     JSValue thisValue = exec->hostThisValue();
1292     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1293         return throwVMTypeError(exec);
1294     UString s = thisValue.toString(exec);
1295     return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1296 }
1297
1298 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1299 {
1300     JSValue thisValue = exec->hostThisValue();
1301     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1302         return throwVMTypeError(exec);
1303     UString s = thisValue.toString(exec);
1304     return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1305 }
1306
1307 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1308 {
1309     JSValue thisValue = exec->hostThisValue();
1310     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1311         return throwVMTypeError(exec);
1312     UString s = thisValue.toString(exec);
1313     return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1314 }
1315
1316 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1317 {
1318     JSValue thisValue = exec->hostThisValue();
1319     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1320         return throwVMTypeError(exec);
1321     UString s = thisValue.toString(exec);
1322     return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1323 }
1324
1325 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1326 {
1327     JSValue thisValue = exec->hostThisValue();
1328     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1329         return throwVMTypeError(exec);
1330     UString s = thisValue.toString(exec);
1331     return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1332 }
1333
1334 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1335 {
1336     JSValue thisValue = exec->hostThisValue();
1337     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1338         return throwVMTypeError(exec);
1339     UString s = thisValue.toString(exec);
1340     JSValue a0 = exec->argument(0);
1341     return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>"));
1342 }
1343
1344 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1345 {
1346     JSValue thisValue = exec->hostThisValue();
1347     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1348         return throwVMTypeError(exec);
1349     UString s = thisValue.toString(exec);
1350     JSValue a0 = exec->argument(0);
1351
1352     uint32_t smallInteger;
1353     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1354         unsigned stringSize = s.length();
1355         unsigned bufferSize = 22 + stringSize;
1356         UChar* buffer;
1357         PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1358         if (!impl)
1359             return JSValue::encode(jsUndefined());
1360         buffer[0] = '<';
1361         buffer[1] = 'f';
1362         buffer[2] = 'o';
1363         buffer[3] = 'n';
1364         buffer[4] = 't';
1365         buffer[5] = ' ';
1366         buffer[6] = 's';
1367         buffer[7] = 'i';
1368         buffer[8] = 'z';
1369         buffer[9] = 'e';
1370         buffer[10] = '=';
1371         buffer[11] = '"';
1372         buffer[12] = '0' + smallInteger;
1373         buffer[13] = '"';
1374         buffer[14] = '>';
1375         memcpy(&buffer[15], s.characters(), stringSize * sizeof(UChar));
1376         buffer[15 + stringSize] = '<';
1377         buffer[16 + stringSize] = '/';
1378         buffer[17 + stringSize] = 'f';
1379         buffer[18 + stringSize] = 'o';
1380         buffer[19 + stringSize] = 'n';
1381         buffer[20 + stringSize] = 't';
1382         buffer[21 + stringSize] = '>';
1383         return JSValue::encode(jsNontrivialString(exec, impl));
1384     }
1385
1386     return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>"));
1387 }
1388
1389 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1390 {
1391     JSValue thisValue = exec->hostThisValue();
1392     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1393         return throwVMTypeError(exec);
1394     UString s = thisValue.toString(exec);
1395     JSValue a0 = exec->argument(0);
1396     return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>"));
1397 }
1398
1399 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1400 {
1401     JSValue thisValue = exec->hostThisValue();
1402     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1403         return throwVMTypeError(exec);
1404     UString s = thisValue.toString(exec);
1405     JSValue a0 = exec->argument(0);
1406     UString linkText = a0.toString(exec);
1407
1408     unsigned linkTextSize = linkText.length();
1409     unsigned stringSize = s.length();
1410     unsigned bufferSize = 15 + linkTextSize + stringSize;
1411     UChar* buffer;
1412     PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1413     if (!impl)
1414         return JSValue::encode(jsUndefined());
1415     buffer[0] = '<';
1416     buffer[1] = 'a';
1417     buffer[2] = ' ';
1418     buffer[3] = 'h';
1419     buffer[4] = 'r';
1420     buffer[5] = 'e';
1421     buffer[6] = 'f';
1422     buffer[7] = '=';
1423     buffer[8] = '"';
1424     memcpy(&buffer[9], linkText.characters(), linkTextSize * sizeof(UChar));
1425     buffer[9 + linkTextSize] = '"';
1426     buffer[10 + linkTextSize] = '>';
1427     memcpy(&buffer[11 + linkTextSize], s.characters(), stringSize * sizeof(UChar));
1428     buffer[11 + linkTextSize + stringSize] = '<';
1429     buffer[12 + linkTextSize + stringSize] = '/';
1430     buffer[13 + linkTextSize + stringSize] = 'a';
1431     buffer[14 + linkTextSize + stringSize] = '>';
1432     return JSValue::encode(jsNontrivialString(exec, impl));
1433 }
1434
1435 enum {
1436     TrimLeft = 1,
1437     TrimRight = 2
1438 };
1439
1440 static inline bool isTrimWhitespace(UChar c)
1441 {
1442     return isStrWhiteSpace(c) || c == 0x200b;
1443 }
1444
1445 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1446 {
1447     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
1448         return throwTypeError(exec);
1449     UString str = thisValue.toString(exec);
1450     unsigned left = 0;
1451     if (trimKind & TrimLeft) {
1452         while (left < str.length() && isTrimWhitespace(str[left]))
1453             left++;
1454     }
1455     unsigned right = str.length();
1456     if (trimKind & TrimRight) {
1457         while (right > left && isTrimWhitespace(str[right - 1]))
1458             right--;
1459     }
1460
1461     // Don't gc allocate a new string if we don't have to.
1462     if (left == 0 && right == str.length() && thisValue.isString())
1463         return thisValue;
1464
1465     return jsString(exec, str.substringSharingImpl(left, right - left));
1466 }
1467
1468 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1469 {
1470     JSValue thisValue = exec->hostThisValue();
1471     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1472 }
1473
1474 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1475 {
1476     JSValue thisValue = exec->hostThisValue();
1477     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1478 }
1479
1480 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1481 {
1482     JSValue thisValue = exec->hostThisValue();
1483     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1484 }
1485     
1486     
1487 } // namespace JSC