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