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