CachedCall should let GC know to keep its arguments alive.
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004-2017 Apple Inc. All rights reserved.
4  *  Copyright (C) 2009 Torch Mobile, Inc.
5  *  Copyright (C) 2015 Jordan Harband (ljharb@gmail.com)
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #include "config.h"
24 #include "StringPrototype.h"
25
26 #include "BuiltinNames.h"
27 #include "ButterflyInlines.h"
28 #include "CachedCall.h"
29 #include "Error.h"
30 #include "IntlObject.h"
31 #include "JSArray.h"
32 #include "JSCBuiltins.h"
33 #include "JSCInlines.h"
34 #include "JSFunction.h"
35 #include "JSGlobalObjectFunctions.h"
36 #include "JSStringBuilder.h"
37 #include "JSStringIterator.h"
38 #include "Lookup.h"
39 #include "ObjectPrototype.h"
40 #include "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(vm, 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                 cachedCall.clearArguments();
543                 for (; i < regExp->numSubpatterns() + 1; ++i) {
544                     int matchStart = ovector[i * 2];
545                     int matchLen = ovector[i * 2 + 1] - matchStart;
546
547                     if (matchStart < 0)
548                         cachedCall.appendArgument(jsUndefined());
549                     else
550                         cachedCall.appendArgument(jsSubstring(&vm, source, matchStart, matchLen));
551                 }
552
553                 cachedCall.appendArgument(jsNumber(result.start));
554                 cachedCall.appendArgument(string);
555
556                 cachedCall.setThis(jsUndefined());
557                 JSValue jsResult = cachedCall.call();
558                 replacements.append(jsResult.toWTFString(exec));
559                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
560
561                 lastIndex = result.end;
562                 startPosition = lastIndex;
563
564                 // special case of empty match
565                 if (result.empty()) {
566                     startPosition++;
567                     if (startPosition > sourceLen)
568                         break;
569                 }
570             }
571         } else {
572             while (true) {
573                 int* ovector;
574                 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
575                 if (!result)
576                     break;
577
578                 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
579                     OUT_OF_MEMORY(exec, scope);
580
581                 unsigned i = 0;
582                 cachedCall.clearArguments();
583                 for (; i < regExp->numSubpatterns() + 1; ++i) {
584                     int matchStart = ovector[i * 2];
585                     int matchLen = ovector[i * 2 + 1] - matchStart;
586
587                     if (matchStart < 0)
588                         cachedCall.appendArgument(jsUndefined());
589                     else
590                         cachedCall.appendArgument(jsSubstring(&vm, source, matchStart, matchLen));
591                 }
592
593                 cachedCall.appendArgument(jsNumber(result.start));
594                 cachedCall.appendArgument(string);
595
596                 cachedCall.setThis(jsUndefined());
597                 JSValue jsResult = cachedCall.call();
598                 replacements.append(jsResult.toWTFString(exec));
599                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
600
601                 lastIndex = result.end;
602                 startPosition = lastIndex;
603
604                 // special case of empty match
605                 if (result.empty()) {
606                     startPosition++;
607                     if (startPosition > sourceLen)
608                         break;
609                 }
610             }
611         }
612     } else {
613         do {
614             int* ovector;
615             MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
616             if (!result)
617                 break;
618
619             if (callType != CallType::None) {
620                 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
621                     OUT_OF_MEMORY(exec, scope);
622
623                 MarkedArgumentBuffer args;
624
625                 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
626                     int matchStart = ovector[i * 2];
627                     int matchLen = ovector[i * 2 + 1] - matchStart;
628
629                     if (matchStart < 0)
630                         args.append(jsUndefined());
631                     else
632                         args.append(jsSubstring(exec, source, matchStart, matchLen));
633                 }
634
635                 args.append(jsNumber(result.start));
636                 args.append(string);
637
638                 JSValue replacement = call(exec, replaceValue, callType, callData, jsUndefined(), args);
639                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
640                 String replacementString = replacement.toWTFString(exec);
641                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
642                 replacements.append(replacementString);
643                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
644             } else {
645                 int replLen = replacementString.length();
646                 if (lastIndex < result.start || replLen) {
647                     if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
648                         OUT_OF_MEMORY(exec, scope);
649
650                     if (replLen)
651                         replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
652                     else
653                         replacements.append(String());
654                 }
655             }
656
657             lastIndex = result.end;
658             startPosition = lastIndex;
659
660             // special case of empty match
661             if (result.empty()) {
662                 startPosition++;
663                 if (startPosition > sourceLen)
664                     break;
665             }
666         } while (global);
667     }
668
669     if (!lastIndex && replacements.isEmpty())
670         return JSValue::encode(string);
671
672     if (static_cast<unsigned>(lastIndex) < sourceLen) {
673         if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
674             OUT_OF_MEMORY(exec, scope);
675     }
676     scope.release();
677     return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
678 }
679
680 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
681     ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
682 {
683     VM& vm = exec->vm();
684     NativeCallFrameTracer tracer(&vm, exec);
685     auto scope = DECLARE_THROW_SCOPE(vm);
686
687     RegExp* regExp = searchValue->regExp();
688     if (regExp->global()) {
689         // ES5.1 15.5.4.10 step 8.a.
690         searchValue->setLastIndex(exec, 0);
691         RETURN_IF_EXCEPTION(scope, encodedJSValue());
692         scope.release();
693         return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
694     }
695
696     CallData callData;
697     String replacementString = emptyString();
698     scope.release();
699     return replaceUsingRegExpSearch(
700         vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
701 }
702
703 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
704     ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
705 {
706     VM& vm = exec->vm();
707     NativeCallFrameTracer tracer(&vm, exec);
708     
709     CallData callData;
710     String replacementString = replaceString->value(exec);
711     return replaceUsingRegExpSearch(
712         vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
713 }
714
715 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
716 {
717     auto scope = DECLARE_THROW_SCOPE(vm);
718
719     String replacementString;
720     CallData callData;
721     CallType callType = getCallData(replaceValue, callData);
722     if (callType == CallType::None) {
723         replacementString = replaceValue.toWTFString(exec);
724         RETURN_IF_EXCEPTION(scope, encodedJSValue());
725     }
726
727     scope.release();
728     return replaceUsingRegExpSearch(
729         vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
730 }
731
732 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM& vm, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
733 {
734     auto scope = DECLARE_THROW_SCOPE(vm);
735
736     const String& string = jsString->value(exec);
737     String searchString = searchValue.toWTFString(exec);
738     RETURN_IF_EXCEPTION(scope, encodedJSValue());
739
740     size_t matchStart = string.find(searchString);
741
742     if (matchStart == notFound)
743         return JSValue::encode(jsString);
744
745     CallData callData;
746     CallType callType = getCallData(replaceValue, callData);
747     if (callType != CallType::None) {
748         MarkedArgumentBuffer args;
749         args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
750         args.append(jsNumber(matchStart));
751         args.append(jsString);
752         replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
753         RETURN_IF_EXCEPTION(scope, encodedJSValue());
754     }
755
756     String replaceString = replaceValue.toWTFString(exec);
757     RETURN_IF_EXCEPTION(scope, encodedJSValue());
758
759     StringImpl* stringImpl = string.impl();
760     String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart));
761
762     size_t matchEnd = matchStart + searchString.impl()->length();
763     int ovector[2] = { static_cast<int>(matchStart),  static_cast<int>(matchEnd)};
764     String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
765
766     size_t leftLength = stringImpl->length() - matchEnd;
767     String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength));
768     scope.release();
769     return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
770 }
771
772 static inline bool checkObjectCoercible(JSValue thisValue)
773 {
774     if (thisValue.isString())
775         return true;
776
777     if (thisValue.isUndefinedOrNull())
778         return false;
779
780     if (thisValue.isObject() && asObject(thisValue)->isEnvironmentRecord())
781         return false;
782
783     return true;
784 }
785
786 template <typename CharacterType>
787 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
788 {
789     VM& vm = exec.vm();
790     auto scope = DECLARE_THROW_SCOPE(vm);
791
792     CharacterType* buffer = nullptr;
793     auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
794     if (!impl) {
795         throwOutOfMemoryError(&exec, scope);
796         return nullptr;
797     }
798
799     std::fill_n(buffer, repeatCount, character);
800
801     scope.release();
802     return jsString(&exec, WTFMove(impl));
803 }
804
805 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
806 {
807     VM& vm = exec->vm();
808     auto scope = DECLARE_THROW_SCOPE(vm);
809
810     // For a string which length is single, instead of creating ropes,
811     // allocating a sequential buffer and fill with the repeated string for efficiency.
812     ASSERT(exec->argumentCount() == 2);
813
814     ASSERT(exec->uncheckedArgument(0).isString());
815     JSString* string = asString(exec->uncheckedArgument(0));
816     ASSERT(string->length() == 1);
817
818     JSValue repeatCountValue = exec->uncheckedArgument(1);
819     RELEASE_ASSERT(repeatCountValue.isNumber());
820     int32_t repeatCount;
821     double value = repeatCountValue.asNumber();
822     if (value > JSString::MaxLength)
823         return JSValue::encode(throwOutOfMemoryError(exec, scope));
824     repeatCount = static_cast<int32_t>(value);
825     ASSERT(repeatCount >= 0);
826     ASSERT(!repeatCountValue.isDouble() || repeatCountValue.asDouble() == repeatCount);
827
828     auto viewWithString = string->viewWithUnderlyingString(*exec);
829     StringView view = viewWithString.view;
830     ASSERT(view.length() == 1 && !scope.exception());
831     UChar character = view[0];
832     scope.release();
833     if (!(character & ~0xff))
834         return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
835     return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
836 }
837
838 ALWAYS_INLINE EncodedJSValue replace(
839     VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
840 {
841     if (searchValue.inherits(vm, RegExpObject::info()))
842         return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
843     return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
844 }
845
846 ALWAYS_INLINE EncodedJSValue replace(
847     VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
848 {
849     auto scope = DECLARE_THROW_SCOPE(vm);
850
851     if (!checkObjectCoercible(thisValue))
852         return throwVMTypeError(exec, scope);
853     JSString* string = thisValue.toString(exec);
854     RETURN_IF_EXCEPTION(scope, encodedJSValue());
855     scope.release();
856     return replace(vm, exec, string, searchValue, replaceValue);
857 }
858
859 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
860 {
861     VM& vm = exec->vm();
862     auto scope = DECLARE_THROW_SCOPE(vm);
863
864     JSString* string = exec->thisValue().toString(exec);
865     RETURN_IF_EXCEPTION(scope, encodedJSValue());
866
867     JSValue searchValue = exec->argument(0);
868     if (!searchValue.inherits(vm, RegExpObject::info()))
869         return JSValue::encode(jsUndefined());
870
871     scope.release();
872     return replaceUsingRegExpSearch(vm, exec, string, searchValue, exec->argument(1));
873 }
874
875 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
876 {
877     VM& vm = exec->vm();
878     auto scope = DECLARE_THROW_SCOPE(vm);
879
880     JSString* string = exec->thisValue().toString(exec);
881     RETURN_IF_EXCEPTION(scope, encodedJSValue());
882
883     scope.release();
884     return replaceUsingStringSearch(vm, exec, string, exec->argument(0), exec->argument(1));
885 }
886
887 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
888     ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
889     EncodedJSValue replaceValue)
890 {
891     VM& vm = exec->vm();
892     NativeCallFrameTracer tracer(&vm, exec);
893     
894     return replace(
895         vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
896         JSValue::decode(replaceValue));
897 }
898
899 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
900 {
901     VM& vm = exec->vm();
902     auto scope = DECLARE_THROW_SCOPE(vm);
903
904     JSValue thisValue = exec->thisValue();
905     // Also used for valueOf.
906
907     if (thisValue.isString())
908         return JSValue::encode(thisValue);
909
910     if (thisValue.inherits(vm, StringObject::info()))
911         return JSValue::encode(asStringObject(thisValue)->internalValue());
912
913     return throwVMTypeError(exec, scope);
914 }
915
916 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
917 {
918     VM& vm = exec->vm();
919     auto scope = DECLARE_THROW_SCOPE(vm);
920
921     JSValue thisValue = exec->thisValue();
922     if (!checkObjectCoercible(thisValue))
923         return throwVMTypeError(exec, scope);
924     auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
925     RETURN_IF_EXCEPTION(scope, encodedJSValue());
926     StringView view = viewWithString.view;
927     RETURN_IF_EXCEPTION(scope, encodedJSValue());
928     JSValue a0 = exec->argument(0);
929     if (a0.isUInt32()) {
930         uint32_t i = a0.asUInt32();
931         if (i < view.length())
932             return JSValue::encode(jsSingleCharacterString(exec, view[i]));
933         return JSValue::encode(jsEmptyString(exec));
934     }
935     double dpos = a0.toInteger(exec);
936     if (dpos >= 0 && dpos < view.length())
937         return JSValue::encode(jsSingleCharacterString(exec, view[static_cast<unsigned>(dpos)]));
938     return JSValue::encode(jsEmptyString(exec));
939 }
940
941 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
942 {
943     VM& vm = exec->vm();
944     auto scope = DECLARE_THROW_SCOPE(vm);
945
946     JSValue thisValue = exec->thisValue();
947     if (!checkObjectCoercible(thisValue))
948         return throwVMTypeError(exec, scope);
949     auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
950     RETURN_IF_EXCEPTION(scope, encodedJSValue());
951     StringView view = viewWithString.view;
952     JSValue a0 = exec->argument(0);
953     if (a0.isUInt32()) {
954         uint32_t i = a0.asUInt32();
955         if (i < view.length())
956             return JSValue::encode(jsNumber(view[i]));
957         return JSValue::encode(jsNaN());
958     }
959     double dpos = a0.toInteger(exec);
960     if (dpos >= 0 && dpos < view.length())
961         return JSValue::encode(jsNumber(view[static_cast<int>(dpos)]));
962     return JSValue::encode(jsNaN());
963 }
964
965 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
966 {
967     RELEASE_ASSERT(position < length);
968     if (string.is8Bit())
969         return string.characters8()[position];
970     UChar32 character;
971     U16_NEXT(string.characters16(), position, length, character);
972     return character;
973 }
974
975 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
976 {
977     VM& vm = exec->vm();
978     auto scope = DECLARE_THROW_SCOPE(vm);
979
980     JSValue thisValue = exec->thisValue();
981     if (!checkObjectCoercible(thisValue))
982         return throwVMTypeError(exec, scope);
983
984     String string = thisValue.toWTFString(exec);
985     RETURN_IF_EXCEPTION(scope, encodedJSValue());
986     unsigned length = string.length();
987
988     JSValue argument0 = exec->argument(0);
989     if (argument0.isUInt32()) {
990         unsigned position = argument0.asUInt32();
991         if (position < length)
992             return JSValue::encode(jsNumber(codePointAt(string, position, length)));
993         return JSValue::encode(jsUndefined());
994     }
995
996     RETURN_IF_EXCEPTION(scope, encodedJSValue());
997
998     double doublePosition = argument0.toInteger(exec);
999     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1000     if (doublePosition >= 0 && doublePosition < length)
1001         return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
1002     return JSValue::encode(jsUndefined());
1003 }
1004
1005 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
1006 {
1007     VM& vm = exec->vm();
1008     auto scope = DECLARE_THROW_SCOPE(vm);
1009
1010     JSValue thisValue = exec->thisValue();
1011     if (thisValue.isString() && exec->argumentCount() == 1) {
1012         JSString* str = exec->uncheckedArgument(0).toString(exec);
1013         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1014         scope.release();
1015         return JSValue::encode(jsString(exec, asString(thisValue), str));
1016     }
1017
1018     if (!checkObjectCoercible(thisValue))
1019         return throwVMTypeError(exec, scope);
1020     scope.release();
1021     return JSValue::encode(jsStringFromArguments(exec, thisValue));
1022 }
1023
1024 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1025 {
1026     VM& vm = exec->vm();
1027     auto scope = DECLARE_THROW_SCOPE(vm);
1028
1029     JSValue thisValue = exec->thisValue();
1030     if (!checkObjectCoercible(thisValue))
1031         return throwVMTypeError(exec, scope);
1032
1033     JSValue a0 = exec->argument(0);
1034     JSValue a1 = exec->argument(1);
1035
1036     JSString* thisJSString = thisValue.toString(exec);
1037     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1038     JSString* otherJSString = a0.toString(exec);
1039     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1040
1041     unsigned pos = 0;
1042     if (!a1.isUndefined()) {
1043         int len = thisJSString->length();
1044         RELEASE_ASSERT(len >= 0);
1045         if (a1.isUInt32())
1046             pos = std::min<uint32_t>(a1.asUInt32(), len);
1047         else {
1048             double dpos = a1.toInteger(exec);
1049             if (dpos < 0)
1050                 dpos = 0;
1051             else if (dpos > len)
1052                 dpos = len;
1053             pos = static_cast<unsigned>(dpos);
1054         }
1055     }
1056
1057     if (thisJSString->length() < otherJSString->length() + pos)
1058         return JSValue::encode(jsNumber(-1));
1059
1060     auto thisViewWithString = thisJSString->viewWithUnderlyingString(*exec);
1061     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1062     auto otherViewWithString = otherJSString->viewWithUnderlyingString(*exec);
1063     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1064     size_t result = thisViewWithString.view.find(otherViewWithString.view, pos);
1065     if (result == notFound)
1066         return JSValue::encode(jsNumber(-1));
1067     return JSValue::encode(jsNumber(result));
1068 }
1069
1070 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1071 {
1072     VM& vm = exec->vm();
1073     auto scope = DECLARE_THROW_SCOPE(vm);
1074
1075     JSValue thisValue = exec->thisValue();
1076     if (!checkObjectCoercible(thisValue))
1077         return throwVMTypeError(exec, scope);
1078
1079     JSValue a0 = exec->argument(0);
1080     JSValue a1 = exec->argument(1);
1081
1082     JSString* thisJSString = thisValue.toString(exec);
1083     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1084     unsigned len = thisJSString->length();
1085     JSString* otherJSString = a0.toString(exec);
1086     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1087
1088     double dpos = a1.toIntegerPreserveNaN(exec);
1089     unsigned startPosition;
1090     if (dpos < 0)
1091         startPosition = 0;
1092     else if (!(dpos <= len)) // true for NaN
1093         startPosition = len;
1094     else
1095         startPosition = static_cast<unsigned>(dpos);
1096
1097     if (len < otherJSString->length())
1098         return JSValue::encode(jsNumber(-1));
1099
1100     String thisString = thisJSString->value(exec);
1101     String otherString = otherJSString->value(exec);
1102     size_t result;
1103     if (!startPosition)
1104         result = thisString.startsWith(otherString) ? 0 : notFound;
1105     else
1106         result = thisString.reverseFind(otherString, startPosition);
1107     if (result == notFound)
1108         return JSValue::encode(jsNumber(-1));
1109     return JSValue::encode(jsNumber(result));
1110 }
1111
1112 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1113 {
1114     VM& vm = exec->vm();
1115     auto scope = DECLARE_THROW_SCOPE(vm);
1116
1117     JSValue thisValue = exec->thisValue();
1118     if (!checkObjectCoercible(thisValue))
1119         return throwVMTypeError(exec, scope);
1120     String s = thisValue.toWTFString(exec);
1121     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1122
1123     int len = s.length();
1124     RELEASE_ASSERT(len >= 0);
1125
1126     JSValue a0 = exec->argument(0);
1127     JSValue a1 = exec->argument(1);
1128
1129     // The arg processing is very much like ArrayProtoFunc::Slice
1130     double start = a0.toInteger(exec);
1131     double end = a1.isUndefined() ? len : a1.toInteger(exec);
1132     double from = start < 0 ? len + start : start;
1133     double to = end < 0 ? len + end : end;
1134     if (to > from && to > 0 && from < len) {
1135         if (from < 0)
1136             from = 0;
1137         if (to > len)
1138             to = len;
1139         return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1140     }
1141
1142     return JSValue::encode(jsEmptyString(exec));
1143 }
1144
1145 // Return true in case of early return (resultLength got to limitLength).
1146 template<typename CharacterType>
1147 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)
1148 {
1149     VM& vm = exec->vm();
1150     auto scope = DECLARE_THROW_SCOPE(vm);
1151
1152     // 12. Let q = p.
1153     size_t matchPosition;
1154     const CharacterType* characters = string->characters<CharacterType>();
1155     // 13. Repeat, while q != s
1156     //   a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1157     //   b. If z is failure, then let q = q+1.
1158     //   c. Else, z is not failure
1159     while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1160         // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1161         //    through q (exclusive).
1162         // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1163         //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1164         result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1165         RETURN_IF_EXCEPTION(scope, false);
1166         // 3. Increment lengthA by 1.
1167         // 4. If lengthA == lim, return A.
1168         if (++resultLength == limitLength)
1169             return true;
1170
1171         // 5. Let p = e.
1172         // 8. Let q = p.
1173         position = matchPosition + 1;
1174     }
1175     return false;
1176 }
1177
1178 // ES 21.1.3.17 String.prototype.split(separator, limit)
1179 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1180 {
1181     VM& vm = exec->vm();
1182     auto scope = DECLARE_THROW_SCOPE(vm);
1183     JSValue thisValue = exec->thisValue();
1184     ASSERT(checkObjectCoercible(thisValue));
1185
1186     // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1187     // 7. Let s be the number of characters in S.
1188     String input = thisValue.toWTFString(exec);
1189     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1190     ASSERT(!input.isNull());
1191
1192     // 4. Let A be a new array created as if by the expression new Array()
1193     //    where Array is the standard built-in constructor with that name.
1194     JSArray* result = constructEmptyArray(exec, 0);
1195     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1196
1197     // 5. Let lengthA be 0.
1198     unsigned resultLength = 0;
1199
1200     // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1201     JSValue limitValue = exec->uncheckedArgument(1);
1202     unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1203
1204     // 8. Let p = 0.
1205     size_t position = 0;
1206
1207     // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1208     //    otherwise let R = ToString(separator).
1209     JSValue separatorValue = exec->uncheckedArgument(0);
1210     String separator = separatorValue.toWTFString(exec);
1211     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1212
1213     // 10. If lim == 0, return A.
1214     if (!limit)
1215         return JSValue::encode(result);
1216
1217     // 11. If separator is undefined, then
1218     if (separatorValue.isUndefined()) {
1219         // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1220         scope.release();
1221         result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1222         // b. Return A.
1223         return JSValue::encode(result);
1224     }
1225
1226     // 12. If s == 0, then
1227     if (input.isEmpty()) {
1228         // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1229         // b. If z is not false, return A.
1230         // c. Call CreateDataProperty(A, "0", S).
1231         // d. Return A.
1232         if (!separator.isEmpty()) {
1233             scope.release();
1234             result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1235         }
1236         return JSValue::encode(result);
1237     }
1238
1239     // Optimized case for splitting on the empty string.
1240     if (separator.isEmpty()) {
1241         limit = std::min(limit, input.length());
1242         // Zero limt/input length handled in steps 9/11 respectively, above.
1243         ASSERT(limit);
1244
1245         do {
1246             result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1247             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1248         } while (++position < limit);
1249
1250         return JSValue::encode(result);
1251     }
1252
1253     // 3 cases:
1254     // -separator length == 1, 8 bits
1255     // -separator length == 1, 16 bits
1256     // -separator length > 1
1257     StringImpl* stringImpl = input.impl();
1258     StringImpl* separatorImpl = separator.impl();
1259     size_t separatorLength = separatorImpl->length();
1260
1261     if (separatorLength == 1) {
1262         UChar separatorCharacter;
1263         if (separatorImpl->is8Bit())
1264             separatorCharacter = separatorImpl->characters8()[0];
1265         else
1266             separatorCharacter = separatorImpl->characters16()[0];
1267
1268         if (stringImpl->is8Bit()) {
1269             if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1270                 scope.release();
1271                 return JSValue::encode(result);
1272             }
1273         } else {
1274             if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1275                 scope.release();
1276                 return JSValue::encode(result);
1277             }
1278         }
1279         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1280     } else {
1281         // 13. Let q = p.
1282         size_t matchPosition;
1283         // 14. Repeat, while q != s
1284         //   a. let e be SplitMatch(S, q, R).
1285         //   b. If e is failure, then let q = q+1.
1286         //   c. Else, e is an integer index <= s.
1287         while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1288             // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1289             //    through q (exclusive).
1290             // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1291             result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1292             RETURN_IF_EXCEPTION(scope, encodedJSValue());
1293             // 3. Increment lengthA by 1.
1294             // 4. If lengthA == lim, return A.
1295             if (++resultLength == limit)
1296                 return JSValue::encode(result);
1297
1298             // 5. Let p = e.
1299             // 6. Let q = p.
1300             position = matchPosition + separator.length();
1301         }
1302     }
1303
1304     // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1305     //     through s (exclusive).
1306     // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1307     scope.release();
1308     result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1309
1310     // 17. Return A.
1311     return JSValue::encode(result);
1312 }
1313
1314 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1315 {
1316     VM& vm = exec->vm();
1317     auto scope = DECLARE_THROW_SCOPE(vm);
1318
1319     JSValue thisValue = exec->thisValue();
1320     if (!checkObjectCoercible(thisValue))
1321         return throwVMTypeError(exec, scope);
1322     unsigned len;
1323     JSString* jsString = 0;
1324     String uString;
1325     if (thisValue.isString()) {
1326         jsString = asString(thisValue);
1327         len = jsString->length();
1328     } else {
1329         uString = thisValue.toWTFString(exec);
1330         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1331         len = uString.length();
1332     }
1333
1334     JSValue a0 = exec->argument(0);
1335     JSValue a1 = exec->argument(1);
1336
1337     double start = a0.toInteger(exec);
1338     double length = a1.isUndefined() ? len : a1.toInteger(exec);
1339     if (start >= len || length <= 0)
1340         return JSValue::encode(jsEmptyString(exec));
1341     if (start < 0) {
1342         start += len;
1343         if (start < 0)
1344             start = 0;
1345     }
1346     if (start + length > len)
1347         length = len - start;
1348     unsigned substringStart = static_cast<unsigned>(start);
1349     unsigned substringLength = static_cast<unsigned>(length);
1350     if (jsString)
1351         return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1352     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1353 }
1354
1355 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1356 {
1357     // @substrInternal should not have any observable side effects (e.g. it should not call
1358     // GetMethod(..., @@toPrimitive) on the thisValue).
1359
1360     // It is ok to use the default stringProtoFuncSubstr as the implementation of
1361     // @substrInternal because @substrInternal will only be called by builtins, which will
1362     // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1363     // will not need to call toString() on the thisValue, and there will be no observable
1364     // side-effects.
1365     ASSERT(exec->thisValue().isString());
1366     return stringProtoFuncSubstr(exec);
1367 }
1368
1369 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1370 {
1371     VM& vm = exec->vm();
1372     auto scope = DECLARE_THROW_SCOPE(vm);
1373
1374     JSValue thisValue = exec->thisValue();
1375     if (!checkObjectCoercible(thisValue))
1376         return throwVMTypeError(exec, scope);
1377
1378     JSString* jsString = thisValue.toString(exec);
1379     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1380
1381     JSValue a0 = exec->argument(0);
1382     JSValue a1 = exec->argument(1);
1383     int len = jsString->length();
1384     RELEASE_ASSERT(len >= 0);
1385
1386     double start = a0.toNumber(exec);
1387     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1388     double end;
1389     if (!(start >= 0)) // check for negative values or NaN
1390         start = 0;
1391     else if (start > len)
1392         start = len;
1393     if (a1.isUndefined())
1394         end = len;
1395     else { 
1396         end = a1.toNumber(exec);
1397         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1398         if (!(end >= 0)) // check for negative values or NaN
1399             end = 0;
1400         else if (end > len)
1401             end = len;
1402     }
1403     if (start > end) {
1404         double temp = end;
1405         end = start;
1406         start = temp;
1407     }
1408     unsigned substringStart = static_cast<unsigned>(start);
1409     unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1410     return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1411 }
1412
1413 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1414 {
1415     VM& vm = exec->vm();
1416     auto scope = DECLARE_THROW_SCOPE(vm);
1417
1418     JSValue thisValue = exec->thisValue();
1419     if (!checkObjectCoercible(thisValue))
1420         return throwVMTypeError(exec, scope);
1421     JSString* sVal = thisValue.toString(exec);
1422     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1423     const String& s = sVal->value(exec);
1424     String lowercasedString = s.convertToLowercaseWithoutLocale();
1425     if (lowercasedString.impl() == s.impl())
1426         return JSValue::encode(sVal);
1427     scope.release();
1428     return JSValue::encode(jsString(exec, lowercasedString));
1429 }
1430
1431 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1432 {
1433     VM& vm = exec->vm();
1434     auto scope = DECLARE_THROW_SCOPE(vm);
1435
1436     JSValue thisValue = exec->thisValue();
1437     if (!checkObjectCoercible(thisValue))
1438         return throwVMTypeError(exec, scope);
1439     JSString* sVal = thisValue.toString(exec);
1440     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1441     const String& s = sVal->value(exec);
1442     String uppercasedString = s.convertToUppercaseWithoutLocale();
1443     if (uppercasedString.impl() == s.impl())
1444         return JSValue::encode(sVal);
1445     scope.release();
1446     return JSValue::encode(jsString(exec, uppercasedString));
1447 }
1448
1449 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1450 {
1451     VM& vm = exec->vm();
1452     auto scope = DECLARE_THROW_SCOPE(vm);
1453
1454     JSValue thisValue = exec->thisValue();
1455     if (!checkObjectCoercible(thisValue))
1456         return throwVMTypeError(exec, scope);
1457     String s = thisValue.toWTFString(exec);
1458     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1459
1460     JSValue a0 = exec->argument(0);
1461     String str = a0.toWTFString(exec);
1462     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1463     return JSValue::encode(jsNumber(Collator().collate(s, str)));
1464 }
1465
1466 #if ENABLE(INTL)
1467 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1468 {
1469     VM& vm = state->vm();
1470     auto scope = DECLARE_THROW_SCOPE(vm);
1471
1472     // 1. Let O be RequireObjectCoercible(this value).
1473     JSValue thisValue = state->thisValue();
1474     if (!checkObjectCoercible(thisValue))
1475         return throwVMTypeError(state, scope);
1476
1477     // 2. Let S be ToString(O).
1478     JSString* sVal = thisValue.toString(state);
1479     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1480     const String& s = sVal->value(state);
1481
1482     // 3. ReturnIfAbrupt(S).
1483     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1484
1485     // Optimization for empty strings.
1486     if (s.isEmpty())
1487         return JSValue::encode(sVal);
1488
1489     // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1490     Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1491
1492     // 5. ReturnIfAbrupt(requestedLocales).
1493     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1494
1495     // 6. Let len be the number of elements in requestedLocales.
1496     size_t len = requestedLocales.size();
1497
1498     // 7. If len > 0, then
1499     // a. Let requestedLocale be the first element of requestedLocales.
1500     // 8. Else
1501     // a. Let requestedLocale be DefaultLocale().
1502     String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1503
1504     // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1505     String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1506
1507     // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1508     // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1509     const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1510
1511     // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1512     String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1513
1514     // 12. If locale is undefined, let locale be "und".
1515     if (locale.isNull())
1516         locale = ASCIILiteral("und");
1517
1518     CString utf8LocaleBuffer = locale.utf8();
1519     const StringView view(s);
1520     const int32_t viewLength = view.length();
1521
1522     // Delegate the following steps to icu u_strToLower or u_strToUpper.
1523     // 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.
1524     // 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).
1525     // 15. Let cuList be a new List.
1526     // 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.
1527     // 17. Let L be a String whose elements are, in order, the elements of cuList.
1528
1529     // Most strings lower/upper case will be the same size as original, so try that first.
1530     UErrorCode error(U_ZERO_ERROR);
1531     Vector<UChar> buffer(viewLength);
1532     String lower;
1533     const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1534     if (U_SUCCESS(error))
1535         lower = String(buffer.data(), resultLength);
1536     else if (error == U_BUFFER_OVERFLOW_ERROR) {
1537         // Converted case needs more space than original. Try again.
1538         UErrorCode error(U_ZERO_ERROR);
1539         Vector<UChar> buffer(resultLength);
1540         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1541         if (U_FAILURE(error))
1542             return throwVMTypeError(state, scope, u_errorName(error));
1543         lower = String(buffer.data(), resultLength);
1544     } else
1545         return throwVMTypeError(state, scope, u_errorName(error));
1546
1547     // 18. Return L.
1548     scope.release();
1549     return JSValue::encode(jsString(state, lower));
1550 }
1551
1552 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1553 {
1554     // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1555     // http://ecma-international.org/publications/standards/Ecma-402.htm
1556     return toLocaleCase(state, u_strToLower);
1557 }
1558
1559 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1560 {
1561     // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1562     // http://ecma-international.org/publications/standards/Ecma-402.htm
1563     // 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.
1564     return toLocaleCase(state, u_strToUpper);
1565 }
1566 #endif // ENABLE(INTL)
1567
1568 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1569 {
1570     VM& vm = exec->vm();
1571     auto scope = DECLARE_THROW_SCOPE(vm);
1572
1573     JSValue thisValue = exec->thisValue();
1574     if (!checkObjectCoercible(thisValue))
1575         return throwVMTypeError(exec, scope);
1576     String s = thisValue.toWTFString(exec);
1577     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1578     scope.release();
1579     return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1580 }
1581
1582 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1583 {
1584     VM& vm = exec->vm();
1585     auto scope = DECLARE_THROW_SCOPE(vm);
1586
1587     JSValue thisValue = exec->thisValue();
1588     if (!checkObjectCoercible(thisValue))
1589         return throwVMTypeError(exec, scope);
1590     String s = thisValue.toWTFString(exec);
1591     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1592     scope.release();
1593     return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1594 }
1595
1596 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1597 {
1598     VM& vm = exec->vm();
1599     auto scope = DECLARE_THROW_SCOPE(vm);
1600
1601     JSValue thisValue = exec->thisValue();
1602     if (!checkObjectCoercible(thisValue))
1603         return throwVMTypeError(exec, scope);
1604     String s = thisValue.toWTFString(exec);
1605     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1606     scope.release();
1607     return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1608 }
1609
1610 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1611 {
1612     VM& vm = exec->vm();
1613     auto scope = DECLARE_THROW_SCOPE(vm);
1614
1615     JSValue thisValue = exec->thisValue();
1616     if (!checkObjectCoercible(thisValue))
1617         return throwVMTypeError(exec, scope);
1618     String s = thisValue.toWTFString(exec);
1619     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1620     scope.release();
1621     return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1622 }
1623
1624 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1625 {
1626     VM& vm = exec->vm();
1627     auto scope = DECLARE_THROW_SCOPE(vm);
1628
1629     JSValue thisValue = exec->thisValue();
1630     if (!checkObjectCoercible(thisValue))
1631         return throwVMTypeError(exec, scope);
1632     String s = thisValue.toWTFString(exec);
1633     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1634     scope.release();
1635     return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1636 }
1637
1638 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1639 {
1640     VM& vm = exec->vm();
1641     auto scope = DECLARE_THROW_SCOPE(vm);
1642
1643     JSValue thisValue = exec->thisValue();
1644     if (!checkObjectCoercible(thisValue))
1645         return throwVMTypeError(exec, scope);
1646     String s = thisValue.toWTFString(exec);
1647     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1648     scope.release();
1649     return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1650 }
1651
1652 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1653 {
1654     VM& vm = exec->vm();
1655     auto scope = DECLARE_THROW_SCOPE(vm);
1656
1657     JSValue thisValue = exec->thisValue();
1658     if (!checkObjectCoercible(thisValue))
1659         return throwVMTypeError(exec, scope);
1660     String s = thisValue.toWTFString(exec);
1661     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1662     scope.release();
1663     return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1664 }
1665
1666 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1667 {
1668     VM& vm = exec->vm();
1669     auto scope = DECLARE_THROW_SCOPE(vm);
1670
1671     JSValue thisValue = exec->thisValue();
1672     if (!checkObjectCoercible(thisValue))
1673         return throwVMTypeError(exec, scope);
1674     String s = thisValue.toWTFString(exec);
1675     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1676     scope.release();
1677     return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1678 }
1679
1680 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1681 {
1682     VM& vm = exec->vm();
1683     auto scope = DECLARE_THROW_SCOPE(vm);
1684
1685     JSValue thisValue = exec->thisValue();
1686     if (!checkObjectCoercible(thisValue))
1687         return throwVMTypeError(exec, scope);
1688     String s = thisValue.toWTFString(exec);
1689     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1690     scope.release();
1691     return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1692 }
1693
1694 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1695 {
1696     VM& vm = exec->vm();
1697     auto scope = DECLARE_THROW_SCOPE(vm);
1698
1699     JSValue thisValue = exec->thisValue();
1700     if (!checkObjectCoercible(thisValue))
1701         return throwVMTypeError(exec, scope);
1702     String s = thisValue.toWTFString(exec);
1703     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1704
1705     JSValue a0 = exec->argument(0);
1706     String color = a0.toWTFString(exec);
1707     color.replaceWithLiteral('"', "&quot;");
1708
1709     scope.release();
1710     return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1711 }
1712
1713 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1714 {
1715     VM& vm = exec->vm();
1716     auto scope = DECLARE_THROW_SCOPE(vm);
1717
1718     JSValue thisValue = exec->thisValue();
1719     if (!checkObjectCoercible(thisValue))
1720         return throwVMTypeError(exec, scope);
1721     String s = thisValue.toWTFString(exec);
1722     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1723
1724     JSValue a0 = exec->argument(0);
1725
1726     uint32_t smallInteger;
1727     if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1728         unsigned stringSize = s.length();
1729         unsigned bufferSize = 22 + stringSize;
1730         // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1731         UChar* buffer;
1732         auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1733         if (!impl)
1734             return JSValue::encode(jsUndefined());
1735         buffer[0] = '<';
1736         buffer[1] = 'f';
1737         buffer[2] = 'o';
1738         buffer[3] = 'n';
1739         buffer[4] = 't';
1740         buffer[5] = ' ';
1741         buffer[6] = 's';
1742         buffer[7] = 'i';
1743         buffer[8] = 'z';
1744         buffer[9] = 'e';
1745         buffer[10] = '=';
1746         buffer[11] = '"';
1747         buffer[12] = '0' + smallInteger;
1748         buffer[13] = '"';
1749         buffer[14] = '>';
1750         StringView(s).getCharactersWithUpconvert(&buffer[15]);
1751         buffer[15 + stringSize] = '<';
1752         buffer[16 + stringSize] = '/';
1753         buffer[17 + stringSize] = 'f';
1754         buffer[18 + stringSize] = 'o';
1755         buffer[19 + stringSize] = 'n';
1756         buffer[20 + stringSize] = 't';
1757         buffer[21 + stringSize] = '>';
1758         return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1759     }
1760
1761     String fontSize = a0.toWTFString(exec);
1762     fontSize.replaceWithLiteral('"', "&quot;");
1763
1764     scope.release();
1765     return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1766 }
1767
1768 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1769 {
1770     VM& vm = exec->vm();
1771     auto scope = DECLARE_THROW_SCOPE(vm);
1772
1773     JSValue thisValue = exec->thisValue();
1774     if (!checkObjectCoercible(thisValue))
1775         return throwVMTypeError(exec, scope);
1776     String s = thisValue.toWTFString(exec);
1777     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1778
1779     JSValue a0 = exec->argument(0);
1780     String anchor = a0.toWTFString(exec);
1781     anchor.replaceWithLiteral('"', "&quot;");
1782
1783     scope.release();
1784     return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1785 }
1786
1787 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1788 {
1789     VM& vm = exec->vm();
1790     auto scope = DECLARE_THROW_SCOPE(vm);
1791
1792     JSValue thisValue = exec->thisValue();
1793     if (!checkObjectCoercible(thisValue))
1794         return throwVMTypeError(exec, scope);
1795     String s = thisValue.toWTFString(exec);
1796     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1797
1798     JSValue a0 = exec->argument(0);
1799     String linkText = a0.toWTFString(exec);
1800     linkText.replaceWithLiteral('"', "&quot;");
1801
1802     unsigned linkTextSize = linkText.length();
1803     unsigned stringSize = s.length();
1804     unsigned bufferSize = 15 + linkTextSize + stringSize;
1805     // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1806     UChar* buffer;
1807     auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1808     if (!impl)
1809         return JSValue::encode(jsUndefined());
1810     buffer[0] = '<';
1811     buffer[1] = 'a';
1812     buffer[2] = ' ';
1813     buffer[3] = 'h';
1814     buffer[4] = 'r';
1815     buffer[5] = 'e';
1816     buffer[6] = 'f';
1817     buffer[7] = '=';
1818     buffer[8] = '"';
1819     StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1820     buffer[9 + linkTextSize] = '"';
1821     buffer[10 + linkTextSize] = '>';
1822     StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1823     buffer[11 + linkTextSize + stringSize] = '<';
1824     buffer[12 + linkTextSize + stringSize] = '/';
1825     buffer[13 + linkTextSize + stringSize] = 'a';
1826     buffer[14 + linkTextSize + stringSize] = '>';
1827     return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1828 }
1829
1830 enum {
1831     TrimLeft = 1,
1832     TrimRight = 2
1833 };
1834
1835 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1836 {
1837     VM& vm = exec->vm();
1838     auto scope = DECLARE_THROW_SCOPE(vm);
1839
1840     if (!checkObjectCoercible(thisValue))
1841         return throwTypeError(exec, scope);
1842     String str = thisValue.toWTFString(exec);
1843     RETURN_IF_EXCEPTION(scope, { });
1844
1845     unsigned left = 0;
1846     if (trimKind & TrimLeft) {
1847         while (left < str.length() && isStrWhiteSpace(str[left]))
1848             left++;
1849     }
1850     unsigned right = str.length();
1851     if (trimKind & TrimRight) {
1852         while (right > left && isStrWhiteSpace(str[right - 1]))
1853             right--;
1854     }
1855
1856     // Don't gc allocate a new string if we don't have to.
1857     if (left == 0 && right == str.length() && thisValue.isString())
1858         return thisValue;
1859
1860     scope.release();
1861     return jsString(exec, str.substringSharingImpl(left, right - left));
1862 }
1863
1864 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1865 {
1866     JSValue thisValue = exec->thisValue();
1867     return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1868 }
1869
1870 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1871 {
1872     JSValue thisValue = exec->thisValue();
1873     return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1874 }
1875
1876 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1877 {
1878     JSValue thisValue = exec->thisValue();
1879     return JSValue::encode(trimString(exec, thisValue, TrimRight));
1880 }
1881
1882 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1883 {
1884     if (value < min)
1885         return min;
1886     if (value > max)
1887         return max;
1888     return static_cast<unsigned>(value);
1889 }
1890
1891 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1892 {
1893     VM& vm = exec->vm();
1894     auto scope = DECLARE_THROW_SCOPE(vm);
1895
1896     JSValue thisValue = exec->thisValue();
1897     if (!checkObjectCoercible(thisValue))
1898         return throwVMTypeError(exec, scope);
1899
1900     String stringToSearchIn = thisValue.toWTFString(exec);
1901     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1902
1903     JSValue a0 = exec->argument(0);
1904     bool isRegularExpression = isRegExp(vm, exec, a0);
1905     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1906     if (isRegularExpression)
1907         return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
1908
1909     String searchString = a0.toWTFString(exec);
1910     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1911
1912     JSValue positionArg = exec->argument(1);
1913     unsigned start = 0;
1914     if (positionArg.isInt32())
1915         start = std::max(0, positionArg.asInt32());
1916     else {
1917         unsigned length = stringToSearchIn.length();
1918         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1919         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1920     }
1921
1922     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1923 }
1924
1925 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1926 {
1927     VM& vm = exec->vm();
1928     auto scope = DECLARE_THROW_SCOPE(vm);
1929
1930     JSValue thisValue = exec->thisValue();
1931     if (!checkObjectCoercible(thisValue))
1932         return throwVMTypeError(exec, scope);
1933
1934     String stringToSearchIn = thisValue.toWTFString(exec);
1935     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1936
1937     JSValue a0 = exec->argument(0);
1938     bool isRegularExpression = isRegExp(vm, exec, a0);
1939     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1940     if (isRegularExpression)
1941         return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
1942
1943     String searchString = a0.toWTFString(exec);
1944     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1945
1946     unsigned length = stringToSearchIn.length();
1947
1948     JSValue endPositionArg = exec->argument(1);
1949     unsigned end = length;
1950     if (endPositionArg.isInt32())
1951         end = std::max(0, endPositionArg.asInt32());
1952     else if (!endPositionArg.isUndefined()) {
1953         end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1954         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1955     }
1956
1957     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1958 }
1959
1960 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1961 {
1962     auto scope = DECLARE_THROW_SCOPE(vm);
1963     unsigned start = 0;
1964     if (positionArg.isInt32())
1965         start = std::max(0, positionArg.asInt32());
1966     else {
1967         unsigned length = stringToSearchIn.length();
1968         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1969         RETURN_IF_EXCEPTION(scope, encodedJSValue());
1970     }
1971
1972     return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1973 }
1974
1975 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1976 {
1977     VM& vm = exec->vm();
1978     auto scope = DECLARE_THROW_SCOPE(vm);
1979
1980     JSValue thisValue = exec->thisValue();
1981     if (!checkObjectCoercible(thisValue))
1982         return throwVMTypeError(exec, scope);
1983
1984     String stringToSearchIn = thisValue.toWTFString(exec);
1985     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1986
1987     JSValue a0 = exec->argument(0);
1988     bool isRegularExpression = isRegExp(vm, exec, a0);
1989     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1990     if (isRegularExpression)
1991         return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
1992
1993     String searchString = a0.toWTFString(exec);
1994     RETURN_IF_EXCEPTION(scope, encodedJSValue());
1995
1996     JSValue positionArg = exec->argument(1);
1997
1998     scope.release();
1999     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2000 }
2001
2002 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
2003 {
2004     VM& vm = exec->vm();
2005     auto scope = DECLARE_THROW_SCOPE(vm);
2006
2007     JSValue thisValue = exec->thisValue();
2008     ASSERT(checkObjectCoercible(thisValue));
2009
2010     String stringToSearchIn = thisValue.toWTFString(exec);
2011     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2012
2013     JSValue a0 = exec->uncheckedArgument(0);
2014     String searchString = a0.toWTFString(exec);
2015     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2016
2017     JSValue positionArg = exec->argument(1);
2018
2019     scope.release();
2020     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2021 }
2022
2023 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
2024 {
2025     VM& vm = exec->vm();
2026     auto scope = DECLARE_THROW_SCOPE(vm);
2027
2028     JSValue thisValue = exec->thisValue();
2029     if (!checkObjectCoercible(thisValue))
2030         return throwVMTypeError(exec, scope);
2031     JSString* string = thisValue.toString(exec);
2032     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2033     return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
2034 }
2035
2036 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
2037 {
2038     VM& vm = exec->vm();
2039     auto scope = DECLARE_THROW_SCOPE(vm);
2040
2041     UErrorCode status = U_ZERO_ERROR;
2042     int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
2043
2044     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
2045         // The behavior is not specified when normalize fails.
2046         // Now we throw a type erorr since it seems that the contents of the string are invalid.
2047         return throwTypeError(exec, scope);
2048     }
2049
2050     UChar* buffer = nullptr;
2051     auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
2052     if (!impl)
2053         return throwOutOfMemoryError(exec, scope);
2054
2055     status = U_ZERO_ERROR;
2056     unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
2057     if (U_FAILURE(status))
2058         return throwTypeError(exec, scope);
2059
2060     scope.release();
2061     return jsString(exec, WTFMove(impl));
2062 }
2063
2064 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
2065 {
2066     VM& vm = exec->vm();
2067     auto scope = DECLARE_THROW_SCOPE(vm);
2068
2069     JSValue thisValue = exec->thisValue();
2070     if (!checkObjectCoercible(thisValue))
2071         return throwVMTypeError(exec, scope);
2072     auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
2073     RETURN_IF_EXCEPTION(scope, encodedJSValue());
2074     StringView view = viewWithString.view;
2075
2076     UNormalizationMode form = UNORM_NFC;
2077     // Verify that the argument is provided and is not undefined.
2078     if (!exec->argument(0).isUndefined()) {
2079         String formString = exec->uncheckedArgument(0).toWTFString(exec);
2080         RETURN_IF_EXCEPTION(scope, encodedJSValue());
2081
2082         if (formString == "NFC")
2083             form = UNORM_NFC;
2084         else if (formString == "NFD")
2085             form = UNORM_NFD;
2086         else if (formString == "NFKC")
2087             form = UNORM_NFKC;
2088         else if (formString == "NFKD")
2089             form = UNORM_NFKD;
2090         else
2091             return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2092     }
2093
2094     scope.release();
2095     return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));
2096 }
2097
2098 } // namespace JSC