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