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