00c51d02640412993bee1bcdde3d4a16b2c69059
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004-2008, 2013, 2016 Apple Inc. All rights reserved.
4  *  Copyright (C) 2009 Torch Mobile, Inc.
5  *  Copyright (C) 2015 Jordan Harband (ljharb@gmail.com)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #include "config.h"
24 #include "StringPrototype.h"
25
26 #include "ButterflyInlines.h"
27 #include "CachedCall.h"
28 #include "CopiedSpaceInlines.h"
29 #include "Error.h"
30 #include "Executable.h"
31 #include "IntlObject.h"
32 #include "JSCBuiltins.h"
33 #include "JSCInlines.h"
34 #include "JSGlobalObjectFunctions.h"
35 #include "JSArray.h"
36 #include "JSFunction.h"
37 #include "JSStringBuilder.h"
38 #include "JSStringIterator.h"
39 #include "Lookup.h"
40 #include "ObjectPrototype.h"
41 #include "PropertyNameArray.h"
42 #include "RegExpCache.h"
43 #include "RegExpConstructor.h"
44 #include "RegExpMatchesArray.h"
45 #include "RegExpObject.h"
46 #include "SuperSampler.h"
47 #include <algorithm>
48 #include <unicode/uconfig.h>
49 #include <unicode/unorm.h>
50 #include <unicode/ustring.h>
51 #include <wtf/ASCIICType.h>
52 #include <wtf/MathExtras.h>
53 #include <wtf/text/StringView.h>
54 #include <wtf/unicode/Collator.h>
55
56 using namespace WTF;
57
58 namespace JSC {
59
60 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringPrototype);
61
62 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
63 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
64 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
65 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*);
66 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
67 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
68 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
69 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadEnd(ExecState*);
70 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState*);
71 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
72 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
73 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
74 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
75 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
76 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
77 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
78 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
79 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState*);
80 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
81 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
82 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
83 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
84 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
85 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
86 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
87 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
88 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
89 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
90 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
91 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
92 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
93 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
94 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
95 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
96 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState*);
97 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState*);
98 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*);
99 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState*);
100 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
101
102 }
103
104 #include "StringPrototype.lut.h"
105
106 namespace JSC {
107
108 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, CREATE_METHOD_TABLE(StringPrototype) };
109
110 /* Source for StringConstructor.lut.h
111 @begin stringPrototypeTable
112     match     JSBuiltin    DontEnum|Function 1
113     repeat    JSBuiltin    DontEnum|Function 1
114     search    JSBuiltin    DontEnum|Function 1
115     split     JSBuiltin    DontEnum|Function 1
116 @end
117 */
118
119 // ECMA 15.5.4
120 StringPrototype::StringPrototype(VM& vm, Structure* structure)
121     : StringObject(vm, structure)
122 {
123 }
124
125 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
126 {
127     Base::finishCreation(vm, nameAndMessage);
128     ASSERT(inherits(info()));
129
130     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
131     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
132     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
133     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
134     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
135     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", stringProtoFuncConcat, DontEnum, 1);
136     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
137     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
138     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padEnd", stringProtoFuncPadEnd, DontEnum, 1);
139     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padStart", stringProtoFuncPadStart, DontEnum, 1);
140     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("replace", stringProtoFuncReplace, DontEnum, 2, StringPrototypeReplaceIntrinsic);
141     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
142     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
143     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
144     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
145     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
146     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
147 #if ENABLE(INTL)
148     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
149     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, DontEnum, 0);
150 #else
151     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
152     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
153 #endif
154     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("big", stringProtoFuncBig, DontEnum, 0);
155     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("small", stringProtoFuncSmall, DontEnum, 0);
156     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("blink", stringProtoFuncBlink, DontEnum, 0);
157     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("bold", stringProtoFuncBold, DontEnum, 0);
158     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fixed", stringProtoFuncFixed, DontEnum, 0);
159     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("italics", stringProtoFuncItalics, DontEnum, 0);
160     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("strike", stringProtoFuncStrike, DontEnum, 0);
161     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sub", stringProtoFuncSub, DontEnum, 0);
162     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sup", stringProtoFuncSup, DontEnum, 0);
163     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
164     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
165     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("anchor", stringProtoFuncAnchor, DontEnum, 1);
166     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("link", stringProtoFuncLink, DontEnum, 1);
167     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trim", stringProtoFuncTrim, DontEnum, 0);
168     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
169     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
170     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("startsWith", stringProtoFuncStartsWith, DontEnum, 1);
171     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("endsWith", stringProtoFuncEndsWith, DontEnum, 1);
172     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("includes", stringProtoFuncIncludes, DontEnum, 1);
173     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("normalize", stringProtoFuncNormalize, DontEnum, 1);
174     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->iteratorSymbol, stringProtoFuncIterator, DontEnum, 0);
175
176     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->charCodeAtPrivateName, stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
177
178     // The constructor will be added later, after StringConstructor has been built
179     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
180 }
181
182 StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
183 {
184     JSString* empty = jsEmptyString(&vm);
185     StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(vm.heap)) StringPrototype(vm, structure);
186     prototype->finishCreation(vm, globalObject, empty);
187     return prototype;
188 }
189
190 bool StringPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
191 {
192     return getStaticFunctionSlot<Base>(exec, stringPrototypeTable, jsCast<StringPrototype*>(object), propertyName, slot);
193 }
194
195 // ------------------------------ Functions --------------------------
196
197 static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
198 {
199     StringBuilder substitutedReplacement;
200     int offset = 0;
201     do {
202         if (i + 1 == replacement.length())
203             break;
204
205         UChar ref = replacement[i + 1];
206         if (ref == '$') {
207             // "$$" -> "$"
208             ++i;
209             substitutedReplacement.append(replacement.substring(offset, i - offset));
210             offset = i + 1;
211             continue;
212         }
213
214         int backrefStart;
215         int backrefLength;
216         int advance = 0;
217         if (ref == '&') {
218             backrefStart = ovector[0];
219             backrefLength = ovector[1] - backrefStart;
220         } else if (ref == '`') {
221             backrefStart = 0;
222             backrefLength = ovector[0];
223         } else if (ref == '\'') {
224             backrefStart = ovector[1];
225             backrefLength = source.length() - backrefStart;
226         } else if (reg && ref >= '0' && ref <= '9') {
227             // 1- and 2-digit back references are allowed
228             unsigned backrefIndex = ref - '0';
229             if (backrefIndex > reg->numSubpatterns())
230                 continue;
231             if (replacement.length() > i + 2) {
232                 ref = replacement[i + 2];
233                 if (ref >= '0' && ref <= '9') {
234                     backrefIndex = 10 * backrefIndex + ref - '0';
235                     if (backrefIndex > reg->numSubpatterns())
236                         backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
237                     else
238                         advance = 1;
239                 }
240             }
241             if (!backrefIndex)
242                 continue;
243             backrefStart = ovector[2 * backrefIndex];
244             backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
245         } else
246             continue;
247
248         if (i - offset)
249             substitutedReplacement.append(replacement.substring(offset, i - offset));
250         i += 1 + advance;
251         offset = i + 1;
252         if (backrefStart >= 0)
253             substitutedReplacement.append(source.substring(backrefStart, backrefLength));
254     } while ((i = replacement.find('$', i + 1)) != notFound);
255
256     if (replacement.length() - offset)
257         substitutedReplacement.append(replacement.substring(offset));
258
259     return substitutedReplacement.toString();
260 }
261
262 inline String substituteBackreferencesInline(const String& replacement, StringView source, const int* ovector, RegExp* reg)
263 {
264     size_t i = replacement.find('$');
265     if (UNLIKELY(i != notFound))
266         return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
267
268     return replacement;
269 }
270
271 String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
272 {
273     return substituteBackreferencesInline(replacement, source, ovector, reg);
274 }
275
276 struct StringRange {
277     StringRange(int pos, int len)
278         : position(pos)
279         , length(len)
280     {
281     }
282
283     StringRange()
284     {
285     }
286
287     int position;
288     int length;
289 };
290
291 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
292 {
293     if (rangeCount == 1) {
294         int sourceSize = source.length();
295         int position = substringRanges[0].position;
296         int length = substringRanges[0].length;
297         if (position <= 0 && length >= sourceSize)
298             return sourceVal;
299         // We could call String::substringSharingImpl(), but this would result in redundant checks.
300         return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
301     }
302
303     int totalLength = 0;
304     for (int i = 0; i < rangeCount; i++)
305         totalLength += substringRanges[i].length;
306
307     if (!totalLength)
308         return jsEmptyString(exec);
309
310     if (source.is8Bit()) {
311         LChar* buffer;
312         const LChar* sourceData = source.characters8();
313         RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
314         if (!impl)
315             return throwOutOfMemoryError(exec);
316
317         int bufferPos = 0;
318         for (int i = 0; i < rangeCount; i++) {
319             if (int srcLen = substringRanges[i].length) {
320                 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
321                 bufferPos += srcLen;
322             }
323         }
324
325         return jsString(exec, impl.release());
326     }
327
328     UChar* buffer;
329     const UChar* sourceData = source.characters16();
330
331     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
332     if (!impl)
333         return throwOutOfMemoryError(exec);
334
335     int bufferPos = 0;
336     for (int i = 0; i < rangeCount; i++) {
337         if (int srcLen = substringRanges[i].length) {
338             StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
339             bufferPos += srcLen;
340         }
341     }
342
343     return jsString(exec, impl.release());
344 }
345
346 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
347 {
348     if (rangeCount == 1 && separatorCount == 0) {
349         int sourceSize = source.length();
350         int position = substringRanges[0].position;
351         int length = substringRanges[0].length;
352         if (position <= 0 && length >= sourceSize)
353             return sourceVal;
354         // We could call String::substringSharingImpl(), but this would result in redundant checks.
355         return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
356     }
357
358     Checked<int, RecordOverflow> totalLength = 0;
359     bool allSeparators8Bit = true;
360     for (int i = 0; i < rangeCount; i++)
361         totalLength += substringRanges[i].length;
362     for (int i = 0; i < separatorCount; i++) {
363         totalLength += separators[i].length();
364         if (separators[i].length() && !separators[i].is8Bit())
365             allSeparators8Bit = false;
366     }
367     if (totalLength.hasOverflowed())
368         return throwOutOfMemoryError(exec);
369
370     if (!totalLength)
371         return jsEmptyString(exec);
372
373     if (source.is8Bit() && allSeparators8Bit) {
374         LChar* buffer;
375         const LChar* sourceData = source.characters8();
376
377         RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
378         if (!impl)
379             return throwOutOfMemoryError(exec);
380
381         int maxCount = std::max(rangeCount, separatorCount);
382         int bufferPos = 0;
383         for (int i = 0; i < maxCount; i++) {
384             if (i < rangeCount) {
385                 if (int srcLen = substringRanges[i].length) {
386                     StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
387                     bufferPos += srcLen;
388                 }
389             }
390             if (i < separatorCount) {
391                 if (int sepLen = separators[i].length()) {
392                     StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
393                     bufferPos += sepLen;
394                 }
395             }
396         }        
397
398         return jsString(exec, impl.release());
399     }
400
401     UChar* buffer;
402     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
403     if (!impl)
404         return throwOutOfMemoryError(exec);
405
406     int maxCount = std::max(rangeCount, separatorCount);
407     int bufferPos = 0;
408     for (int i = 0; i < maxCount; i++) {
409         if (i < rangeCount) {
410             if (int srcLen = substringRanges[i].length) {
411                 if (source.is8Bit())
412                     StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
413                 else
414                     StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
415                 bufferPos += srcLen;
416             }
417         }
418         if (i < separatorCount) {
419             if (int sepLen = separators[i].length()) {
420                 if (separators[i].is8Bit())
421                     StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
422                 else
423                     StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
424                 bufferPos += sepLen;
425             }
426         }
427     }
428
429     return jsString(exec, impl.release());
430 }
431
432 static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
433 {
434     SuperSamplerScope superSamplerScope(false);
435     
436     size_t lastIndex = 0;
437     unsigned startPosition = 0;
438
439     Vector<StringRange, 16> sourceRanges;
440     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
441     unsigned sourceLen = source.length();
442
443     while (true) {
444         MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition);
445         if (!result)
446             break;
447
448         if (lastIndex < result.start)
449             sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
450
451         lastIndex = result.end;
452         startPosition = lastIndex;
453
454         // special case of empty match
455         if (result.empty()) {
456             startPosition++;
457             if (startPosition > sourceLen)
458                 break;
459         }
460     }
461
462     if (!lastIndex)
463         return JSValue::encode(string);
464
465     if (static_cast<unsigned>(lastIndex) < sourceLen)
466         sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
467
468     return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
469 }
470
471 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
472     VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
473     CallType callType, String& replacementString, JSValue replaceValue)
474 {
475     const String& source = string->value(exec);
476     unsigned sourceLen = source.length();
477     if (exec->hadException())
478         return JSValue::encode(jsUndefined());
479     RegExpObject* regExpObject = asRegExpObject(searchValue);
480     RegExp* regExp = regExpObject->regExp();
481     bool global = regExp->global();
482
483     if (global) {
484         // ES5.1 15.5.4.10 step 8.a.
485         regExpObject->setLastIndex(exec, 0);
486         if (exec->hadException())
487             return JSValue::encode(jsUndefined());
488
489         if (callType == CallType::None && !replacementString.length())
490             return removeUsingRegExpSearch(vm, exec, string, source, regExp);
491     }
492
493     // FIXME: This is wrong because we may be called directly from the FTL.
494     // https://bugs.webkit.org/show_bug.cgi?id=154874
495     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
496
497     size_t lastIndex = 0;
498     unsigned startPosition = 0;
499
500     Vector<StringRange, 16> sourceRanges;
501     Vector<String, 16> replacements;
502
503     // This is either a loop (if global is set) or a one-way (if not).
504     if (global && callType == CallType::JS) {
505         // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
506         int argCount = regExp->numSubpatterns() + 1 + 2;
507         JSFunction* func = jsCast<JSFunction*>(replaceValue);
508         CachedCall cachedCall(exec, func, argCount);
509         if (exec->hadException())
510             return JSValue::encode(jsUndefined());
511         if (source.is8Bit()) {
512             while (true) {
513                 int* ovector;
514                 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
515                 if (!result)
516                     break;
517
518                 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
519
520                 unsigned i = 0;
521                 for (; i < regExp->numSubpatterns() + 1; ++i) {
522                     int matchStart = ovector[i * 2];
523                     int matchLen = ovector[i * 2 + 1] - matchStart;
524
525                     if (matchStart < 0)
526                         cachedCall.setArgument(i, jsUndefined());
527                     else
528                         cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
529                 }
530
531                 cachedCall.setArgument(i++, jsNumber(result.start));
532                 cachedCall.setArgument(i++, string);
533
534                 cachedCall.setThis(jsUndefined());
535                 JSValue jsResult = cachedCall.call();
536                 replacements.append(jsResult.toString(exec)->value(exec));
537                 if (exec->hadException())
538                     return JSValue::encode(jsUndefined());
539
540                 lastIndex = result.end;
541                 startPosition = lastIndex;
542
543                 // special case of empty match
544                 if (result.empty()) {
545                     startPosition++;
546                     if (startPosition > sourceLen)
547                         break;
548                 }
549             }
550         } else {
551             while (true) {
552                 int* ovector;
553                 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
554                 if (!result)
555                     break;
556
557                 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
558
559                 unsigned i = 0;
560                 for (; i < regExp->numSubpatterns() + 1; ++i) {
561                     int matchStart = ovector[i * 2];
562                     int matchLen = ovector[i * 2 + 1] - matchStart;
563
564                     if (matchStart < 0)
565                         cachedCall.setArgument(i, jsUndefined());
566                     else
567                         cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
568                 }
569
570                 cachedCall.setArgument(i++, jsNumber(result.start));
571                 cachedCall.setArgument(i++, string);
572
573                 cachedCall.setThis(jsUndefined());
574                 JSValue jsResult = cachedCall.call();
575                 replacements.append(jsResult.toString(exec)->value(exec));
576                 if (exec->hadException())
577                     return JSValue::encode(jsUndefined());
578
579                 lastIndex = result.end;
580                 startPosition = lastIndex;
581
582                 // special case of empty match
583                 if (result.empty()) {
584                     startPosition++;
585                     if (startPosition > sourceLen)
586                         break;
587                 }
588             }
589         }
590     } else {
591         do {
592             int* ovector;
593             MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
594             if (!result)
595                 break;
596
597             if (callType != CallType::None) {
598                 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
599
600                 MarkedArgumentBuffer args;
601
602                 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
603                     int matchStart = ovector[i * 2];
604                     int matchLen = ovector[i * 2 + 1] - matchStart;
605
606                     if (matchStart < 0)
607                         args.append(jsUndefined());
608                     else
609                         args.append(jsSubstring(exec, source, matchStart, matchLen));
610                 }
611
612                 args.append(jsNumber(result.start));
613                 args.append(string);
614
615                 replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
616                 if (exec->hadException())
617                     return JSValue::encode(jsUndefined());
618             } else {
619                 int replLen = replacementString.length();
620                 if (lastIndex < result.start || replLen) {
621                     sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
622
623                     if (replLen)
624                         replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
625                     else
626                         replacements.append(String());
627                 }
628             }
629
630             lastIndex = result.end;
631             startPosition = lastIndex;
632
633             // special case of empty match
634             if (result.empty()) {
635                 startPosition++;
636                 if (startPosition > sourceLen)
637                     break;
638             }
639         } while (global);
640     }
641
642     if (!lastIndex && replacements.isEmpty())
643         return JSValue::encode(string);
644
645     if (static_cast<unsigned>(lastIndex) < sourceLen)
646         sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
647
648     return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
649 }
650
651 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
652     ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
653 {
654     VM& vm = exec->vm();
655     NativeCallFrameTracer tracer(&vm, exec);
656     
657     RegExp* regExp = searchValue->regExp();
658     if (regExp->global()) {
659         // ES5.1 15.5.4.10 step 8.a.
660         searchValue->setLastIndex(exec, 0);
661         if (exec->hadException())
662             return JSValue::encode(jsUndefined());
663         return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
664     }
665
666     CallData callData;
667     String replacementString = emptyString();
668     return replaceUsingRegExpSearch(
669         vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
670 }
671
672 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
673     ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
674 {
675     VM& vm = exec->vm();
676     NativeCallFrameTracer tracer(&vm, exec);
677     
678     CallData callData;
679     String replacementString = replaceString->value(exec);
680     return replaceUsingRegExpSearch(
681         vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
682 }
683
684 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
685 {
686     String replacementString;
687     CallData callData;
688     CallType callType = getCallData(replaceValue, callData);
689     if (callType == CallType::None) {
690         replacementString = replaceValue.toString(exec)->value(exec);
691         if (exec->hadException())
692             return JSValue::encode(jsUndefined());
693     }
694
695     return replaceUsingRegExpSearch(
696         vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
697 }
698
699 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM&, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
700 {
701     const String& string = jsString->value(exec);
702     String searchString = searchValue.toString(exec)->value(exec);
703     if (exec->hadException())
704         return JSValue::encode(jsUndefined());
705
706     size_t matchStart = string.find(searchString);
707
708     if (matchStart == notFound)
709         return JSValue::encode(jsString);
710
711     CallData callData;
712     CallType callType = getCallData(replaceValue, callData);
713     if (callType != CallType::None) {
714         MarkedArgumentBuffer args;
715         args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
716         args.append(jsNumber(matchStart));
717         args.append(jsString);
718         replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
719         if (exec->hadException())
720             return JSValue::encode(jsUndefined());
721     }
722
723     String replaceString = replaceValue.toString(exec)->value(exec);
724     if (exec->hadException())
725         return JSValue::encode(jsUndefined());
726
727     StringImpl* stringImpl = string.impl();
728     String leftPart(StringImpl::createSubstringSharingImpl(stringImpl, 0, matchStart));
729
730     size_t matchEnd = matchStart + searchString.impl()->length();
731     int ovector[2] = { static_cast<int>(matchStart),  static_cast<int>(matchEnd)};
732     String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
733
734     size_t leftLength = stringImpl->length() - matchEnd;
735     String rightPart(StringImpl::createSubstringSharingImpl(stringImpl, matchEnd, leftLength));
736     return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
737 }
738
739 static inline bool checkObjectCoercible(JSValue thisValue)
740 {
741     if (thisValue.isString())
742         return true;
743
744     if (thisValue.isUndefinedOrNull())
745         return false;
746
747     if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isEnvironmentRecord())
748         return false;
749
750     return true;
751 }
752
753 template <typename CharacterType>
754 static inline JSValue repeatCharacter(ExecState* exec, CharacterType character, unsigned repeatCount)
755 {
756     CharacterType* buffer = nullptr;
757     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
758     if (!impl)
759         return throwOutOfMemoryError(exec);
760
761     std::fill_n(buffer, repeatCount, character);
762
763     return jsString(exec, impl.release());
764 }
765
766 template <typename CharacterType>
767 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
768 {
769     CharacterType* buffer = nullptr;
770     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
771     if (!impl)
772         return throwOutOfMemoryError(&exec), nullptr;
773
774     std::fill_n(buffer, repeatCount, character);
775
776     return jsString(&exec, impl.release());
777 }
778
779 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
780 {
781     // For a string which length is single, instead of creating ropes,
782     // allocating a sequential buffer and fill with the repeated string for efficiency.
783     ASSERT(exec->argumentCount() == 2);
784
785     ASSERT(exec->uncheckedArgument(0).isString());
786     JSString* string = jsCast<JSString*>(exec->uncheckedArgument(0));
787     ASSERT(string->length() == 1);
788
789     if (!exec->uncheckedArgument(1).isInt32())
790         return JSValue::encode(jsNull());
791
792     int32_t repeatCount = exec->uncheckedArgument(1).asInt32();
793     UChar character = string->view(exec)[0];
794     if (!(character & ~0xff))
795         return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
796     return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
797 }
798
799 static inline bool repeatStringPattern(ExecState& exec, unsigned maxLength, JSString* string, JSRopeString::RopeBuilder& ropeBuilder)
800 {
801     unsigned repeatCount = maxLength / string->length();
802     unsigned remainingCharacters = maxLength - repeatCount * string->length();
803     for (unsigned i = 0; i < repeatCount; ++i) {
804         if (!ropeBuilder.append(string)) {
805             throwOutOfMemoryError(&exec);
806             return false;
807         }
808     }
809     if (remainingCharacters) {
810         JSString* substr = jsSubstring(&exec, string, 0, remainingCharacters);
811         if (!substr || !ropeBuilder.append(substr)) {
812             throwOutOfMemoryError(&exec);
813             return false;
814         }
815     }
816     return true;
817 }
818
819 enum class StringPaddingLocation { Start, End };
820
821 static EncodedJSValue padString(ExecState& exec, StringPaddingLocation paddingLocation)
822 {
823     JSValue thisValue = exec.thisValue();
824     if (!thisValue.requireObjectCoercible(&exec))
825         return JSValue::encode(jsUndefined());
826     JSString* thisString = thisValue.toString(&exec);
827     if (exec.hadException())
828         return JSValue::encode(jsUndefined());
829
830     double maxLengthAsDouble = exec.argument(0).toLength(&exec);
831     if (exec.hadException())
832         return JSValue::encode(jsUndefined());
833     ASSERT(maxLengthAsDouble >= 0.0);
834     ASSERT(maxLengthAsDouble == std::trunc(maxLengthAsDouble));
835
836     if (maxLengthAsDouble <= thisString->length())
837         return JSValue::encode(thisString);
838
839     if (maxLengthAsDouble > JSString::MaxLength)
840         return JSValue::encode(throwOutOfMemoryError(&exec));
841
842     unsigned maxLength = static_cast<unsigned>(maxLengthAsDouble);
843
844     JSValue fillString = exec.argument(1);
845     JSString* filler = nullptr;
846     if (!fillString.isUndefined()) {
847         filler = fillString.toString(&exec);
848         if (!filler)
849             return JSValue::encode(jsUndefined());
850     }
851
852     unsigned fillLength = static_cast<unsigned>(maxLength) - thisString->length();
853
854     JSRopeString::RopeBuilder ropeBuilder(exec.vm());
855     if (paddingLocation == StringPaddingLocation::End) {
856         if (!ropeBuilder.append(thisString))
857             return JSValue::encode(throwOutOfMemoryError(&exec));
858     }
859
860     if (!filler || filler->length() <= 1) {
861         UChar character = filler && filler->length() ? filler->view(&exec)[0] : ' ';
862         if (!(character & ~0xff))
863             filler = repeatCharacter(exec, static_cast<LChar>(character), fillLength);
864         else
865             filler = repeatCharacter(exec, character, fillLength);
866         if (!filler || !ropeBuilder.append(filler))
867             return JSValue::encode(throwOutOfMemoryError(&exec));
868         ASSERT(filler->length() == fillLength);
869     } else {
870         if (!repeatStringPattern(exec, fillLength, filler, ropeBuilder))
871             return JSValue::encode(throwOutOfMemoryError(&exec));
872     }
873
874     if (paddingLocation == StringPaddingLocation::Start) {
875         if (!ropeBuilder.append(thisString))
876             return JSValue::encode(throwOutOfMemoryError(&exec));
877     }
878     ASSERT(!exec.hadException());
879     return JSValue::encode(ropeBuilder.release());
880 }
881
882 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadEnd(ExecState* exec)
883 {
884     return padString(*exec, StringPaddingLocation::End);
885 }
886
887 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState* exec)
888 {
889     return padString(*exec, StringPaddingLocation::Start);
890 }
891
892 ALWAYS_INLINE EncodedJSValue replace(
893     VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
894 {
895     if (searchValue.inherits(RegExpObject::info()))
896         return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
897     return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
898 }
899
900 ALWAYS_INLINE EncodedJSValue replace(
901     VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
902 {
903     if (!checkObjectCoercible(thisValue))
904         return throwVMTypeError(exec);
905     JSString* string = thisValue.toString(exec);
906     if (exec->hadException())
907         return JSValue::encode(jsUndefined());
908     return replace(vm, exec, string, searchValue, replaceValue);
909 }
910
911 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
912 {
913     return replace(exec->vm(), exec, exec->thisValue(), exec->argument(0), exec->argument(1));
914 }
915
916 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
917     ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
918     EncodedJSValue replaceValue)
919 {
920     VM& vm = exec->vm();
921     NativeCallFrameTracer tracer(&vm, exec);
922     
923     return replace(
924         vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
925         JSValue::decode(replaceValue));
926 }
927
928 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
929 {
930     JSValue thisValue = exec->thisValue();
931     // Also used for valueOf.
932
933     if (thisValue.isString())
934         return JSValue::encode(thisValue);
935
936     if (thisValue.inherits(StringObject::info()))
937         return JSValue::encode(asStringObject(thisValue)->internalValue());
938
939     return throwVMTypeError(exec);
940 }
941
942 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
943 {
944     JSValue thisValue = exec->thisValue();
945     if (!checkObjectCoercible(thisValue))
946         return throwVMTypeError(exec);
947     JSString::SafeView string = thisValue.toString(exec)->view(exec);
948     JSValue a0 = exec->argument(0);
949     if (a0.isUInt32()) {
950         uint32_t i = a0.asUInt32();
951         if (i < string.length())
952             return JSValue::encode(jsSingleCharacterString(exec, string[i]));
953         return JSValue::encode(jsEmptyString(exec));
954     }
955     double dpos = a0.toInteger(exec);
956     if (dpos >= 0 && dpos < string.length())
957         return JSValue::encode(jsSingleCharacterString(exec, string[static_cast<unsigned>(dpos)]));
958     return JSValue::encode(jsEmptyString(exec));
959 }
960
961 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
962 {
963     JSValue thisValue = exec->thisValue();
964     if (!checkObjectCoercible(thisValue))
965         return throwVMTypeError(exec);
966     JSString::SafeView string = thisValue.toString(exec)->view(exec);
967     JSValue a0 = exec->argument(0);
968     if (a0.isUInt32()) {
969         uint32_t i = a0.asUInt32();
970         if (i < string.length())
971             return JSValue::encode(jsNumber(string[i]));
972         return JSValue::encode(jsNaN());
973     }
974     double dpos = a0.toInteger(exec);
975     if (dpos >= 0 && dpos < string.length())
976         return JSValue::encode(jsNumber(string[static_cast<int>(dpos)]));
977     return JSValue::encode(jsNaN());
978 }
979
980 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
981 {
982     RELEASE_ASSERT(position < length);
983     if (string.is8Bit())
984         return string.characters8()[position];
985     UChar32 character;
986     U16_NEXT(string.characters16(), position, length, character);
987     return character;
988 }
989
990 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
991 {
992     JSValue thisValue = exec->thisValue();
993     if (!checkObjectCoercible(thisValue))
994         return throwVMTypeError(exec);
995
996     String string = thisValue.toWTFString(exec);
997     unsigned length = string.length();
998
999     JSValue argument0 = exec->argument(0);
1000     if (argument0.isUInt32()) {
1001         unsigned position = argument0.asUInt32();
1002         if (position < length)
1003             return JSValue::encode(jsNumber(codePointAt(string, position, length)));
1004         return JSValue::encode(jsUndefined());
1005     }
1006
1007     if (UNLIKELY(exec->hadException()))
1008         return JSValue::encode(jsUndefined());
1009
1010     double doublePosition = argument0.toInteger(exec);
1011     if (doublePosition >= 0 && doublePosition < length)
1012         return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
1013     return JSValue::encode(jsUndefined());
1014 }
1015
1016 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
1017 {
1018     JSValue thisValue = exec->thisValue();
1019     if (thisValue.isString() && exec->argumentCount() == 1)
1020         return JSValue::encode(jsString(exec, asString(thisValue), exec->uncheckedArgument(0).toString(exec)));
1021
1022     if (!checkObjectCoercible(thisValue))
1023         return throwVMTypeError(exec);
1024     return JSValue::encode(jsStringFromArguments(exec, thisValue));
1025 }
1026
1027 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1028 {
1029     JSValue thisValue = exec->thisValue();
1030     if (!checkObjectCoercible(thisValue))
1031         return throwVMTypeError(exec);
1032
1033     JSValue a0 = exec->argument(0);
1034     JSValue a1 = exec->argument(1);
1035
1036     JSString* thisJSString = thisValue.toString(exec);
1037     JSString* otherJSString = a0.toString(exec);
1038
1039     unsigned pos = 0;
1040     if (!a1.isUndefined()) {
1041         int len = thisJSString->length();
1042         RELEASE_ASSERT(len >= 0);
1043         if (a1.isUInt32())
1044             pos = std::min<uint32_t>(a1.asUInt32(), len);
1045         else {
1046             double dpos = a1.toInteger(exec);
1047             if (dpos < 0)
1048                 dpos = 0;
1049             else if (dpos > len)
1050                 dpos = len;
1051             pos = static_cast<unsigned>(dpos);
1052         }
1053     }
1054
1055     if (thisJSString->length() < otherJSString->length() + pos)
1056         return JSValue::encode(jsNumber(-1));
1057
1058     size_t result = thisJSString->view(exec).get().find(otherJSString->view(exec).get(), pos);
1059     if (result == notFound)
1060         return JSValue::encode(jsNumber(-1));
1061     return JSValue::encode(jsNumber(result));
1062 }
1063
1064 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1065 {
1066     JSValue thisValue = exec->thisValue();
1067     if (!checkObjectCoercible(thisValue))
1068         return throwVMTypeError(exec);
1069
1070     JSValue a0 = exec->argument(0);
1071     JSValue a1 = exec->argument(1);
1072
1073     JSString* thisJSString = thisValue.toString(exec);
1074     unsigned len = thisJSString->length();
1075     JSString* otherJSString = a0.toString(exec);
1076
1077     double dpos = a1.toIntegerPreserveNaN(exec);
1078     unsigned startPosition;
1079     if (dpos < 0)
1080         startPosition = 0;
1081     else if (!(dpos <= len)) // true for NaN
1082         startPosition = len;
1083     else
1084         startPosition = static_cast<unsigned>(dpos);
1085
1086     if (len < otherJSString->length())
1087         return JSValue::encode(jsNumber(-1));
1088
1089     String thisString = thisJSString->value(exec);
1090     String otherString = otherJSString->value(exec);
1091     size_t result;
1092     if (!startPosition)
1093         result = thisString.startsWith(otherString) ? 0 : notFound;
1094     else
1095         result = thisString.reverseFind(otherString, startPosition);
1096     if (result == notFound)
1097         return JSValue::encode(jsNumber(-1));
1098     return JSValue::encode(jsNumber(result));
1099 }
1100
1101 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1102 {
1103     JSValue thisValue = exec->thisValue();
1104     if (!checkObjectCoercible(thisValue))
1105         return throwVMTypeError(exec);
1106     String s = thisValue.toString(exec)->value(exec);
1107     if (exec->hadException())
1108         return JSValue::encode(jsUndefined());
1109
1110     int len = s.length();
1111     RELEASE_ASSERT(len >= 0);
1112
1113     JSValue a0 = exec->argument(0);
1114     JSValue a1 = exec->argument(1);
1115
1116     // The arg processing is very much like ArrayProtoFunc::Slice
1117     double start = a0.toInteger(exec);
1118     double end = a1.isUndefined() ? len : a1.toInteger(exec);
1119     double from = start < 0 ? len + start : start;
1120     double to = end < 0 ? len + end : end;
1121     if (to > from && to > 0 && from < len) {
1122         if (from < 0)
1123             from = 0;
1124         if (to > len)
1125             to = len;
1126         return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1127     }
1128
1129     return JSValue::encode(jsEmptyString(exec));
1130 }
1131
1132 // Return true in case of early return (resultLength got to limitLength).
1133 template<typename CharacterType>
1134 static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, JSValue originalValue, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength)
1135 {
1136     // 12. Let q = p.
1137     size_t matchPosition;
1138     const CharacterType* characters = string->characters<CharacterType>();
1139     // 13. Repeat, while q != s
1140     //   a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1141     //   b. If z is failure, then let q = q+1.
1142     //   c. Else, z is not failure
1143     while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1144         // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1145         //    through q (exclusive).
1146         // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1147         //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1148         result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1149         // 3. Increment lengthA by 1.
1150         // 4. If lengthA == lim, return A.
1151         if (++resultLength == limitLength)
1152             return true;
1153
1154         // 5. Let p = e.
1155         // 8. Let q = p.
1156         position = matchPosition + 1;
1157     }
1158     return false;
1159 }
1160
1161 // ES 21.1.3.17 String.prototype.split(separator, limit)
1162 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1163 {
1164     JSValue thisValue = exec->thisValue();
1165     ASSERT(checkObjectCoercible(thisValue));
1166
1167     // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1168     // 7. Let s be the number of characters in S.
1169     String input = thisValue.toString(exec)->value(exec);
1170     if (exec->hadException())
1171         return JSValue::encode(jsUndefined());
1172     ASSERT(!input.isNull());
1173
1174     // 4. Let A be a new array created as if by the expression new Array()
1175     //    where Array is the standard built-in constructor with that name.
1176     JSArray* result = constructEmptyArray(exec, 0);
1177
1178     // 5. Let lengthA be 0.
1179     unsigned resultLength = 0;
1180
1181     // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1182     JSValue limitValue = exec->uncheckedArgument(1);
1183     unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1184
1185     // 8. Let p = 0.
1186     size_t position = 0;
1187
1188     // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1189     //    otherwise let R = ToString(separator).
1190     JSValue separatorValue = exec->uncheckedArgument(0);
1191     { // FIXME: Keeping this indentation here to minimize the diff. Will unindent and remove this later.
1192         String separator = separatorValue.toString(exec)->value(exec);
1193         if (exec->hadException())
1194             return JSValue::encode(jsUndefined());
1195
1196         // 10. If lim == 0, return A.
1197         if (!limit)
1198             return JSValue::encode(result);
1199
1200         // 11. If separator is undefined, then
1201         if (separatorValue.isUndefined()) {
1202             // a.  Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1203             result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1204             // b.  Return A.
1205             return JSValue::encode(result);
1206         }
1207
1208         // 12. If s == 0, then
1209         if (input.isEmpty()) {
1210             // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1211             // b. If z is not false, return A.
1212             // c. Call CreateDataProperty(A, "0", S).
1213             // d. Return A.
1214             if (!separator.isEmpty())
1215                 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1216             return JSValue::encode(result);
1217         }
1218
1219         // Optimized case for splitting on the empty string.
1220         if (separator.isEmpty()) {
1221             limit = std::min(limit, input.length());
1222             // Zero limt/input length handled in steps 9/11 respectively, above.
1223             ASSERT(limit);
1224
1225             do {
1226                 result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1227             } while (++position < limit);
1228
1229             return JSValue::encode(result);
1230         }
1231
1232         // 3 cases:
1233         // -separator length == 1, 8 bits
1234         // -separator length == 1, 16 bits
1235         // -separator length > 1
1236         StringImpl* stringImpl = input.impl();
1237         StringImpl* separatorImpl = separator.impl();
1238         size_t separatorLength = separatorImpl->length();
1239
1240         if (separatorLength == 1) {
1241             UChar separatorCharacter;
1242             if (separatorImpl->is8Bit())
1243                 separatorCharacter = separatorImpl->characters8()[0];
1244             else
1245                 separatorCharacter = separatorImpl->characters16()[0];
1246
1247             if (stringImpl->is8Bit()) {
1248                 if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1249                     return JSValue::encode(result);
1250             } else {
1251                 if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1252                     return JSValue::encode(result);
1253             }
1254         } else {
1255             // 13. Let q = p.
1256             size_t matchPosition;
1257             // 14. Repeat, while q != s
1258             //   a. let e be SplitMatch(S, q, R).
1259             //   b. If e is failure, then let q = q+1.
1260             //   c. Else, e is an integer index <= s.
1261             while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1262                 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1263                 //    through q (exclusive).
1264                 // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1265                 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1266                 // 3. Increment lengthA by 1.
1267                 // 4. If lengthA == lim, return A.
1268                 if (++resultLength == limit)
1269                     return JSValue::encode(result);
1270
1271                 // 5. Let p = e.
1272                 // 6. Let q = p.
1273                 position = matchPosition + separator.length();
1274             }
1275         }
1276     } // FIXME: Keeping this indentation here to minimize the diff. Will unindent and remove this later.
1277
1278     // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1279     //     through s (exclusive).
1280     // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1281     result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1282
1283     // 17. Return A.
1284     return JSValue::encode(result);
1285 }
1286
1287 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1288 {
1289     JSValue thisValue = exec->thisValue();
1290     if (!checkObjectCoercible(thisValue))
1291         return throwVMTypeError(exec);
1292     unsigned len;
1293     JSString* jsString = 0;
1294     String uString;
1295     if (thisValue.isString()) {
1296         jsString = jsCast<JSString*>(thisValue.asCell());
1297         len = jsString->length();
1298     } else {
1299         uString = thisValue.toString(exec)->value(exec);
1300         if (exec->hadException())
1301             return JSValue::encode(jsUndefined());
1302         len = uString.length();
1303     }
1304
1305     JSValue a0 = exec->argument(0);
1306     JSValue a1 = exec->argument(1);
1307
1308     double start = a0.toInteger(exec);
1309     double length = a1.isUndefined() ? len : a1.toInteger(exec);
1310     if (start >= len || length <= 0)
1311         return JSValue::encode(jsEmptyString(exec));
1312     if (start < 0) {
1313         start += len;
1314         if (start < 0)
1315             start = 0;
1316     }
1317     if (start + length > len)
1318         length = len - start;
1319     unsigned substringStart = static_cast<unsigned>(start);
1320     unsigned substringLength = static_cast<unsigned>(length);
1321     if (jsString)
1322         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1323     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1324 }
1325
1326 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1327 {
1328     // @substrInternal should not have any observable side effects (e.g. it should not call
1329     // GetMethod(..., @@toPrimitive) on the thisValue).
1330
1331     // It is ok to use the default stringProtoFuncSubstr as the implementation of
1332     // @substrInternal because @substrInternal will only be called by builtins, which will
1333     // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1334     // will not need to call toString() on the thisValue, and there will be no observable
1335     // side-effects.
1336 #if !ASSERT_DISABLED
1337     JSValue thisValue = exec->thisValue();
1338     ASSERT(thisValue.isString());
1339 #endif
1340     return stringProtoFuncSubstr(exec);
1341 }
1342
1343 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1344 {
1345     JSValue thisValue = exec->thisValue();
1346     if (!checkObjectCoercible(thisValue))
1347         return throwVMTypeError(exec);
1348
1349     JSString* jsString = thisValue.toString(exec);
1350     if (exec->hadException())
1351         return JSValue::encode(jsUndefined());
1352
1353     JSValue a0 = exec->argument(0);
1354     JSValue a1 = exec->argument(1);
1355     int len = jsString->length();
1356     RELEASE_ASSERT(len >= 0);
1357
1358     double start = a0.toNumber(exec);
1359     double end;
1360     if (!(start >= 0)) // check for negative values or NaN
1361         start = 0;
1362     else if (start > len)
1363         start = len;
1364     if (a1.isUndefined())
1365         end = len;
1366     else { 
1367         end = a1.toNumber(exec);
1368         if (!(end >= 0)) // check for negative values or NaN
1369             end = 0;
1370         else if (end > len)
1371             end = len;
1372     }
1373     if (start > end) {
1374         double temp = end;
1375         end = start;
1376         start = temp;
1377     }
1378     unsigned substringStart = static_cast<unsigned>(start);
1379     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1380     return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1381 }
1382
1383 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1384 {
1385     JSValue thisValue = exec->thisValue();
1386     if (!checkObjectCoercible(thisValue))
1387         return throwVMTypeError(exec);
1388     JSString* sVal = thisValue.toString(exec);
1389     const String& s = sVal->value(exec);
1390     String lowercasedString = s.convertToLowercaseWithoutLocale();
1391     if (lowercasedString.impl() == s.impl())
1392         return JSValue::encode(sVal);
1393     return JSValue::encode(jsString(exec, lowercasedString));
1394 }
1395
1396 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1397 {
1398     JSValue thisValue = exec->thisValue();
1399     if (!checkObjectCoercible(thisValue))
1400         return throwVMTypeError(exec);
1401     JSString* sVal = thisValue.toString(exec);
1402     const String& s = sVal->value(exec);
1403     String uppercasedString = s.convertToUppercaseWithoutLocale();
1404     if (uppercasedString.impl() == s.impl())
1405         return JSValue::encode(sVal);
1406     return JSValue::encode(jsString(exec, uppercasedString));
1407 }
1408
1409 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1410 {
1411     JSValue thisValue = exec->thisValue();
1412     if (!checkObjectCoercible(thisValue))
1413         return throwVMTypeError(exec);
1414     String s = thisValue.toString(exec)->value(exec);
1415     if (exec->hadException())
1416         return JSValue::encode(jsUndefined());
1417
1418     JSValue a0 = exec->argument(0);
1419     String str = a0.toString(exec)->value(exec);
1420     if (exec->hadException())
1421         return JSValue::encode(jsUndefined());
1422     return JSValue::encode(jsNumber(Collator().collate(s, str)));
1423 }
1424
1425 #if ENABLE(INTL)
1426 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1427 {
1428     // 1. Let O be RequireObjectCoercible(this value).
1429     JSValue thisValue = state->thisValue();
1430     if (!checkObjectCoercible(thisValue))
1431         return throwVMTypeError(state);
1432
1433     // 2. Let S be ToString(O).
1434     JSString* sVal = thisValue.toString(state);
1435     const String& s = sVal->value(state);
1436
1437     // 3. ReturnIfAbrupt(S).
1438     if (state->hadException())
1439         return JSValue::encode(jsUndefined());
1440
1441     // Optimization for empty strings.
1442     if (s.isEmpty())
1443         return JSValue::encode(sVal);
1444
1445     // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1446     Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1447
1448     // 5. ReturnIfAbrupt(requestedLocales).
1449     if (state->hadException())
1450         return JSValue::encode(jsUndefined());
1451
1452     // 6. Let len be the number of elements in requestedLocales.
1453     size_t len = requestedLocales.size();
1454
1455     // 7. If len > 0, then
1456     // a. Let requestedLocale be the first element of requestedLocales.
1457     // 8. Else
1458     // a. Let requestedLocale be DefaultLocale().
1459     String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1460
1461     // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1462     String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1463
1464     // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1465     // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1466     const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1467
1468     // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1469     String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1470
1471     // 12. If locale is undefined, let locale be "und".
1472     if (locale.isNull())
1473         locale = ASCIILiteral("und");
1474
1475     CString utf8LocaleBuffer = locale.utf8();
1476     const StringView view(s);
1477     const int32_t viewLength = view.length();
1478
1479     // Delegate the following steps to icu u_strToLower or u_strToUpper.
1480     // 13. Let cpList be a List containing in order the code points of S as defined in ES2015, 6.1.4, starting at the first element of S.
1481     // 14. For each code point c in cpList, if the Unicode Character Database provides a lower(/upper) case equivalent of c that is either language insensitive or for the language locale, then replace c in cpList with that/those equivalent code point(s).
1482     // 15. Let cuList be a new List.
1483     // 16. For each code point c in cpList, in order, append to cuList the elements of the UTF-16 Encoding (defined in ES2015, 6.1.4) of c.
1484     // 17. Let L be a String whose elements are, in order, the elements of cuList.
1485
1486     // Most strings lower/upper case will be the same size as original, so try that first.
1487     UErrorCode error(U_ZERO_ERROR);
1488     Vector<UChar> buffer(viewLength);
1489     String lower;
1490     const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1491     if (U_SUCCESS(error))
1492         lower = String(buffer.data(), resultLength);
1493     else if (error == U_BUFFER_OVERFLOW_ERROR) {
1494         // Converted case needs more space than original. Try again.
1495         UErrorCode error(U_ZERO_ERROR);
1496         Vector<UChar> buffer(resultLength);
1497         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1498         if (U_FAILURE(error))
1499             return throwVMTypeError(state, u_errorName(error));
1500         lower = String(buffer.data(), resultLength);
1501     } else
1502         return throwVMTypeError(state, u_errorName(error));
1503
1504     // 18. Return L.
1505     return JSValue::encode(jsString(state, lower));
1506 }
1507
1508 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1509 {
1510     // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1511     // http://ecma-international.org/publications/standards/Ecma-402.htm
1512     return toLocaleCase(state, u_strToLower);
1513 }
1514
1515 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1516 {
1517     // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1518     // http://ecma-international.org/publications/standards/Ecma-402.htm
1519     // This function interprets a string value as a sequence of code points, as described in ES2015, 6.1.4. This function behaves in exactly the same way as String.prototype.toLocaleLowerCase, except that characters are mapped to their uppercase equivalents as specified in the Unicode character database.
1520     return toLocaleCase(state, u_strToUpper);
1521 }
1522 #endif // ENABLE(INTL)
1523
1524 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1525 {
1526     JSValue thisValue = exec->thisValue();
1527     if (!checkObjectCoercible(thisValue))
1528         return throwVMTypeError(exec);
1529     String s = thisValue.toString(exec)->value(exec);
1530     if (exec->hadException())
1531         return JSValue::encode(jsUndefined());
1532     return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1533 }
1534
1535 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1536 {
1537     JSValue thisValue = exec->thisValue();
1538     if (!checkObjectCoercible(thisValue))
1539         return throwVMTypeError(exec);
1540     String s = thisValue.toString(exec)->value(exec);
1541     if (exec->hadException())
1542         return JSValue::encode(jsUndefined());
1543     return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1544 }
1545
1546 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1547 {
1548     JSValue thisValue = exec->thisValue();
1549     if (!checkObjectCoercible(thisValue))
1550         return throwVMTypeError(exec);
1551     String s = thisValue.toString(exec)->value(exec);
1552     if (exec->hadException())
1553         return JSValue::encode(jsUndefined());
1554     return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1555 }
1556
1557 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1558 {
1559     JSValue thisValue = exec->thisValue();
1560     if (!checkObjectCoercible(thisValue))
1561         return throwVMTypeError(exec);
1562     String s = thisValue.toString(exec)->value(exec);
1563     if (exec->hadException())
1564         return JSValue::encode(jsUndefined());
1565     return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1566 }
1567
1568 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1569 {
1570     JSValue thisValue = exec->thisValue();
1571     if (!checkObjectCoercible(thisValue))
1572         return throwVMTypeError(exec);
1573     String s = thisValue.toString(exec)->value(exec);
1574     if (exec->hadException())
1575         return JSValue::encode(jsUndefined());
1576     return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1577 }
1578
1579 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1580 {
1581     JSValue thisValue = exec->thisValue();
1582     if (!checkObjectCoercible(thisValue))
1583         return throwVMTypeError(exec);
1584     String s = thisValue.toString(exec)->value(exec);
1585     if (exec->hadException())
1586         return JSValue::encode(jsUndefined());
1587     return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1588 }
1589
1590 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1591 {
1592     JSValue thisValue = exec->thisValue();
1593     if (!checkObjectCoercible(thisValue))
1594         return throwVMTypeError(exec);
1595     String s = thisValue.toString(exec)->value(exec);
1596     if (exec->hadException())
1597         return JSValue::encode(jsUndefined());
1598     return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1599 }
1600
1601 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1602 {
1603     JSValue thisValue = exec->thisValue();
1604     if (!checkObjectCoercible(thisValue))
1605         return throwVMTypeError(exec);
1606     String s = thisValue.toString(exec)->value(exec);
1607     if (exec->hadException())
1608         return JSValue::encode(jsUndefined());
1609     return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1610 }
1611
1612 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1613 {
1614     JSValue thisValue = exec->thisValue();
1615     if (!checkObjectCoercible(thisValue))
1616         return throwVMTypeError(exec);
1617     String s = thisValue.toString(exec)->value(exec);
1618     if (exec->hadException())
1619         return JSValue::encode(jsUndefined());
1620     return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1621 }
1622
1623 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1624 {
1625     JSValue thisValue = exec->thisValue();
1626     if (!checkObjectCoercible(thisValue))
1627         return throwVMTypeError(exec);
1628     String s = thisValue.toString(exec)->value(exec);
1629     if (exec->hadException())
1630         return JSValue::encode(jsUndefined());
1631
1632     JSValue a0 = exec->argument(0);
1633     String color = a0.toWTFString(exec);
1634     color.replaceWithLiteral('"', "&quot;");
1635
1636     return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1637 }
1638
1639 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1640 {
1641     JSValue thisValue = exec->thisValue();
1642     if (!checkObjectCoercible(thisValue))
1643         return throwVMTypeError(exec);
1644     String s = thisValue.toString(exec)->value(exec);
1645     if (exec->hadException())
1646         return JSValue::encode(jsUndefined());
1647
1648     JSValue a0 = exec->argument(0);
1649
1650     uint32_t smallInteger;
1651     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1652         unsigned stringSize = s.length();
1653         unsigned bufferSize = 22 + stringSize;
1654         // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1655         UChar* buffer;
1656         PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1657         if (!impl)
1658             return JSValue::encode(jsUndefined());
1659         buffer[0] = '<';
1660         buffer[1] = 'f';
1661         buffer[2] = 'o';
1662         buffer[3] = 'n';
1663         buffer[4] = 't';
1664         buffer[5] = ' ';
1665         buffer[6] = 's';
1666         buffer[7] = 'i';
1667         buffer[8] = 'z';
1668         buffer[9] = 'e';
1669         buffer[10] = '=';
1670         buffer[11] = '"';
1671         buffer[12] = '0' + smallInteger;
1672         buffer[13] = '"';
1673         buffer[14] = '>';
1674         StringView(s).getCharactersWithUpconvert(&buffer[15]);
1675         buffer[15 + stringSize] = '<';
1676         buffer[16 + stringSize] = '/';
1677         buffer[17 + stringSize] = 'f';
1678         buffer[18 + stringSize] = 'o';
1679         buffer[19 + stringSize] = 'n';
1680         buffer[20 + stringSize] = 't';
1681         buffer[21 + stringSize] = '>';
1682         return JSValue::encode(jsNontrivialString(exec, impl));
1683     }
1684
1685     String fontSize = a0.toWTFString(exec);
1686     fontSize.replaceWithLiteral('"', "&quot;");
1687
1688     return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1689 }
1690
1691 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1692 {
1693     JSValue thisValue = exec->thisValue();
1694     if (!checkObjectCoercible(thisValue))
1695         return throwVMTypeError(exec);
1696     String s = thisValue.toString(exec)->value(exec);
1697     if (exec->hadException())
1698         return JSValue::encode(jsUndefined());
1699
1700     JSValue a0 = exec->argument(0);
1701     String anchor = a0.toWTFString(exec);
1702     anchor.replaceWithLiteral('"', "&quot;");
1703
1704     return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1705 }
1706
1707 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1708 {
1709     JSValue thisValue = exec->thisValue();
1710     if (!checkObjectCoercible(thisValue))
1711         return throwVMTypeError(exec);
1712     String s = thisValue.toString(exec)->value(exec);
1713     if (exec->hadException())
1714         return JSValue::encode(jsUndefined());
1715
1716     JSValue a0 = exec->argument(0);
1717     String linkText = a0.toWTFString(exec);
1718     linkText.replaceWithLiteral('"', "&quot;");
1719
1720     unsigned linkTextSize = linkText.length();
1721     unsigned stringSize = s.length();
1722     unsigned bufferSize = 15 + linkTextSize + stringSize;
1723     // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1724     UChar* buffer;
1725     PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1726     if (!impl)
1727         return JSValue::encode(jsUndefined());
1728     buffer[0] = '<';
1729     buffer[1] = 'a';
1730     buffer[2] = ' ';
1731     buffer[3] = 'h';
1732     buffer[4] = 'r';
1733     buffer[5] = 'e';
1734     buffer[6] = 'f';
1735     buffer[7] = '=';
1736     buffer[8] = '"';
1737     StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1738     buffer[9 + linkTextSize] = '"';
1739     buffer[10 + linkTextSize] = '>';
1740     StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1741     buffer[11 + linkTextSize + stringSize] = '<';
1742     buffer[12 + linkTextSize + stringSize] = '/';
1743     buffer[13 + linkTextSize + stringSize] = 'a';
1744     buffer[14 + linkTextSize + stringSize] = '>';
1745     return JSValue::encode(jsNontrivialString(exec, impl));
1746 }
1747
1748 enum {
1749     TrimLeft = 1,
1750     TrimRight = 2
1751 };
1752
1753 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1754 {
1755     if (!checkObjectCoercible(thisValue))
1756         return throwTypeError(exec);
1757     String str = thisValue.toString(exec)->value(exec);
1758     if (exec->hadException())
1759         return jsUndefined();
1760
1761     unsigned left = 0;
1762     if (trimKind & TrimLeft) {
1763         while (left < str.length() && isStrWhiteSpace(str[left]))
1764             left++;
1765     }
1766     unsigned right = str.length();
1767     if (trimKind & TrimRight) {
1768         while (right > left && isStrWhiteSpace(str[right - 1]))
1769             right--;
1770     }
1771
1772     // Don't gc allocate a new string if we don't have to.
1773     if (left == 0 && right == str.length() && thisValue.isString())
1774         return thisValue;
1775
1776     return jsString(exec, str.substringSharingImpl(left, right - left));
1777 }
1778
1779 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1780 {
1781     JSValue thisValue = exec->thisValue();
1782     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1783 }
1784
1785 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1786 {
1787     JSValue thisValue = exec->thisValue();
1788     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1789 }
1790
1791 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1792 {
1793     JSValue thisValue = exec->thisValue();
1794     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1795 }
1796
1797 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1798 {
1799     if (value < min)
1800         return min;
1801     if (value > max)
1802         return max;
1803     return static_cast<unsigned>(value);
1804 }
1805
1806 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1807 {
1808     JSValue thisValue = exec->thisValue();
1809     if (!checkObjectCoercible(thisValue))
1810         return throwVMTypeError(exec);
1811
1812     String stringToSearchIn = thisValue.toString(exec)->value(exec);
1813     if (exec->hadException())
1814         return JSValue::encode(jsUndefined());
1815
1816     JSValue a0 = exec->argument(0);
1817     VM& vm = exec->vm();
1818     bool isRegularExpression = isRegExp(vm, exec, a0);
1819     if (vm.exception())
1820         return JSValue::encode(JSValue());
1821     if (isRegularExpression)
1822         return throwVMTypeError(exec, "Argument to String.prototype.startsWith cannot be a RegExp");
1823
1824     String searchString = a0.toString(exec)->value(exec);
1825     if (exec->hadException())
1826         return JSValue::encode(jsUndefined());
1827
1828     JSValue positionArg = exec->argument(1);
1829     unsigned start = 0;
1830     if (positionArg.isInt32())
1831         start = std::max(0, positionArg.asInt32());
1832     else {
1833         unsigned length = stringToSearchIn.length();
1834         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1835         if (exec->hadException())
1836             return JSValue::encode(jsUndefined());
1837     }
1838
1839     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1840 }
1841
1842 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1843 {
1844     JSValue thisValue = exec->thisValue();
1845     if (!checkObjectCoercible(thisValue))
1846         return throwVMTypeError(exec);
1847
1848     String stringToSearchIn = thisValue.toString(exec)->value(exec);
1849     if (exec->hadException())
1850         return JSValue::encode(jsUndefined());
1851
1852     JSValue a0 = exec->argument(0);
1853     VM& vm = exec->vm();
1854     bool isRegularExpression = isRegExp(vm, exec, a0);
1855     if (vm.exception())
1856         return JSValue::encode(JSValue());
1857     if (isRegularExpression)
1858         return throwVMTypeError(exec, "Argument to String.prototype.endsWith cannot be a RegExp");
1859
1860     String searchString = a0.toString(exec)->value(exec);
1861     if (exec->hadException())
1862         return JSValue::encode(jsUndefined());
1863
1864     unsigned length = stringToSearchIn.length();
1865
1866     JSValue endPositionArg = exec->argument(1);
1867     unsigned end = length;
1868     if (endPositionArg.isInt32())
1869         end = std::max(0, endPositionArg.asInt32());
1870     else if (!endPositionArg.isUndefined()) {
1871         end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1872         if (exec->hadException())
1873             return JSValue::encode(jsUndefined());
1874     }
1875
1876     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1877 }
1878
1879 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1880 {
1881     unsigned start = 0;
1882     if (positionArg.isInt32())
1883         start = std::max(0, positionArg.asInt32());
1884     else {
1885         unsigned length = stringToSearchIn.length();
1886         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1887         if (vm.exception())
1888             return JSValue::encode(jsUndefined());
1889     }
1890
1891     return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1892 }
1893
1894 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1895 {
1896     JSValue thisValue = exec->thisValue();
1897     if (!checkObjectCoercible(thisValue))
1898         return throwVMTypeError(exec);
1899
1900     String stringToSearchIn = thisValue.toString(exec)->value(exec);
1901     if (exec->hadException())
1902         return JSValue::encode(jsUndefined());
1903
1904     JSValue a0 = exec->argument(0);
1905     VM& vm = exec->vm();
1906     bool isRegularExpression = isRegExp(vm, exec, a0);
1907     if (vm.exception())
1908         return JSValue::encode(JSValue());
1909     if (isRegularExpression)
1910         return throwVMTypeError(exec, "Argument to String.prototype.includes cannot be a RegExp");
1911
1912     String searchString = a0.toString(exec)->value(exec);
1913     if (exec->hadException())
1914         return JSValue::encode(jsUndefined());
1915
1916     JSValue positionArg = exec->argument(1);
1917
1918     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1919 }
1920
1921 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
1922 {
1923     JSValue thisValue = exec->thisValue();
1924     ASSERT(checkObjectCoercible(thisValue));
1925
1926     String stringToSearchIn = thisValue.toString(exec)->value(exec);
1927     if (exec->hadException())
1928         return JSValue::encode(jsUndefined());
1929
1930     JSValue a0 = exec->uncheckedArgument(0);
1931     VM& vm = exec->vm();
1932     String searchString = a0.toString(exec)->value(exec);
1933     if (exec->hadException())
1934         return JSValue::encode(jsUndefined());
1935
1936     JSValue positionArg = exec->argument(1);
1937
1938     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1939 }
1940
1941 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
1942 {
1943     JSValue thisValue = exec->thisValue();
1944     if (!checkObjectCoercible(thisValue))
1945         return throwVMTypeError(exec);
1946     JSString* string = thisValue.toString(exec);
1947     return JSValue::encode(JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), string));
1948 }
1949
1950 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
1951 {
1952     UErrorCode status = U_ZERO_ERROR;
1953     int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
1954
1955     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1956         // The behavior is not specified when normalize fails.
1957         // Now we throw a type erorr since it seems that the contents of the string are invalid.
1958         return throwTypeError(exec);
1959     }
1960
1961     UChar* buffer = nullptr;
1962     RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
1963     if (!impl)
1964         return throwOutOfMemoryError(exec);
1965
1966     status = U_ZERO_ERROR;
1967     unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
1968     if (U_FAILURE(status))
1969         return throwTypeError(exec);
1970
1971     return jsString(exec, impl.release());
1972 }
1973
1974 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
1975 {
1976     JSValue thisValue = exec->thisValue();
1977     if (!checkObjectCoercible(thisValue))
1978         return throwVMTypeError(exec);
1979     JSString::SafeView source = thisValue.toString(exec)->view(exec);
1980     if (exec->hadException())
1981         return JSValue::encode(jsUndefined());
1982
1983     UNormalizationMode form = UNORM_NFC;
1984     // Verify that the argument is provided and is not undefined.
1985     if (!exec->argument(0).isUndefined()) {
1986         String formString = exec->uncheckedArgument(0).toString(exec)->value(exec);
1987         if (exec->hadException())
1988             return JSValue::encode(jsUndefined());
1989
1990         if (formString == "NFC")
1991             form = UNORM_NFC;
1992         else if (formString == "NFD")
1993             form = UNORM_NFD;
1994         else if (formString == "NFKC")
1995             form = UNORM_NFKC;
1996         else if (formString == "NFKD")
1997             form = UNORM_NFKD;
1998         else
1999             return throwVMError(exec, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2000     }
2001
2002     return JSValue::encode(normalize(exec, source.get().upconvertedCharacters(), source.length(), form));
2003 }
2004
2005 } // namespace JSC