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