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