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