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