[JSC][DFG][DOMJIT] Extend CheckDOM to CheckSubClass
[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 stringProtoFuncConcat(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     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("concat", stringProtoFuncConcat, 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 stringProtoFuncConcat(ExecState* exec)
998 {
999     VM& vm = exec->vm();
1000     auto scope = DECLARE_THROW_SCOPE(vm);
1001
1002     JSValue thisValue = exec->thisValue();
1003     if (thisValue.isString() && exec->argumentCount() == 1) {
1004         JSString* str = exec->uncheckedArgument(0).toString(exec);
1005         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1006         scope.release();
1007         return JSValue::encode(jsString(exec, asString(thisValue), str));
1008     }
1009
1010     if (!checkObjectCoercible(thisValue))
1011         return throwVMTypeError(exec, scope);
1012     scope.release();
1013     return JSValue::encode(jsStringFromArguments(exec, thisValue));
1014 }
1015
1016 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1017 {
1018     VM& vm = exec->vm();
1019     auto scope = DECLARE_THROW_SCOPE(vm);
1020
1021     JSValue thisValue = exec->thisValue();
1022     if (!checkObjectCoercible(thisValue))
1023         return throwVMTypeError(exec, scope);
1024
1025     JSValue a0 = exec->argument(0);
1026     JSValue a1 = exec->argument(1);
1027
1028     JSString* thisJSString = thisValue.toString(exec);
1029     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1030     JSString* otherJSString = a0.toString(exec);
1031     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1032
1033     unsigned pos = 0;
1034     if (!a1.isUndefined()) {
1035         int len = thisJSString->length();
1036         RELEASE_ASSERT(len >= 0);
1037         if (a1.isUInt32())
1038             pos = std::min<uint32_t>(a1.asUInt32(), len);
1039         else {
1040             double dpos = a1.toInteger(exec);
1041             if (dpos < 0)
1042                 dpos = 0;
1043             else if (dpos > len)
1044                 dpos = len;
1045             pos = static_cast<unsigned>(dpos);
1046         }
1047     }
1048
1049     if (thisJSString->length() < otherJSString->length() + pos)
1050         return JSValue::encode(jsNumber(-1));
1051
1052     auto thisViewWithString = thisJSString->viewWithUnderlyingString(exec);
1053     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1054     auto otherViewWithString = otherJSString->viewWithUnderlyingString(exec);
1055     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1056     size_t result = thisViewWithString.view.find(otherViewWithString.view, pos);
1057     if (result == notFound)
1058         return JSValue::encode(jsNumber(-1));
1059     return JSValue::encode(jsNumber(result));
1060 }
1061
1062 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1063 {
1064     VM& vm = exec->vm();
1065     auto scope = DECLARE_THROW_SCOPE(vm);
1066
1067     JSValue thisValue = exec->thisValue();
1068     if (!checkObjectCoercible(thisValue))
1069         return throwVMTypeError(exec, scope);
1070
1071     JSValue a0 = exec->argument(0);
1072     JSValue a1 = exec->argument(1);
1073
1074     JSString* thisJSString = thisValue.toString(exec);
1075     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1076     unsigned len = thisJSString->length();
1077     JSString* otherJSString = a0.toString(exec);
1078     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1079
1080     double dpos = a1.toIntegerPreserveNaN(exec);
1081     unsigned startPosition;
1082     if (dpos < 0)
1083         startPosition = 0;
1084     else if (!(dpos <= len)) // true for NaN
1085         startPosition = len;
1086     else
1087         startPosition = static_cast<unsigned>(dpos);
1088
1089     if (len < otherJSString->length())
1090         return JSValue::encode(jsNumber(-1));
1091
1092     String thisString = thisJSString->value(exec);
1093     String otherString = otherJSString->value(exec);
1094     size_t result;
1095     if (!startPosition)
1096         result = thisString.startsWith(otherString) ? 0 : notFound;
1097     else
1098         result = thisString.reverseFind(otherString, startPosition);
1099     if (result == notFound)
1100         return JSValue::encode(jsNumber(-1));
1101     return JSValue::encode(jsNumber(result));
1102 }
1103
1104 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1105 {
1106     VM& vm = exec->vm();
1107     auto scope = DECLARE_THROW_SCOPE(vm);
1108
1109     JSValue thisValue = exec->thisValue();
1110     if (!checkObjectCoercible(thisValue))
1111         return throwVMTypeError(exec, scope);
1112     String s = thisValue.toWTFString(exec);
1113     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1114
1115     int len = s.length();
1116     RELEASE_ASSERT(len >= 0);
1117
1118     JSValue a0 = exec->argument(0);
1119     JSValue a1 = exec->argument(1);
1120
1121     // The arg processing is very much like ArrayProtoFunc::Slice
1122     double start = a0.toInteger(exec);
1123     double end = a1.isUndefined() ? len : a1.toInteger(exec);
1124     double from = start < 0 ? len + start : start;
1125     double to = end < 0 ? len + end : end;
1126     if (to > from && to > 0 && from < len) {
1127         if (from < 0)
1128             from = 0;
1129         if (to > len)
1130             to = len;
1131         return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1132     }
1133
1134     return JSValue::encode(jsEmptyString(exec));
1135 }
1136
1137 // Return true in case of early return (resultLength got to limitLength).
1138 template<typename CharacterType>
1139 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)
1140 {
1141     VM& vm = exec->vm();
1142     auto scope = DECLARE_THROW_SCOPE(vm);
1143
1144     // 12. Let q = p.
1145     size_t matchPosition;
1146     const CharacterType* characters = string->characters<CharacterType>();
1147     // 13. Repeat, while q != s
1148     //   a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1149     //   b. If z is failure, then let q = q+1.
1150     //   c. Else, z is not failure
1151     while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1152         // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1153         //    through q (exclusive).
1154         // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1155         //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1156         result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1157         RETURN_IF_EXCEPTION(scope, false);
1158         // 3. Increment lengthA by 1.
1159         // 4. If lengthA == lim, return A.
1160         if (++resultLength == limitLength)
1161             return true;
1162
1163         // 5. Let p = e.
1164         // 8. Let q = p.
1165         position = matchPosition + 1;
1166     }
1167     return false;
1168 }
1169
1170 // ES 21.1.3.17 String.prototype.split(separator, limit)
1171 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1172 {
1173     VM& vm = exec->vm();
1174     auto scope = DECLARE_THROW_SCOPE(vm);
1175     JSValue thisValue = exec->thisValue();
1176     ASSERT(checkObjectCoercible(thisValue));
1177
1178     // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1179     // 7. Let s be the number of characters in S.
1180     String input = thisValue.toWTFString(exec);
1181     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1182     ASSERT(!input.isNull());
1183
1184     // 4. Let A be a new array created as if by the expression new Array()
1185     //    where Array is the standard built-in constructor with that name.
1186     JSArray* result = constructEmptyArray(exec, 0);
1187     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1188
1189     // 5. Let lengthA be 0.
1190     unsigned resultLength = 0;
1191
1192     // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1193     JSValue limitValue = exec->uncheckedArgument(1);
1194     unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1195
1196     // 8. Let p = 0.
1197     size_t position = 0;
1198
1199     // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1200     //    otherwise let R = ToString(separator).
1201     JSValue separatorValue = exec->uncheckedArgument(0);
1202     String separator = separatorValue.toWTFString(exec);
1203     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1204
1205     // 10. If lim == 0, return A.
1206     if (!limit)
1207         return JSValue::encode(result);
1208
1209     // 11. If separator is undefined, then
1210     if (separatorValue.isUndefined()) {
1211         // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1212         scope.release();
1213         result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1214         // b. Return A.
1215         return JSValue::encode(result);
1216     }
1217
1218     // 12. If s == 0, then
1219     if (input.isEmpty()) {
1220         // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1221         // b. If z is not false, return A.
1222         // c. Call CreateDataProperty(A, "0", S).
1223         // d. Return A.
1224         if (!separator.isEmpty()) {
1225             scope.release();
1226             result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1227         }
1228         return JSValue::encode(result);
1229     }
1230
1231     // Optimized case for splitting on the empty string.
1232     if (separator.isEmpty()) {
1233         limit = std::min(limit, input.length());
1234         // Zero limt/input length handled in steps 9/11 respectively, above.
1235         ASSERT(limit);
1236
1237         do {
1238             result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1239             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1240         } while (++position < limit);
1241
1242         return JSValue::encode(result);
1243     }
1244
1245     // 3 cases:
1246     // -separator length == 1, 8 bits
1247     // -separator length == 1, 16 bits
1248     // -separator length > 1
1249     StringImpl* stringImpl = input.impl();
1250     StringImpl* separatorImpl = separator.impl();
1251     size_t separatorLength = separatorImpl->length();
1252
1253     if (separatorLength == 1) {
1254         UChar separatorCharacter;
1255         if (separatorImpl->is8Bit())
1256             separatorCharacter = separatorImpl->characters8()[0];
1257         else
1258             separatorCharacter = separatorImpl->characters16()[0];
1259
1260         if (stringImpl->is8Bit()) {
1261             if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1262                 scope.release();
1263                 return JSValue::encode(result);
1264             }
1265         } else {
1266             if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1267                 scope.release();
1268                 return JSValue::encode(result);
1269             }
1270         }
1271         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1272     } else {
1273         // 13. Let q = p.
1274         size_t matchPosition;
1275         // 14. Repeat, while q != s
1276         //   a. let e be SplitMatch(S, q, R).
1277         //   b. If e is failure, then let q = q+1.
1278         //   c. Else, e is an integer index <= s.
1279         while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1280             // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1281             //    through q (exclusive).
1282             // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1283             result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1284             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1285             // 3. Increment lengthA by 1.
1286             // 4. If lengthA == lim, return A.
1287             if (++resultLength == limit)
1288                 return JSValue::encode(result);
1289
1290             // 5. Let p = e.
1291             // 6. Let q = p.
1292             position = matchPosition + separator.length();
1293         }
1294     }
1295
1296     // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1297     //     through s (exclusive).
1298     // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1299     scope.release();
1300     result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1301
1302     // 17. Return A.
1303     return JSValue::encode(result);
1304 }
1305
1306 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1307 {
1308     VM& vm = exec->vm();
1309     auto scope = DECLARE_THROW_SCOPE(vm);
1310
1311     JSValue thisValue = exec->thisValue();
1312     if (!checkObjectCoercible(thisValue))
1313         return throwVMTypeError(exec, scope);
1314     unsigned len;
1315     JSString* jsString = 0;
1316     String uString;
1317     if (thisValue.isString()) {
1318         jsString = asString(thisValue);
1319         len = jsString->length();
1320     } else {
1321         uString = thisValue.toWTFString(exec);
1322         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1323         len = uString.length();
1324     }
1325
1326     JSValue a0 = exec->argument(0);
1327     JSValue a1 = exec->argument(1);
1328
1329     double start = a0.toInteger(exec);
1330     double length = a1.isUndefined() ? len : a1.toInteger(exec);
1331     if (start >= len || length <= 0)
1332         return JSValue::encode(jsEmptyString(exec));
1333     if (start < 0) {
1334         start += len;
1335         if (start < 0)
1336             start = 0;
1337     }
1338     if (start + length > len)
1339         length = len - start;
1340     unsigned substringStart = static_cast<unsigned>(start);
1341     unsigned substringLength = static_cast<unsigned>(length);
1342     if (jsString)
1343         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1344     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1345 }
1346
1347 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1348 {
1349     // @substrInternal should not have any observable side effects (e.g. it should not call
1350     // GetMethod(..., @@toPrimitive) on the thisValue).
1351
1352     // It is ok to use the default stringProtoFuncSubstr as the implementation of
1353     // @substrInternal because @substrInternal will only be called by builtins, which will
1354     // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1355     // will not need to call toString() on the thisValue, and there will be no observable
1356     // side-effects.
1357     ASSERT(exec->thisValue().isString());
1358     return stringProtoFuncSubstr(exec);
1359 }
1360
1361 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1362 {
1363     VM& vm = exec->vm();
1364     auto scope = DECLARE_THROW_SCOPE(vm);
1365
1366     JSValue thisValue = exec->thisValue();
1367     if (!checkObjectCoercible(thisValue))
1368         return throwVMTypeError(exec, scope);
1369
1370     JSString* jsString = thisValue.toString(exec);
1371     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1372
1373     JSValue a0 = exec->argument(0);
1374     JSValue a1 = exec->argument(1);
1375     int len = jsString->length();
1376     RELEASE_ASSERT(len >= 0);
1377
1378     double start = a0.toNumber(exec);
1379     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1380     double end;
1381     if (!(start >= 0)) // check for negative values or NaN
1382         start = 0;
1383     else if (start > len)
1384         start = len;
1385     if (a1.isUndefined())
1386         end = len;
1387     else { 
1388         end = a1.toNumber(exec);
1389         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1390         if (!(end >= 0)) // check for negative values or NaN
1391             end = 0;
1392         else if (end > len)
1393             end = len;
1394     }
1395     if (start > end) {
1396         double temp = end;
1397         end = start;
1398         start = temp;
1399     }
1400     unsigned substringStart = static_cast<unsigned>(start);
1401     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1402     return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1403 }
1404
1405 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1406 {
1407     VM& vm = exec->vm();
1408     auto scope = DECLARE_THROW_SCOPE(vm);
1409
1410     JSValue thisValue = exec->thisValue();
1411     if (!checkObjectCoercible(thisValue))
1412         return throwVMTypeError(exec, scope);
1413     JSString* sVal = thisValue.toString(exec);
1414     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1415     const String& s = sVal->value(exec);
1416     String lowercasedString = s.convertToLowercaseWithoutLocale();
1417     if (lowercasedString.impl() == s.impl())
1418         return JSValue::encode(sVal);
1419     scope.release();
1420     return JSValue::encode(jsString(exec, lowercasedString));
1421 }
1422
1423 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1424 {
1425     VM& vm = exec->vm();
1426     auto scope = DECLARE_THROW_SCOPE(vm);
1427
1428     JSValue thisValue = exec->thisValue();
1429     if (!checkObjectCoercible(thisValue))
1430         return throwVMTypeError(exec, scope);
1431     JSString* sVal = thisValue.toString(exec);
1432     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1433     const String& s = sVal->value(exec);
1434     String uppercasedString = s.convertToUppercaseWithoutLocale();
1435     if (uppercasedString.impl() == s.impl())
1436         return JSValue::encode(sVal);
1437     scope.release();
1438     return JSValue::encode(jsString(exec, uppercasedString));
1439 }
1440
1441 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1442 {
1443     VM& vm = exec->vm();
1444     auto scope = DECLARE_THROW_SCOPE(vm);
1445
1446     JSValue thisValue = exec->thisValue();
1447     if (!checkObjectCoercible(thisValue))
1448         return throwVMTypeError(exec, scope);
1449     String s = thisValue.toWTFString(exec);
1450     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1451
1452     JSValue a0 = exec->argument(0);
1453     String str = a0.toWTFString(exec);
1454     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1455     return JSValue::encode(jsNumber(Collator().collate(s, str)));
1456 }
1457
1458 #if ENABLE(INTL)
1459 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1460 {
1461     VM& vm = state->vm();
1462     auto scope = DECLARE_THROW_SCOPE(vm);
1463
1464     // 1. Let O be RequireObjectCoercible(this value).
1465     JSValue thisValue = state->thisValue();
1466     if (!checkObjectCoercible(thisValue))
1467         return throwVMTypeError(state, scope);
1468
1469     // 2. Let S be ToString(O).
1470     JSString* sVal = thisValue.toString(state);
1471     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1472     const String& s = sVal->value(state);
1473
1474     // 3. ReturnIfAbrupt(S).
1475     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1476
1477     // Optimization for empty strings.
1478     if (s.isEmpty())
1479         return JSValue::encode(sVal);
1480
1481     // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1482     Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1483
1484     // 5. ReturnIfAbrupt(requestedLocales).
1485     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1486
1487     // 6. Let len be the number of elements in requestedLocales.
1488     size_t len = requestedLocales.size();
1489
1490     // 7. If len > 0, then
1491     // a. Let requestedLocale be the first element of requestedLocales.
1492     // 8. Else
1493     // a. Let requestedLocale be DefaultLocale().
1494     String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1495
1496     // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1497     String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1498
1499     // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1500     // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1501     const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1502
1503     // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1504     String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1505
1506     // 12. If locale is undefined, let locale be "und".
1507     if (locale.isNull())
1508         locale = ASCIILiteral("und");
1509
1510     CString utf8LocaleBuffer = locale.utf8();
1511     const StringView view(s);
1512     const int32_t viewLength = view.length();
1513
1514     // Delegate the following steps to icu u_strToLower or u_strToUpper.
1515     // 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.
1516     // 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).
1517     // 15. Let cuList be a new List.
1518     // 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.
1519     // 17. Let L be a String whose elements are, in order, the elements of cuList.
1520
1521     // Most strings lower/upper case will be the same size as original, so try that first.
1522     UErrorCode error(U_ZERO_ERROR);
1523     Vector<UChar> buffer(viewLength);
1524     String lower;
1525     const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1526     if (U_SUCCESS(error))
1527         lower = String(buffer.data(), resultLength);
1528     else if (error == U_BUFFER_OVERFLOW_ERROR) {
1529         // Converted case needs more space than original. Try again.
1530         UErrorCode error(U_ZERO_ERROR);
1531         Vector<UChar> buffer(resultLength);
1532         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1533         if (U_FAILURE(error))
1534             return throwVMTypeError(state, scope, u_errorName(error));
1535         lower = String(buffer.data(), resultLength);
1536     } else
1537         return throwVMTypeError(state, scope, u_errorName(error));
1538
1539     // 18. Return L.
1540     scope.release();
1541     return JSValue::encode(jsString(state, lower));
1542 }
1543
1544 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1545 {
1546     // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1547     // http://ecma-international.org/publications/standards/Ecma-402.htm
1548     return toLocaleCase(state, u_strToLower);
1549 }
1550
1551 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1552 {
1553     // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1554     // http://ecma-international.org/publications/standards/Ecma-402.htm
1555     // 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.
1556     return toLocaleCase(state, u_strToUpper);
1557 }
1558 #endif // ENABLE(INTL)
1559
1560 enum {
1561     TrimLeft = 1,
1562     TrimRight = 2
1563 };
1564
1565 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1566 {
1567     VM& vm = exec->vm();
1568     auto scope = DECLARE_THROW_SCOPE(vm);
1569
1570     if (!checkObjectCoercible(thisValue))
1571         return throwTypeError(exec, scope);
1572     String str = thisValue.toWTFString(exec);
1573     RETURN_IF_EXCEPTION(scope, { });
1574
1575     unsigned left = 0;
1576     if (trimKind & TrimLeft) {
1577         while (left < str.length() && isStrWhiteSpace(str[left]))
1578             left++;
1579     }
1580     unsigned right = str.length();
1581     if (trimKind & TrimRight) {
1582         while (right > left && isStrWhiteSpace(str[right - 1]))
1583             right--;
1584     }
1585
1586     // Don't gc allocate a new string if we don't have to.
1587     if (left == 0 && right == str.length() && thisValue.isString())
1588         return thisValue;
1589
1590     scope.release();
1591     return jsString(exec, str.substringSharingImpl(left, right - left));
1592 }
1593
1594 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1595 {
1596     JSValue thisValue = exec->thisValue();
1597     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1598 }
1599
1600 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1601 {
1602     JSValue thisValue = exec->thisValue();
1603     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1604 }
1605
1606 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1607 {
1608     JSValue thisValue = exec->thisValue();
1609     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1610 }
1611
1612 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1613 {
1614     if (value < min)
1615         return min;
1616     if (value > max)
1617         return max;
1618     return static_cast<unsigned>(value);
1619 }
1620
1621 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1622 {
1623     VM& vm = exec->vm();
1624     auto scope = DECLARE_THROW_SCOPE(vm);
1625
1626     JSValue thisValue = exec->thisValue();
1627     if (!checkObjectCoercible(thisValue))
1628         return throwVMTypeError(exec, scope);
1629
1630     String stringToSearchIn = thisValue.toWTFString(exec);
1631     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1632
1633     JSValue a0 = exec->argument(0);
1634     bool isRegularExpression = isRegExp(vm, exec, a0);
1635     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1636     if (isRegularExpression)
1637         return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
1638
1639     String searchString = a0.toWTFString(exec);
1640     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1641
1642     JSValue positionArg = exec->argument(1);
1643     unsigned start = 0;
1644     if (positionArg.isInt32())
1645         start = std::max(0, positionArg.asInt32());
1646     else {
1647         unsigned length = stringToSearchIn.length();
1648         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1649         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1650     }
1651
1652     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1653 }
1654
1655 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1656 {
1657     VM& vm = exec->vm();
1658     auto scope = DECLARE_THROW_SCOPE(vm);
1659
1660     JSValue thisValue = exec->thisValue();
1661     if (!checkObjectCoercible(thisValue))
1662         return throwVMTypeError(exec, scope);
1663
1664     String stringToSearchIn = thisValue.toWTFString(exec);
1665     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1666
1667     JSValue a0 = exec->argument(0);
1668     bool isRegularExpression = isRegExp(vm, exec, a0);
1669     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1670     if (isRegularExpression)
1671         return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
1672
1673     String searchString = a0.toWTFString(exec);
1674     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1675
1676     unsigned length = stringToSearchIn.length();
1677
1678     JSValue endPositionArg = exec->argument(1);
1679     unsigned end = length;
1680     if (endPositionArg.isInt32())
1681         end = std::max(0, endPositionArg.asInt32());
1682     else if (!endPositionArg.isUndefined()) {
1683         end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1684         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1685     }
1686
1687     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1688 }
1689
1690 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1691 {
1692     auto scope = DECLARE_THROW_SCOPE(vm);
1693     unsigned start = 0;
1694     if (positionArg.isInt32())
1695         start = std::max(0, positionArg.asInt32());
1696     else {
1697         unsigned length = stringToSearchIn.length();
1698         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1699         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1700     }
1701
1702     return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1703 }
1704
1705 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1706 {
1707     VM& vm = exec->vm();
1708     auto scope = DECLARE_THROW_SCOPE(vm);
1709
1710     JSValue thisValue = exec->thisValue();
1711     if (!checkObjectCoercible(thisValue))
1712         return throwVMTypeError(exec, scope);
1713
1714     String stringToSearchIn = thisValue.toWTFString(exec);
1715     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1716
1717     JSValue a0 = exec->argument(0);
1718     bool isRegularExpression = isRegExp(vm, exec, a0);
1719     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1720     if (isRegularExpression)
1721         return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
1722
1723     String searchString = a0.toWTFString(exec);
1724     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1725
1726     JSValue positionArg = exec->argument(1);
1727
1728     scope.release();
1729     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1730 }
1731
1732 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
1733 {
1734     VM& vm = exec->vm();
1735     auto scope = DECLARE_THROW_SCOPE(vm);
1736
1737     JSValue thisValue = exec->thisValue();
1738     ASSERT(checkObjectCoercible(thisValue));
1739
1740     String stringToSearchIn = thisValue.toWTFString(exec);
1741     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1742
1743     JSValue a0 = exec->uncheckedArgument(0);
1744     String searchString = a0.toWTFString(exec);
1745     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1746
1747     JSValue positionArg = exec->argument(1);
1748
1749     scope.release();
1750     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1751 }
1752
1753 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
1754 {
1755     VM& vm = exec->vm();
1756     auto scope = DECLARE_THROW_SCOPE(vm);
1757
1758     JSValue thisValue = exec->thisValue();
1759     if (!checkObjectCoercible(thisValue))
1760         return throwVMTypeError(exec, scope);
1761     JSString* string = thisValue.toString(exec);
1762     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1763     return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
1764 }
1765
1766 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
1767 {
1768     VM& vm = exec->vm();
1769     auto scope = DECLARE_THROW_SCOPE(vm);
1770
1771     UErrorCode status = U_ZERO_ERROR;
1772     int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
1773
1774     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1775         // The behavior is not specified when normalize fails.
1776         // Now we throw a type erorr since it seems that the contents of the string are invalid.
1777         return throwTypeError(exec, scope);
1778     }
1779
1780     UChar* buffer = nullptr;
1781     auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
1782     if (!impl)
1783         return throwOutOfMemoryError(exec, scope);
1784
1785     status = U_ZERO_ERROR;
1786     unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
1787     if (U_FAILURE(status))
1788         return throwTypeError(exec, scope);
1789
1790     scope.release();
1791     return jsString(exec, WTFMove(impl));
1792 }
1793
1794 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
1795 {
1796     VM& vm = exec->vm();
1797     auto scope = DECLARE_THROW_SCOPE(vm);
1798
1799     JSValue thisValue = exec->thisValue();
1800     if (!checkObjectCoercible(thisValue))
1801         return throwVMTypeError(exec, scope);
1802     auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(exec);
1803     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1804     StringView view = viewWithString.view;
1805
1806     UNormalizationMode form = UNORM_NFC;
1807     // Verify that the argument is provided and is not undefined.
1808     if (!exec->argument(0).isUndefined()) {
1809         String formString = exec->uncheckedArgument(0).toWTFString(exec);
1810         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1811
1812         if (formString == "NFC")
1813             form = UNORM_NFC;
1814         else if (formString == "NFD")
1815             form = UNORM_NFD;
1816         else if (formString == "NFKC")
1817             form = UNORM_NFKC;
1818         else if (formString == "NFKD")
1819             form = UNORM_NFKD;
1820         else
1821             return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
1822     }
1823
1824     scope.release();
1825     return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));
1826 }
1827
1828 } // namespace JSC