Use JSValue::toWTFString instead of calling toString(exec) and value(exec)
[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 = jsCast<JSString*>(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 = jsCast<JSString*>(thisValue.asCell());
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 #if !ASSERT_DISABLED
1364     JSValue thisValue = exec->thisValue();
1365     ASSERT(thisValue.isString());
1366 #endif
1367     return stringProtoFuncSubstr(exec);
1368 }
1369
1370 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1371 {
1372     VM& vm = exec->vm();
1373     auto scope = DECLARE_THROW_SCOPE(vm);
1374
1375     JSValue thisValue = exec->thisValue();
1376     if (!checkObjectCoercible(thisValue))
1377         return throwVMTypeError(exec, scope);
1378
1379     JSString* jsString = thisValue.toString(exec);
1380     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1381
1382     JSValue a0 = exec->argument(0);
1383     JSValue a1 = exec->argument(1);
1384     int len = jsString->length();
1385     RELEASE_ASSERT(len >= 0);
1386
1387     double start = a0.toNumber(exec);
1388     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1389     double end;
1390     if (!(start >= 0)) // check for negative values or NaN
1391         start = 0;
1392     else if (start > len)
1393         start = len;
1394     if (a1.isUndefined())
1395         end = len;
1396     else { 
1397         end = a1.toNumber(exec);
1398         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1399         if (!(end >= 0)) // check for negative values or NaN
1400             end = 0;
1401         else if (end > len)
1402             end = len;
1403     }
1404     if (start > end) {
1405         double temp = end;
1406         end = start;
1407         start = temp;
1408     }
1409     unsigned substringStart = static_cast<unsigned>(start);
1410     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1411     return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1412 }
1413
1414 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1415 {
1416     VM& vm = exec->vm();
1417     auto scope = DECLARE_THROW_SCOPE(vm);
1418
1419     JSValue thisValue = exec->thisValue();
1420     if (!checkObjectCoercible(thisValue))
1421         return throwVMTypeError(exec, scope);
1422     JSString* sVal = thisValue.toString(exec);
1423     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1424     const String& s = sVal->value(exec);
1425     String lowercasedString = s.convertToLowercaseWithoutLocale();
1426     if (lowercasedString.impl() == s.impl())
1427         return JSValue::encode(sVal);
1428     scope.release();
1429     return JSValue::encode(jsString(exec, lowercasedString));
1430 }
1431
1432 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1433 {
1434     VM& vm = exec->vm();
1435     auto scope = DECLARE_THROW_SCOPE(vm);
1436
1437     JSValue thisValue = exec->thisValue();
1438     if (!checkObjectCoercible(thisValue))
1439         return throwVMTypeError(exec, scope);
1440     JSString* sVal = thisValue.toString(exec);
1441     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1442     const String& s = sVal->value(exec);
1443     String uppercasedString = s.convertToUppercaseWithoutLocale();
1444     if (uppercasedString.impl() == s.impl())
1445         return JSValue::encode(sVal);
1446     scope.release();
1447     return JSValue::encode(jsString(exec, uppercasedString));
1448 }
1449
1450 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1451 {
1452     VM& vm = exec->vm();
1453     auto scope = DECLARE_THROW_SCOPE(vm);
1454
1455     JSValue thisValue = exec->thisValue();
1456     if (!checkObjectCoercible(thisValue))
1457         return throwVMTypeError(exec, scope);
1458     String s = thisValue.toWTFString(exec);
1459     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1460
1461     JSValue a0 = exec->argument(0);
1462     String str = a0.toWTFString(exec);
1463     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1464     return JSValue::encode(jsNumber(Collator().collate(s, str)));
1465 }
1466
1467 #if ENABLE(INTL)
1468 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1469 {
1470     VM& vm = state->vm();
1471     auto scope = DECLARE_THROW_SCOPE(vm);
1472
1473     // 1. Let O be RequireObjectCoercible(this value).
1474     JSValue thisValue = state->thisValue();
1475     if (!checkObjectCoercible(thisValue))
1476         return throwVMTypeError(state, scope);
1477
1478     // 2. Let S be ToString(O).
1479     JSString* sVal = thisValue.toString(state);
1480     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1481     const String& s = sVal->value(state);
1482
1483     // 3. ReturnIfAbrupt(S).
1484     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1485
1486     // Optimization for empty strings.
1487     if (s.isEmpty())
1488         return JSValue::encode(sVal);
1489
1490     // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1491     Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1492
1493     // 5. ReturnIfAbrupt(requestedLocales).
1494     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1495
1496     // 6. Let len be the number of elements in requestedLocales.
1497     size_t len = requestedLocales.size();
1498
1499     // 7. If len > 0, then
1500     // a. Let requestedLocale be the first element of requestedLocales.
1501     // 8. Else
1502     // a. Let requestedLocale be DefaultLocale().
1503     String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1504
1505     // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1506     String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1507
1508     // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1509     // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1510     const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1511
1512     // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1513     String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1514
1515     // 12. If locale is undefined, let locale be "und".
1516     if (locale.isNull())
1517         locale = ASCIILiteral("und");
1518
1519     CString utf8LocaleBuffer = locale.utf8();
1520     const StringView view(s);
1521     const int32_t viewLength = view.length();
1522
1523     // Delegate the following steps to icu u_strToLower or u_strToUpper.
1524     // 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.
1525     // 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).
1526     // 15. Let cuList be a new List.
1527     // 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.
1528     // 17. Let L be a String whose elements are, in order, the elements of cuList.
1529
1530     // Most strings lower/upper case will be the same size as original, so try that first.
1531     UErrorCode error(U_ZERO_ERROR);
1532     Vector<UChar> buffer(viewLength);
1533     String lower;
1534     const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1535     if (U_SUCCESS(error))
1536         lower = String(buffer.data(), resultLength);
1537     else if (error == U_BUFFER_OVERFLOW_ERROR) {
1538         // Converted case needs more space than original. Try again.
1539         UErrorCode error(U_ZERO_ERROR);
1540         Vector<UChar> buffer(resultLength);
1541         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1542         if (U_FAILURE(error))
1543             return throwVMTypeError(state, scope, u_errorName(error));
1544         lower = String(buffer.data(), resultLength);
1545     } else
1546         return throwVMTypeError(state, scope, u_errorName(error));
1547
1548     // 18. Return L.
1549     scope.release();
1550     return JSValue::encode(jsString(state, lower));
1551 }
1552
1553 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1554 {
1555     // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1556     // http://ecma-international.org/publications/standards/Ecma-402.htm
1557     return toLocaleCase(state, u_strToLower);
1558 }
1559
1560 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1561 {
1562     // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1563     // http://ecma-international.org/publications/standards/Ecma-402.htm
1564     // 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.
1565     return toLocaleCase(state, u_strToUpper);
1566 }
1567 #endif // ENABLE(INTL)
1568
1569 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1570 {
1571     VM& vm = exec->vm();
1572     auto scope = DECLARE_THROW_SCOPE(vm);
1573
1574     JSValue thisValue = exec->thisValue();
1575     if (!checkObjectCoercible(thisValue))
1576         return throwVMTypeError(exec, scope);
1577     String s = thisValue.toWTFString(exec);
1578     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1579     scope.release();
1580     return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1581 }
1582
1583 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1584 {
1585     VM& vm = exec->vm();
1586     auto scope = DECLARE_THROW_SCOPE(vm);
1587
1588     JSValue thisValue = exec->thisValue();
1589     if (!checkObjectCoercible(thisValue))
1590         return throwVMTypeError(exec, scope);
1591     String s = thisValue.toWTFString(exec);
1592     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1593     scope.release();
1594     return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1595 }
1596
1597 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1598 {
1599     VM& vm = exec->vm();
1600     auto scope = DECLARE_THROW_SCOPE(vm);
1601
1602     JSValue thisValue = exec->thisValue();
1603     if (!checkObjectCoercible(thisValue))
1604         return throwVMTypeError(exec, scope);
1605     String s = thisValue.toWTFString(exec);
1606     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1607     scope.release();
1608     return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1609 }
1610
1611 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1612 {
1613     VM& vm = exec->vm();
1614     auto scope = DECLARE_THROW_SCOPE(vm);
1615
1616     JSValue thisValue = exec->thisValue();
1617     if (!checkObjectCoercible(thisValue))
1618         return throwVMTypeError(exec, scope);
1619     String s = thisValue.toWTFString(exec);
1620     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1621     scope.release();
1622     return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1623 }
1624
1625 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1626 {
1627     VM& vm = exec->vm();
1628     auto scope = DECLARE_THROW_SCOPE(vm);
1629
1630     JSValue thisValue = exec->thisValue();
1631     if (!checkObjectCoercible(thisValue))
1632         return throwVMTypeError(exec, scope);
1633     String s = thisValue.toWTFString(exec);
1634     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1635     scope.release();
1636     return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1637 }
1638
1639 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1640 {
1641     VM& vm = exec->vm();
1642     auto scope = DECLARE_THROW_SCOPE(vm);
1643
1644     JSValue thisValue = exec->thisValue();
1645     if (!checkObjectCoercible(thisValue))
1646         return throwVMTypeError(exec, scope);
1647     String s = thisValue.toWTFString(exec);
1648     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1649     scope.release();
1650     return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1651 }
1652
1653 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1654 {
1655     VM& vm = exec->vm();
1656     auto scope = DECLARE_THROW_SCOPE(vm);
1657
1658     JSValue thisValue = exec->thisValue();
1659     if (!checkObjectCoercible(thisValue))
1660         return throwVMTypeError(exec, scope);
1661     String s = thisValue.toWTFString(exec);
1662     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1663     scope.release();
1664     return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1665 }
1666
1667 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1668 {
1669     VM& vm = exec->vm();
1670     auto scope = DECLARE_THROW_SCOPE(vm);
1671
1672     JSValue thisValue = exec->thisValue();
1673     if (!checkObjectCoercible(thisValue))
1674         return throwVMTypeError(exec, scope);
1675     String s = thisValue.toWTFString(exec);
1676     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1677     scope.release();
1678     return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1679 }
1680
1681 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1682 {
1683     VM& vm = exec->vm();
1684     auto scope = DECLARE_THROW_SCOPE(vm);
1685
1686     JSValue thisValue = exec->thisValue();
1687     if (!checkObjectCoercible(thisValue))
1688         return throwVMTypeError(exec, scope);
1689     String s = thisValue.toWTFString(exec);
1690     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1691     scope.release();
1692     return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1693 }
1694
1695 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1696 {
1697     VM& vm = exec->vm();
1698     auto scope = DECLARE_THROW_SCOPE(vm);
1699
1700     JSValue thisValue = exec->thisValue();
1701     if (!checkObjectCoercible(thisValue))
1702         return throwVMTypeError(exec, scope);
1703     String s = thisValue.toWTFString(exec);
1704     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1705
1706     JSValue a0 = exec->argument(0);
1707     String color = a0.toWTFString(exec);
1708     color.replaceWithLiteral('"', "&quot;");
1709
1710     scope.release();
1711     return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1712 }
1713
1714 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1715 {
1716     VM& vm = exec->vm();
1717     auto scope = DECLARE_THROW_SCOPE(vm);
1718
1719     JSValue thisValue = exec->thisValue();
1720     if (!checkObjectCoercible(thisValue))
1721         return throwVMTypeError(exec, scope);
1722     String s = thisValue.toWTFString(exec);
1723     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1724
1725     JSValue a0 = exec->argument(0);
1726
1727     uint32_t smallInteger;
1728     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1729         unsigned stringSize = s.length();
1730         unsigned bufferSize = 22 + stringSize;
1731         // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1732         UChar* buffer;
1733         auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1734         if (!impl)
1735             return JSValue::encode(jsUndefined());
1736         buffer[0] = '<';
1737         buffer[1] = 'f';
1738         buffer[2] = 'o';
1739         buffer[3] = 'n';
1740         buffer[4] = 't';
1741         buffer[5] = ' ';
1742         buffer[6] = 's';
1743         buffer[7] = 'i';
1744         buffer[8] = 'z';
1745         buffer[9] = 'e';
1746         buffer[10] = '=';
1747         buffer[11] = '"';
1748         buffer[12] = '0' + smallInteger;
1749         buffer[13] = '"';
1750         buffer[14] = '>';
1751         StringView(s).getCharactersWithUpconvert(&buffer[15]);
1752         buffer[15 + stringSize] = '<';
1753         buffer[16 + stringSize] = '/';
1754         buffer[17 + stringSize] = 'f';
1755         buffer[18 + stringSize] = 'o';
1756         buffer[19 + stringSize] = 'n';
1757         buffer[20 + stringSize] = 't';
1758         buffer[21 + stringSize] = '>';
1759         return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1760     }
1761
1762     String fontSize = a0.toWTFString(exec);
1763     fontSize.replaceWithLiteral('"', "&quot;");
1764
1765     scope.release();
1766     return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1767 }
1768
1769 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1770 {
1771     VM& vm = exec->vm();
1772     auto scope = DECLARE_THROW_SCOPE(vm);
1773
1774     JSValue thisValue = exec->thisValue();
1775     if (!checkObjectCoercible(thisValue))
1776         return throwVMTypeError(exec, scope);
1777     String s = thisValue.toWTFString(exec);
1778     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1779
1780     JSValue a0 = exec->argument(0);
1781     String anchor = a0.toWTFString(exec);
1782     anchor.replaceWithLiteral('"', "&quot;");
1783
1784     scope.release();
1785     return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1786 }
1787
1788 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1789 {
1790     VM& vm = exec->vm();
1791     auto scope = DECLARE_THROW_SCOPE(vm);
1792
1793     JSValue thisValue = exec->thisValue();
1794     if (!checkObjectCoercible(thisValue))
1795         return throwVMTypeError(exec, scope);
1796     String s = thisValue.toWTFString(exec);
1797     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1798
1799     JSValue a0 = exec->argument(0);
1800     String linkText = a0.toWTFString(exec);
1801     linkText.replaceWithLiteral('"', "&quot;");
1802
1803     unsigned linkTextSize = linkText.length();
1804     unsigned stringSize = s.length();
1805     unsigned bufferSize = 15 + linkTextSize + stringSize;
1806     // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1807     UChar* buffer;
1808     auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1809     if (!impl)
1810         return JSValue::encode(jsUndefined());
1811     buffer[0] = '<';
1812     buffer[1] = 'a';
1813     buffer[2] = ' ';
1814     buffer[3] = 'h';
1815     buffer[4] = 'r';
1816     buffer[5] = 'e';
1817     buffer[6] = 'f';
1818     buffer[7] = '=';
1819     buffer[8] = '"';
1820     StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1821     buffer[9 + linkTextSize] = '"';
1822     buffer[10 + linkTextSize] = '>';
1823     StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1824     buffer[11 + linkTextSize + stringSize] = '<';
1825     buffer[12 + linkTextSize + stringSize] = '/';
1826     buffer[13 + linkTextSize + stringSize] = 'a';
1827     buffer[14 + linkTextSize + stringSize] = '>';
1828     return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1829 }
1830
1831 enum {
1832     TrimLeft = 1,
1833     TrimRight = 2
1834 };
1835
1836 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1837 {
1838     VM& vm = exec->vm();
1839     auto scope = DECLARE_THROW_SCOPE(vm);
1840
1841     if (!checkObjectCoercible(thisValue))
1842         return throwTypeError(exec, scope);
1843     String str = thisValue.toWTFString(exec);
1844     RETURN_IF_EXCEPTION(scope, { });
1845
1846     unsigned left = 0;
1847     if (trimKind & TrimLeft) {
1848         while (left < str.length() && isStrWhiteSpace(str[left]))
1849             left++;
1850     }
1851     unsigned right = str.length();
1852     if (trimKind & TrimRight) {
1853         while (right > left && isStrWhiteSpace(str[right - 1]))
1854             right--;
1855     }
1856
1857     // Don't gc allocate a new string if we don't have to.
1858     if (left == 0 && right == str.length() && thisValue.isString())
1859         return thisValue;
1860
1861     scope.release();
1862     return jsString(exec, str.substringSharingImpl(left, right - left));
1863 }
1864
1865 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1866 {
1867     JSValue thisValue = exec->thisValue();
1868     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1869 }
1870
1871 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1872 {
1873     JSValue thisValue = exec->thisValue();
1874     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1875 }
1876
1877 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1878 {
1879     JSValue thisValue = exec->thisValue();
1880     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1881 }
1882
1883 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1884 {
1885     if (value < min)
1886         return min;
1887     if (value > max)
1888         return max;
1889     return static_cast<unsigned>(value);
1890 }
1891
1892 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1893 {
1894     VM& vm = exec->vm();
1895     auto scope = DECLARE_THROW_SCOPE(vm);
1896
1897     JSValue thisValue = exec->thisValue();
1898     if (!checkObjectCoercible(thisValue))
1899         return throwVMTypeError(exec, scope);
1900
1901     String stringToSearchIn = thisValue.toWTFString(exec);
1902     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1903
1904     JSValue a0 = exec->argument(0);
1905     bool isRegularExpression = isRegExp(vm, exec, a0);
1906     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1907     if (isRegularExpression)
1908         return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
1909
1910     String searchString = a0.toWTFString(exec);
1911     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1912
1913     JSValue positionArg = exec->argument(1);
1914     unsigned start = 0;
1915     if (positionArg.isInt32())
1916         start = std::max(0, positionArg.asInt32());
1917     else {
1918         unsigned length = stringToSearchIn.length();
1919         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1920         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1921     }
1922
1923     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1924 }
1925
1926 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1927 {
1928     VM& vm = exec->vm();
1929     auto scope = DECLARE_THROW_SCOPE(vm);
1930
1931     JSValue thisValue = exec->thisValue();
1932     if (!checkObjectCoercible(thisValue))
1933         return throwVMTypeError(exec, scope);
1934
1935     String stringToSearchIn = thisValue.toWTFString(exec);
1936     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1937
1938     JSValue a0 = exec->argument(0);
1939     bool isRegularExpression = isRegExp(vm, exec, a0);
1940     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1941     if (isRegularExpression)
1942         return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
1943
1944     String searchString = a0.toWTFString(exec);
1945     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1946
1947     unsigned length = stringToSearchIn.length();
1948
1949     JSValue endPositionArg = exec->argument(1);
1950     unsigned end = length;
1951     if (endPositionArg.isInt32())
1952         end = std::max(0, endPositionArg.asInt32());
1953     else if (!endPositionArg.isUndefined()) {
1954         end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1955         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1956     }
1957
1958     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1959 }
1960
1961 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1962 {
1963     auto scope = DECLARE_THROW_SCOPE(vm);
1964     unsigned start = 0;
1965     if (positionArg.isInt32())
1966         start = std::max(0, positionArg.asInt32());
1967     else {
1968         unsigned length = stringToSearchIn.length();
1969         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1970         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1971     }
1972
1973     return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1974 }
1975
1976 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1977 {
1978     VM& vm = exec->vm();
1979     auto scope = DECLARE_THROW_SCOPE(vm);
1980
1981     JSValue thisValue = exec->thisValue();
1982     if (!checkObjectCoercible(thisValue))
1983         return throwVMTypeError(exec, scope);
1984
1985     String stringToSearchIn = thisValue.toWTFString(exec);
1986     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1987
1988     JSValue a0 = exec->argument(0);
1989     bool isRegularExpression = isRegExp(vm, exec, a0);
1990     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1991     if (isRegularExpression)
1992         return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
1993
1994     String searchString = a0.toWTFString(exec);
1995     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1996
1997     JSValue positionArg = exec->argument(1);
1998
1999     scope.release();
2000     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2001 }
2002
2003 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
2004 {
2005     VM& vm = exec->vm();
2006     auto scope = DECLARE_THROW_SCOPE(vm);
2007
2008     JSValue thisValue = exec->thisValue();
2009     ASSERT(checkObjectCoercible(thisValue));
2010
2011     String stringToSearchIn = thisValue.toWTFString(exec);
2012     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2013
2014     JSValue a0 = exec->uncheckedArgument(0);
2015     String searchString = a0.toWTFString(exec);
2016     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2017
2018     JSValue positionArg = exec->argument(1);
2019
2020     scope.release();
2021     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2022 }
2023
2024 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
2025 {
2026     VM& vm = exec->vm();
2027     auto scope = DECLARE_THROW_SCOPE(vm);
2028
2029     JSValue thisValue = exec->thisValue();
2030     if (!checkObjectCoercible(thisValue))
2031         return throwVMTypeError(exec, scope);
2032     JSString* string = thisValue.toString(exec);
2033     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2034     return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
2035 }
2036
2037 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
2038 {
2039     VM& vm = exec->vm();
2040     auto scope = DECLARE_THROW_SCOPE(vm);
2041
2042     UErrorCode status = U_ZERO_ERROR;
2043     int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
2044
2045     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
2046         // The behavior is not specified when normalize fails.
2047         // Now we throw a type erorr since it seems that the contents of the string are invalid.
2048         return throwTypeError(exec, scope);
2049     }
2050
2051     UChar* buffer = nullptr;
2052     auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
2053     if (!impl)
2054         return throwOutOfMemoryError(exec, scope);
2055
2056     status = U_ZERO_ERROR;
2057     unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
2058     if (U_FAILURE(status))
2059         return throwTypeError(exec, scope);
2060
2061     scope.release();
2062     return jsString(exec, WTFMove(impl));
2063 }
2064
2065 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
2066 {
2067     VM& vm = exec->vm();
2068     auto scope = DECLARE_THROW_SCOPE(vm);
2069
2070     JSValue thisValue = exec->thisValue();
2071     if (!checkObjectCoercible(thisValue))
2072         return throwVMTypeError(exec, scope);
2073     auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
2074     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2075     StringView view = viewWithString.view;
2076
2077     UNormalizationMode form = UNORM_NFC;
2078     // Verify that the argument is provided and is not undefined.
2079     if (!exec->argument(0).isUndefined()) {
2080         String formString = exec->uncheckedArgument(0).toWTFString(exec);
2081         RETURN_IF_EXCEPTION(scope, encodedJSValue());
2082
2083         if (formString == "NFC")
2084             form = UNORM_NFC;
2085         else if (formString == "NFD")
2086             form = UNORM_NFD;
2087         else if (formString == "NFKC")
2088             form = UNORM_NFKC;
2089         else if (formString == "NFKD")
2090             form = UNORM_NFKD;
2091         else
2092             return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2093     }
2094
2095     scope.release();
2096     return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));
2097 }
2098
2099 } // namespace JSC