2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
4 * Copyright (C) 2009 Torch Mobile, Inc.
5 * Copyright (C) 2015 Jordan Harband (ljharb@gmail.com)
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.
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.
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
24 #include "StringPrototype.h"
26 #include "BuiltinNames.h"
27 #include "ButterflyInlines.h"
28 #include "CachedCall.h"
30 #include "IntlObject.h"
32 #include "JSCBuiltins.h"
33 #include "JSCInlines.h"
34 #include "JSFunction.h"
35 #include "JSGlobalObjectFunctions.h"
36 #include "JSStringBuilder.h"
37 #include "JSStringIterator.h"
39 #include "ObjectPrototype.h"
40 #include "PropertyNameArray.h"
41 #include "RegExpCache.h"
42 #include "RegExpConstructor.h"
43 #include "RegExpObject.h"
44 #include "SuperSampler.h"
46 #include <unicode/uconfig.h>
47 #include <unicode/unorm.h>
48 #include <unicode/ustring.h>
49 #include <wtf/ASCIICType.h>
50 #include <wtf/MathExtras.h>
51 #include <wtf/text/StringBuilder.h>
52 #include <wtf/text/StringView.h>
53 #include <wtf/unicode/Collator.h>
59 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringPrototype);
61 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
62 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
63 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
64 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*);
65 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
66 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
67 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
68 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState*);
69 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState*);
70 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
71 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
72 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
73 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
74 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
75 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
76 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
77 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState*);
78 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
79 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
80 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
81 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
82 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
83 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
84 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
85 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
86 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
87 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
88 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
89 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
90 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
91 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
92 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
93 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
94 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState*);
95 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState*);
96 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*);
97 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState*);
98 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
102 #include "StringPrototype.lut.h"
106 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, CREATE_METHOD_TABLE(StringPrototype) };
108 /* Source for StringConstructor.lut.h
109 @begin stringPrototypeTable
110 match JSBuiltin DontEnum|Function 1
111 padStart JSBuiltin DontEnum|Function 1
112 padEnd JSBuiltin DontEnum|Function 1
113 repeat JSBuiltin DontEnum|Function 1
114 replace JSBuiltin DontEnum|Function 2
115 search JSBuiltin DontEnum|Function 1
116 split JSBuiltin DontEnum|Function 1
121 StringPrototype::StringPrototype(VM& vm, Structure* structure)
122 : StringObject(vm, structure)
126 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
128 Base::finishCreation(vm, nameAndMessage);
129 ASSERT(inherits(vm, info()));
131 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
132 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
133 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
134 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
135 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
136 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", stringProtoFuncConcat, DontEnum, 1);
137 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
138 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
139 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, DontEnum, 2, StringPrototypeReplaceRegExpIntrinsic);
140 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingStringSearchPrivateName(), stringProtoFuncReplaceUsingStringSearch, DontEnum, 2);
141 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
142 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
143 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
144 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, ToLowerCaseIntrinsic);
145 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
147 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);
148 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
149 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, DontEnum, 0);
151 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
152 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
153 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
155 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("big", stringProtoFuncBig, DontEnum, 0);
156 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("small", stringProtoFuncSmall, DontEnum, 0);
157 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("blink", stringProtoFuncBlink, DontEnum, 0);
158 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("bold", stringProtoFuncBold, DontEnum, 0);
159 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fixed", stringProtoFuncFixed, DontEnum, 0);
160 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("italics", stringProtoFuncItalics, DontEnum, 0);
161 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("strike", stringProtoFuncStrike, DontEnum, 0);
162 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sub", stringProtoFuncSub, DontEnum, 0);
163 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sup", stringProtoFuncSup, DontEnum, 0);
164 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
165 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
166 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("anchor", stringProtoFuncAnchor, DontEnum, 1);
167 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("link", stringProtoFuncLink, DontEnum, 1);
168 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trim", stringProtoFuncTrim, DontEnum, 0);
169 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
170 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
171 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("startsWith", stringProtoFuncStartsWith, DontEnum, 1);
172 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("endsWith", stringProtoFuncEndsWith, DontEnum, 1);
173 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("includes", stringProtoFuncIncludes, DontEnum, 1);
174 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("normalize", stringProtoFuncNormalize, DontEnum, 0);
175 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().charCodeAtPrivateName(), stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
177 JSFunction* iteratorFunction = JSFunction::create(vm, globalObject, 0, ASCIILiteral("[Symbol.iterator]"), stringProtoFuncIterator, NoIntrinsic);
178 putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, iteratorFunction, DontEnum);
180 // The constructor will be added later, after StringConstructor has been built
181 putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
184 StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
186 JSString* empty = jsEmptyString(&vm);
187 StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(vm.heap)) StringPrototype(vm, structure);
188 prototype->finishCreation(vm, globalObject, empty);
192 // ------------------------------ Functions --------------------------
194 static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
196 StringBuilder substitutedReplacement;
199 if (i + 1 == replacement.length())
202 UChar ref = replacement[i + 1];
206 substitutedReplacement.append(replacement.substring(offset, i - offset));
215 backrefStart = ovector[0];
216 backrefLength = ovector[1] - backrefStart;
217 } else if (ref == '`') {
219 backrefLength = ovector[0];
220 } else if (ref == '\'') {
221 backrefStart = ovector[1];
222 backrefLength = source.length() - backrefStart;
223 } else if (reg && isASCIIDigit(ref)) {
224 // 1- and 2-digit back references are allowed
225 unsigned backrefIndex = ref - '0';
226 if (backrefIndex > reg->numSubpatterns())
228 if (replacement.length() > i + 2) {
229 ref = replacement[i + 2];
230 if (isASCIIDigit(ref)) {
231 backrefIndex = 10 * backrefIndex + ref - '0';
232 if (backrefIndex > reg->numSubpatterns())
233 backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
240 backrefStart = ovector[2 * backrefIndex];
241 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
246 substitutedReplacement.append(replacement.substring(offset, i - offset));
249 if (backrefStart >= 0)
250 substitutedReplacement.append(source.substring(backrefStart, backrefLength));
251 } while ((i = replacement.find('$', i + 1)) != notFound);
253 if (replacement.length() - offset)
254 substitutedReplacement.append(replacement.substring(offset));
256 return substitutedReplacement.toString();
259 inline String substituteBackreferencesInline(const String& replacement, StringView source, const int* ovector, RegExp* reg)
261 size_t i = replacement.find('$');
262 if (UNLIKELY(i != notFound))
263 return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
268 String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
270 return substituteBackreferencesInline(replacement, source, ovector, reg);
274 StringRange(int pos, int len)
288 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
291 auto scope = DECLARE_THROW_SCOPE(vm);
293 if (rangeCount == 1) {
294 int sourceSize = source.length();
295 int position = substringRanges[0].position;
296 int length = substringRanges[0].length;
297 if (position <= 0 && length >= sourceSize)
299 // 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)));
305 for (int i = 0; i < rangeCount; i++)
306 totalLength += substringRanges[i].length;
309 return jsEmptyString(exec);
311 if (source.is8Bit()) {
313 const LChar* sourceData = source.characters8();
314 auto impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
316 return throwOutOfMemoryError(exec, scope);
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);
327 return jsString(exec, WTFMove(impl));
331 const UChar* sourceData = source.characters16();
333 auto impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
335 return throwOutOfMemoryError(exec, scope);
338 for (int i = 0; i < rangeCount; i++) {
339 if (int srcLen = substringRanges[i].length) {
340 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
346 return jsString(exec, WTFMove(impl));
349 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
352 auto scope = DECLARE_THROW_SCOPE(vm);
354 if (rangeCount == 1 && separatorCount == 0) {
355 int sourceSize = source.length();
356 int position = substringRanges[0].position;
357 int length = substringRanges[0].length;
358 if (position <= 0 && length >= sourceSize)
360 // We could call String::substringSharingImpl(), but this would result in redundant checks.
362 return jsString(exec, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length)));
365 Checked<int, RecordOverflow> totalLength = 0;
366 bool allSeparators8Bit = true;
367 for (int i = 0; i < rangeCount; i++)
368 totalLength += substringRanges[i].length;
369 for (int i = 0; i < separatorCount; i++) {
370 totalLength += separators[i].length();
371 if (separators[i].length() && !separators[i].is8Bit())
372 allSeparators8Bit = false;
374 if (totalLength.hasOverflowed())
375 return throwOutOfMemoryError(exec, scope);
378 return jsEmptyString(exec);
380 if (source.is8Bit() && allSeparators8Bit) {
382 const LChar* sourceData = source.characters8();
384 auto impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
386 return throwOutOfMemoryError(exec, scope);
388 int maxCount = std::max(rangeCount, separatorCount);
390 for (int i = 0; i < maxCount; i++) {
391 if (i < rangeCount) {
392 if (int srcLen = substringRanges[i].length) {
393 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
397 if (i < separatorCount) {
398 if (int sepLen = separators[i].length()) {
399 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
406 return jsString(exec, WTFMove(impl));
410 auto impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
412 return throwOutOfMemoryError(exec, scope);
414 int maxCount = std::max(rangeCount, separatorCount);
416 for (int i = 0; i < maxCount; i++) {
417 if (i < rangeCount) {
418 if (int srcLen = substringRanges[i].length) {
420 StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
422 StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
426 if (i < separatorCount) {
427 if (int sepLen = separators[i].length()) {
428 if (separators[i].is8Bit())
429 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
431 StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
438 return jsString(exec, WTFMove(impl));
441 #define OUT_OF_MEMORY(exec__, scope__) \
443 throwOutOfMemoryError(exec__, scope__); \
444 return encodedJSValue(); \
447 static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
449 auto scope = DECLARE_THROW_SCOPE(vm);
450 SuperSamplerScope superSamplerScope(false);
452 size_t lastIndex = 0;
453 unsigned startPosition = 0;
455 Vector<StringRange, 16> sourceRanges;
456 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
457 unsigned sourceLen = source.length();
460 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition);
464 if (lastIndex < result.start) {
465 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
466 OUT_OF_MEMORY(exec, scope);
468 lastIndex = result.end;
469 startPosition = lastIndex;
471 // special case of empty match
472 if (result.empty()) {
474 if (startPosition > sourceLen)
480 return JSValue::encode(string);
482 if (static_cast<unsigned>(lastIndex) < sourceLen) {
483 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
484 OUT_OF_MEMORY(exec, scope);
487 return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
490 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
491 VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
492 CallType callType, String& replacementString, JSValue replaceValue)
494 auto scope = DECLARE_THROW_SCOPE(vm);
496 const String& source = string->value(exec);
497 unsigned sourceLen = source.length();
498 RETURN_IF_EXCEPTION(scope, encodedJSValue());
499 RegExpObject* regExpObject = asRegExpObject(searchValue);
500 RegExp* regExp = regExpObject->regExp();
501 bool global = regExp->global();
504 // ES5.1 15.5.4.10 step 8.a.
505 regExpObject->setLastIndex(exec, 0);
506 RETURN_IF_EXCEPTION(scope, encodedJSValue());
508 if (callType == CallType::None && !replacementString.length()) {
510 return removeUsingRegExpSearch(vm, exec, string, source, regExp);
514 // FIXME: This is wrong because we may be called directly from the FTL.
515 // https://bugs.webkit.org/show_bug.cgi?id=154874
516 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
518 size_t lastIndex = 0;
519 unsigned startPosition = 0;
521 Vector<StringRange, 16> sourceRanges;
522 Vector<String, 16> replacements;
524 // This is either a loop (if global is set) or a one-way (if not).
525 if (global && callType == CallType::JS) {
526 // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
527 int argCount = regExp->numSubpatterns() + 1 + 2;
528 JSFunction* func = jsCast<JSFunction*>(replaceValue);
529 CachedCall cachedCall(exec, func, argCount);
530 RETURN_IF_EXCEPTION(scope, encodedJSValue());
531 if (source.is8Bit()) {
534 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
538 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
539 OUT_OF_MEMORY(exec, scope);
542 cachedCall.clearArguments();
543 for (; i < regExp->numSubpatterns() + 1; ++i) {
544 int matchStart = ovector[i * 2];
545 int matchLen = ovector[i * 2 + 1] - matchStart;
548 cachedCall.appendArgument(jsUndefined());
550 cachedCall.appendArgument(jsSubstring(&vm, source, matchStart, matchLen));
553 cachedCall.appendArgument(jsNumber(result.start));
554 cachedCall.appendArgument(string);
556 cachedCall.setThis(jsUndefined());
557 JSValue jsResult = cachedCall.call();
558 replacements.append(jsResult.toWTFString(exec));
559 RETURN_IF_EXCEPTION(scope, encodedJSValue());
561 lastIndex = result.end;
562 startPosition = lastIndex;
564 // special case of empty match
565 if (result.empty()) {
567 if (startPosition > sourceLen)
574 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
578 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
579 OUT_OF_MEMORY(exec, scope);
582 cachedCall.clearArguments();
583 for (; i < regExp->numSubpatterns() + 1; ++i) {
584 int matchStart = ovector[i * 2];
585 int matchLen = ovector[i * 2 + 1] - matchStart;
588 cachedCall.appendArgument(jsUndefined());
590 cachedCall.appendArgument(jsSubstring(&vm, source, matchStart, matchLen));
593 cachedCall.appendArgument(jsNumber(result.start));
594 cachedCall.appendArgument(string);
596 cachedCall.setThis(jsUndefined());
597 JSValue jsResult = cachedCall.call();
598 replacements.append(jsResult.toWTFString(exec));
599 RETURN_IF_EXCEPTION(scope, encodedJSValue());
601 lastIndex = result.end;
602 startPosition = lastIndex;
604 // special case of empty match
605 if (result.empty()) {
607 if (startPosition > sourceLen)
615 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
619 if (callType != CallType::None) {
620 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
621 OUT_OF_MEMORY(exec, scope);
623 MarkedArgumentBuffer args;
625 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
626 int matchStart = ovector[i * 2];
627 int matchLen = ovector[i * 2 + 1] - matchStart;
630 args.append(jsUndefined());
632 args.append(jsSubstring(exec, source, matchStart, matchLen));
635 args.append(jsNumber(result.start));
638 JSValue replacement = call(exec, replaceValue, callType, callData, jsUndefined(), args);
639 RETURN_IF_EXCEPTION(scope, encodedJSValue());
640 String replacementString = replacement.toWTFString(exec);
641 RETURN_IF_EXCEPTION(scope, encodedJSValue());
642 replacements.append(replacementString);
643 RETURN_IF_EXCEPTION(scope, encodedJSValue());
645 int replLen = replacementString.length();
646 if (lastIndex < result.start || replLen) {
647 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
648 OUT_OF_MEMORY(exec, scope);
651 replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
653 replacements.append(String());
657 lastIndex = result.end;
658 startPosition = lastIndex;
660 // special case of empty match
661 if (result.empty()) {
663 if (startPosition > sourceLen)
669 if (!lastIndex && replacements.isEmpty())
670 return JSValue::encode(string);
672 if (static_cast<unsigned>(lastIndex) < sourceLen) {
673 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
674 OUT_OF_MEMORY(exec, scope);
677 return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
680 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
681 ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
684 NativeCallFrameTracer tracer(&vm, exec);
685 auto scope = DECLARE_THROW_SCOPE(vm);
687 RegExp* regExp = searchValue->regExp();
688 if (regExp->global()) {
689 // ES5.1 15.5.4.10 step 8.a.
690 searchValue->setLastIndex(exec, 0);
691 RETURN_IF_EXCEPTION(scope, encodedJSValue());
693 return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
697 String replacementString = emptyString();
699 return replaceUsingRegExpSearch(
700 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
703 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
704 ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
707 NativeCallFrameTracer tracer(&vm, exec);
710 String replacementString = replaceString->value(exec);
711 return replaceUsingRegExpSearch(
712 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
715 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
717 auto scope = DECLARE_THROW_SCOPE(vm);
719 String replacementString;
721 CallType callType = getCallData(replaceValue, callData);
722 if (callType == CallType::None) {
723 replacementString = replaceValue.toWTFString(exec);
724 RETURN_IF_EXCEPTION(scope, encodedJSValue());
728 return replaceUsingRegExpSearch(
729 vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
732 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM& vm, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
734 auto scope = DECLARE_THROW_SCOPE(vm);
736 const String& string = jsString->value(exec);
737 String searchString = searchValue.toWTFString(exec);
738 RETURN_IF_EXCEPTION(scope, encodedJSValue());
740 size_t matchStart = string.find(searchString);
742 if (matchStart == notFound)
743 return JSValue::encode(jsString);
746 CallType callType = getCallData(replaceValue, callData);
747 if (callType != CallType::None) {
748 MarkedArgumentBuffer args;
749 args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
750 args.append(jsNumber(matchStart));
751 args.append(jsString);
752 replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
753 RETURN_IF_EXCEPTION(scope, encodedJSValue());
756 String replaceString = replaceValue.toWTFString(exec);
757 RETURN_IF_EXCEPTION(scope, encodedJSValue());
759 StringImpl* stringImpl = string.impl();
760 String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart));
762 size_t matchEnd = matchStart + searchString.impl()->length();
763 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
764 String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
766 size_t leftLength = stringImpl->length() - matchEnd;
767 String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength));
769 return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
772 static inline bool checkObjectCoercible(JSValue thisValue)
774 if (thisValue.isString())
777 if (thisValue.isUndefinedOrNull())
780 if (thisValue.isObject() && asObject(thisValue)->isEnvironmentRecord())
786 template <typename CharacterType>
787 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
790 auto scope = DECLARE_THROW_SCOPE(vm);
792 CharacterType* buffer = nullptr;
793 auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
795 throwOutOfMemoryError(&exec, scope);
799 std::fill_n(buffer, repeatCount, character);
802 return jsString(&exec, WTFMove(impl));
805 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
808 auto scope = DECLARE_THROW_SCOPE(vm);
810 // For a string which length is single, instead of creating ropes,
811 // allocating a sequential buffer and fill with the repeated string for efficiency.
812 ASSERT(exec->argumentCount() == 2);
814 ASSERT(exec->uncheckedArgument(0).isString());
815 JSString* string = asString(exec->uncheckedArgument(0));
816 ASSERT(string->length() == 1);
818 JSValue repeatCountValue = exec->uncheckedArgument(1);
819 RELEASE_ASSERT(repeatCountValue.isNumber());
821 double value = repeatCountValue.asNumber();
822 if (value > JSString::MaxLength)
823 return JSValue::encode(throwOutOfMemoryError(exec, scope));
824 repeatCount = static_cast<int32_t>(value);
825 ASSERT(repeatCount >= 0);
826 ASSERT(!repeatCountValue.isDouble() || repeatCountValue.asDouble() == repeatCount);
828 auto viewWithString = string->viewWithUnderlyingString(*exec);
829 StringView view = viewWithString.view;
830 ASSERT(view.length() == 1 && !scope.exception());
831 UChar character = view[0];
833 if (!(character & ~0xff))
834 return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
835 return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
838 ALWAYS_INLINE EncodedJSValue replace(
839 VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
841 if (searchValue.inherits(vm, RegExpObject::info()))
842 return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
843 return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
846 ALWAYS_INLINE EncodedJSValue replace(
847 VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
849 auto scope = DECLARE_THROW_SCOPE(vm);
851 if (!checkObjectCoercible(thisValue))
852 return throwVMTypeError(exec, scope);
853 JSString* string = thisValue.toString(exec);
854 RETURN_IF_EXCEPTION(scope, encodedJSValue());
856 return replace(vm, exec, string, searchValue, replaceValue);
859 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
862 auto scope = DECLARE_THROW_SCOPE(vm);
864 JSString* string = exec->thisValue().toString(exec);
865 RETURN_IF_EXCEPTION(scope, encodedJSValue());
867 JSValue searchValue = exec->argument(0);
868 if (!searchValue.inherits(vm, RegExpObject::info()))
869 return JSValue::encode(jsUndefined());
872 return replaceUsingRegExpSearch(vm, exec, string, searchValue, exec->argument(1));
875 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
878 auto scope = DECLARE_THROW_SCOPE(vm);
880 JSString* string = exec->thisValue().toString(exec);
881 RETURN_IF_EXCEPTION(scope, encodedJSValue());
884 return replaceUsingStringSearch(vm, exec, string, exec->argument(0), exec->argument(1));
887 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
888 ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
889 EncodedJSValue replaceValue)
892 NativeCallFrameTracer tracer(&vm, exec);
895 vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
896 JSValue::decode(replaceValue));
899 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
902 auto scope = DECLARE_THROW_SCOPE(vm);
904 JSValue thisValue = exec->thisValue();
905 // Also used for valueOf.
907 if (thisValue.isString())
908 return JSValue::encode(thisValue);
910 if (thisValue.inherits(vm, StringObject::info()))
911 return JSValue::encode(asStringObject(thisValue)->internalValue());
913 return throwVMTypeError(exec, scope);
916 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
919 auto scope = DECLARE_THROW_SCOPE(vm);
921 JSValue thisValue = exec->thisValue();
922 if (!checkObjectCoercible(thisValue))
923 return throwVMTypeError(exec, scope);
924 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
925 RETURN_IF_EXCEPTION(scope, encodedJSValue());
926 StringView view = viewWithString.view;
927 RETURN_IF_EXCEPTION(scope, encodedJSValue());
928 JSValue a0 = exec->argument(0);
930 uint32_t i = a0.asUInt32();
931 if (i < view.length())
932 return JSValue::encode(jsSingleCharacterString(exec, view[i]));
933 return JSValue::encode(jsEmptyString(exec));
935 double dpos = a0.toInteger(exec);
936 if (dpos >= 0 && dpos < view.length())
937 return JSValue::encode(jsSingleCharacterString(exec, view[static_cast<unsigned>(dpos)]));
938 return JSValue::encode(jsEmptyString(exec));
941 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
944 auto scope = DECLARE_THROW_SCOPE(vm);
946 JSValue thisValue = exec->thisValue();
947 if (!checkObjectCoercible(thisValue))
948 return throwVMTypeError(exec, scope);
949 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
950 RETURN_IF_EXCEPTION(scope, encodedJSValue());
951 StringView view = viewWithString.view;
952 JSValue a0 = exec->argument(0);
954 uint32_t i = a0.asUInt32();
955 if (i < view.length())
956 return JSValue::encode(jsNumber(view[i]));
957 return JSValue::encode(jsNaN());
959 double dpos = a0.toInteger(exec);
960 if (dpos >= 0 && dpos < view.length())
961 return JSValue::encode(jsNumber(view[static_cast<int>(dpos)]));
962 return JSValue::encode(jsNaN());
965 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
967 RELEASE_ASSERT(position < length);
969 return string.characters8()[position];
971 U16_NEXT(string.characters16(), position, length, character);
975 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
978 auto scope = DECLARE_THROW_SCOPE(vm);
980 JSValue thisValue = exec->thisValue();
981 if (!checkObjectCoercible(thisValue))
982 return throwVMTypeError(exec, scope);
984 String string = thisValue.toWTFString(exec);
985 RETURN_IF_EXCEPTION(scope, encodedJSValue());
986 unsigned length = string.length();
988 JSValue argument0 = exec->argument(0);
989 if (argument0.isUInt32()) {
990 unsigned position = argument0.asUInt32();
991 if (position < length)
992 return JSValue::encode(jsNumber(codePointAt(string, position, length)));
993 return JSValue::encode(jsUndefined());
996 RETURN_IF_EXCEPTION(scope, encodedJSValue());
998 double doublePosition = argument0.toInteger(exec);
999 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1000 if (doublePosition >= 0 && doublePosition < length)
1001 return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
1002 return JSValue::encode(jsUndefined());
1005 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
1007 VM& vm = exec->vm();
1008 auto scope = DECLARE_THROW_SCOPE(vm);
1010 JSValue thisValue = exec->thisValue();
1011 if (thisValue.isString() && exec->argumentCount() == 1) {
1012 JSString* str = exec->uncheckedArgument(0).toString(exec);
1013 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1015 return JSValue::encode(jsString(exec, asString(thisValue), str));
1018 if (!checkObjectCoercible(thisValue))
1019 return throwVMTypeError(exec, scope);
1021 return JSValue::encode(jsStringFromArguments(exec, thisValue));
1024 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1026 VM& vm = exec->vm();
1027 auto scope = DECLARE_THROW_SCOPE(vm);
1029 JSValue thisValue = exec->thisValue();
1030 if (!checkObjectCoercible(thisValue))
1031 return throwVMTypeError(exec, scope);
1033 JSValue a0 = exec->argument(0);
1034 JSValue a1 = exec->argument(1);
1036 JSString* thisJSString = thisValue.toString(exec);
1037 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1038 JSString* otherJSString = a0.toString(exec);
1039 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1042 if (!a1.isUndefined()) {
1043 int len = thisJSString->length();
1044 RELEASE_ASSERT(len >= 0);
1046 pos = std::min<uint32_t>(a1.asUInt32(), len);
1048 double dpos = a1.toInteger(exec);
1051 else if (dpos > len)
1053 pos = static_cast<unsigned>(dpos);
1057 if (thisJSString->length() < otherJSString->length() + pos)
1058 return JSValue::encode(jsNumber(-1));
1060 auto thisViewWithString = thisJSString->viewWithUnderlyingString(*exec);
1061 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1062 auto otherViewWithString = otherJSString->viewWithUnderlyingString(*exec);
1063 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1064 size_t result = thisViewWithString.view.find(otherViewWithString.view, pos);
1065 if (result == notFound)
1066 return JSValue::encode(jsNumber(-1));
1067 return JSValue::encode(jsNumber(result));
1070 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1072 VM& vm = exec->vm();
1073 auto scope = DECLARE_THROW_SCOPE(vm);
1075 JSValue thisValue = exec->thisValue();
1076 if (!checkObjectCoercible(thisValue))
1077 return throwVMTypeError(exec, scope);
1079 JSValue a0 = exec->argument(0);
1080 JSValue a1 = exec->argument(1);
1082 JSString* thisJSString = thisValue.toString(exec);
1083 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1084 unsigned len = thisJSString->length();
1085 JSString* otherJSString = a0.toString(exec);
1086 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1088 double dpos = a1.toIntegerPreserveNaN(exec);
1089 unsigned startPosition;
1092 else if (!(dpos <= len)) // true for NaN
1093 startPosition = len;
1095 startPosition = static_cast<unsigned>(dpos);
1097 if (len < otherJSString->length())
1098 return JSValue::encode(jsNumber(-1));
1100 String thisString = thisJSString->value(exec);
1101 String otherString = otherJSString->value(exec);
1104 result = thisString.startsWith(otherString) ? 0 : notFound;
1106 result = thisString.reverseFind(otherString, startPosition);
1107 if (result == notFound)
1108 return JSValue::encode(jsNumber(-1));
1109 return JSValue::encode(jsNumber(result));
1112 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1114 VM& vm = exec->vm();
1115 auto scope = DECLARE_THROW_SCOPE(vm);
1117 JSValue thisValue = exec->thisValue();
1118 if (!checkObjectCoercible(thisValue))
1119 return throwVMTypeError(exec, scope);
1120 String s = thisValue.toWTFString(exec);
1121 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1123 int len = s.length();
1124 RELEASE_ASSERT(len >= 0);
1126 JSValue a0 = exec->argument(0);
1127 JSValue a1 = exec->argument(1);
1129 // The arg processing is very much like ArrayProtoFunc::Slice
1130 double start = a0.toInteger(exec);
1131 double end = a1.isUndefined() ? len : a1.toInteger(exec);
1132 double from = start < 0 ? len + start : start;
1133 double to = end < 0 ? len + end : end;
1134 if (to > from && to > 0 && from < len) {
1139 return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1142 return JSValue::encode(jsEmptyString(exec));
1145 // Return true in case of early return (resultLength got to limitLength).
1146 template<typename CharacterType>
1147 static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, JSValue originalValue, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength)
1149 VM& vm = exec->vm();
1150 auto scope = DECLARE_THROW_SCOPE(vm);
1153 size_t matchPosition;
1154 const CharacterType* characters = string->characters<CharacterType>();
1155 // 13. Repeat, while q != s
1156 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1157 // b. If z is failure, then let q = q+1.
1158 // c. Else, z is not failure
1159 while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1160 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1161 // through q (exclusive).
1162 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1163 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1164 result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1165 RETURN_IF_EXCEPTION(scope, false);
1166 // 3. Increment lengthA by 1.
1167 // 4. If lengthA == lim, return A.
1168 if (++resultLength == limitLength)
1173 position = matchPosition + 1;
1178 // ES 21.1.3.17 String.prototype.split(separator, limit)
1179 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1181 VM& vm = exec->vm();
1182 auto scope = DECLARE_THROW_SCOPE(vm);
1183 JSValue thisValue = exec->thisValue();
1184 ASSERT(checkObjectCoercible(thisValue));
1186 // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1187 // 7. Let s be the number of characters in S.
1188 String input = thisValue.toWTFString(exec);
1189 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1190 ASSERT(!input.isNull());
1192 // 4. Let A be a new array created as if by the expression new Array()
1193 // where Array is the standard built-in constructor with that name.
1194 JSArray* result = constructEmptyArray(exec, 0);
1195 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1197 // 5. Let lengthA be 0.
1198 unsigned resultLength = 0;
1200 // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1201 JSValue limitValue = exec->uncheckedArgument(1);
1202 unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1205 size_t position = 0;
1207 // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1208 // otherwise let R = ToString(separator).
1209 JSValue separatorValue = exec->uncheckedArgument(0);
1210 String separator = separatorValue.toWTFString(exec);
1211 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1213 // 10. If lim == 0, return A.
1215 return JSValue::encode(result);
1217 // 11. If separator is undefined, then
1218 if (separatorValue.isUndefined()) {
1219 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1221 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1223 return JSValue::encode(result);
1226 // 12. If s == 0, then
1227 if (input.isEmpty()) {
1228 // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1229 // b. If z is not false, return A.
1230 // c. Call CreateDataProperty(A, "0", S).
1232 if (!separator.isEmpty()) {
1234 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1236 return JSValue::encode(result);
1239 // Optimized case for splitting on the empty string.
1240 if (separator.isEmpty()) {
1241 limit = std::min(limit, input.length());
1242 // Zero limt/input length handled in steps 9/11 respectively, above.
1246 result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1247 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1248 } while (++position < limit);
1250 return JSValue::encode(result);
1254 // -separator length == 1, 8 bits
1255 // -separator length == 1, 16 bits
1256 // -separator length > 1
1257 StringImpl* stringImpl = input.impl();
1258 StringImpl* separatorImpl = separator.impl();
1259 size_t separatorLength = separatorImpl->length();
1261 if (separatorLength == 1) {
1262 UChar separatorCharacter;
1263 if (separatorImpl->is8Bit())
1264 separatorCharacter = separatorImpl->characters8()[0];
1266 separatorCharacter = separatorImpl->characters16()[0];
1268 if (stringImpl->is8Bit()) {
1269 if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1271 return JSValue::encode(result);
1274 if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1276 return JSValue::encode(result);
1279 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1282 size_t matchPosition;
1283 // 14. Repeat, while q != s
1284 // a. let e be SplitMatch(S, q, R).
1285 // b. If e is failure, then let q = q+1.
1286 // c. Else, e is an integer index <= s.
1287 while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1288 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1289 // through q (exclusive).
1290 // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1291 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1292 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1293 // 3. Increment lengthA by 1.
1294 // 4. If lengthA == lim, return A.
1295 if (++resultLength == limit)
1296 return JSValue::encode(result);
1300 position = matchPosition + separator.length();
1304 // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1305 // through s (exclusive).
1306 // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1308 result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1311 return JSValue::encode(result);
1314 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1316 VM& vm = exec->vm();
1317 auto scope = DECLARE_THROW_SCOPE(vm);
1319 JSValue thisValue = exec->thisValue();
1320 if (!checkObjectCoercible(thisValue))
1321 return throwVMTypeError(exec, scope);
1323 JSString* jsString = 0;
1325 if (thisValue.isString()) {
1326 jsString = asString(thisValue);
1327 len = jsString->length();
1329 uString = thisValue.toWTFString(exec);
1330 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1331 len = uString.length();
1334 JSValue a0 = exec->argument(0);
1335 JSValue a1 = exec->argument(1);
1337 double start = a0.toInteger(exec);
1338 double length = a1.isUndefined() ? len : a1.toInteger(exec);
1339 if (start >= len || length <= 0)
1340 return JSValue::encode(jsEmptyString(exec));
1346 if (start + length > len)
1347 length = len - start;
1348 unsigned substringStart = static_cast<unsigned>(start);
1349 unsigned substringLength = static_cast<unsigned>(length);
1351 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1352 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1355 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1357 // @substrInternal should not have any observable side effects (e.g. it should not call
1358 // GetMethod(..., @@toPrimitive) on the thisValue).
1360 // It is ok to use the default stringProtoFuncSubstr as the implementation of
1361 // @substrInternal because @substrInternal will only be called by builtins, which will
1362 // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1363 // will not need to call toString() on the thisValue, and there will be no observable
1365 ASSERT(exec->thisValue().isString());
1366 return stringProtoFuncSubstr(exec);
1369 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1371 VM& vm = exec->vm();
1372 auto scope = DECLARE_THROW_SCOPE(vm);
1374 JSValue thisValue = exec->thisValue();
1375 if (!checkObjectCoercible(thisValue))
1376 return throwVMTypeError(exec, scope);
1378 JSString* jsString = thisValue.toString(exec);
1379 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1381 JSValue a0 = exec->argument(0);
1382 JSValue a1 = exec->argument(1);
1383 int len = jsString->length();
1384 RELEASE_ASSERT(len >= 0);
1386 double start = a0.toNumber(exec);
1387 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1389 if (!(start >= 0)) // check for negative values or NaN
1391 else if (start > len)
1393 if (a1.isUndefined())
1396 end = a1.toNumber(exec);
1397 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1398 if (!(end >= 0)) // check for negative values or NaN
1408 unsigned substringStart = static_cast<unsigned>(start);
1409 unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1410 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1413 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1415 VM& vm = exec->vm();
1416 auto scope = DECLARE_THROW_SCOPE(vm);
1418 JSValue thisValue = exec->thisValue();
1419 if (!checkObjectCoercible(thisValue))
1420 return throwVMTypeError(exec, scope);
1421 JSString* sVal = thisValue.toString(exec);
1422 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1423 const String& s = sVal->value(exec);
1424 String lowercasedString = s.convertToLowercaseWithoutLocale();
1425 if (lowercasedString.impl() == s.impl())
1426 return JSValue::encode(sVal);
1428 return JSValue::encode(jsString(exec, lowercasedString));
1431 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1433 VM& vm = exec->vm();
1434 auto scope = DECLARE_THROW_SCOPE(vm);
1436 JSValue thisValue = exec->thisValue();
1437 if (!checkObjectCoercible(thisValue))
1438 return throwVMTypeError(exec, scope);
1439 JSString* sVal = thisValue.toString(exec);
1440 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1441 const String& s = sVal->value(exec);
1442 String uppercasedString = s.convertToUppercaseWithoutLocale();
1443 if (uppercasedString.impl() == s.impl())
1444 return JSValue::encode(sVal);
1446 return JSValue::encode(jsString(exec, uppercasedString));
1449 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1451 VM& vm = exec->vm();
1452 auto scope = DECLARE_THROW_SCOPE(vm);
1454 JSValue thisValue = exec->thisValue();
1455 if (!checkObjectCoercible(thisValue))
1456 return throwVMTypeError(exec, scope);
1457 String s = thisValue.toWTFString(exec);
1458 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1460 JSValue a0 = exec->argument(0);
1461 String str = a0.toWTFString(exec);
1462 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1463 return JSValue::encode(jsNumber(Collator().collate(s, str)));
1467 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1469 VM& vm = state->vm();
1470 auto scope = DECLARE_THROW_SCOPE(vm);
1472 // 1. Let O be RequireObjectCoercible(this value).
1473 JSValue thisValue = state->thisValue();
1474 if (!checkObjectCoercible(thisValue))
1475 return throwVMTypeError(state, scope);
1477 // 2. Let S be ToString(O).
1478 JSString* sVal = thisValue.toString(state);
1479 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1480 const String& s = sVal->value(state);
1482 // 3. ReturnIfAbrupt(S).
1483 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1485 // Optimization for empty strings.
1487 return JSValue::encode(sVal);
1489 // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1490 Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1492 // 5. ReturnIfAbrupt(requestedLocales).
1493 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1495 // 6. Let len be the number of elements in requestedLocales.
1496 size_t len = requestedLocales.size();
1498 // 7. If len > 0, then
1499 // a. Let requestedLocale be the first element of requestedLocales.
1501 // a. Let requestedLocale be DefaultLocale().
1502 String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1504 // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1505 String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1507 // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1508 // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1509 const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1511 // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1512 String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1514 // 12. If locale is undefined, let locale be "und".
1515 if (locale.isNull())
1516 locale = ASCIILiteral("und");
1518 CString utf8LocaleBuffer = locale.utf8();
1519 const StringView view(s);
1520 const int32_t viewLength = view.length();
1522 // Delegate the following steps to icu u_strToLower or u_strToUpper.
1523 // 13. Let cpList be a List containing in order the code points of S as defined in ES2015, 6.1.4, starting at the first element of S.
1524 // 14. For each code point c in cpList, if the Unicode Character Database provides a lower(/upper) case equivalent of c that is either language insensitive or for the language locale, then replace c in cpList with that/those equivalent code point(s).
1525 // 15. Let cuList be a new List.
1526 // 16. For each code point c in cpList, in order, append to cuList the elements of the UTF-16 Encoding (defined in ES2015, 6.1.4) of c.
1527 // 17. Let L be a String whose elements are, in order, the elements of cuList.
1529 // Most strings lower/upper case will be the same size as original, so try that first.
1530 UErrorCode error(U_ZERO_ERROR);
1531 Vector<UChar> buffer(viewLength);
1533 const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1534 if (U_SUCCESS(error))
1535 lower = String(buffer.data(), resultLength);
1536 else if (error == U_BUFFER_OVERFLOW_ERROR) {
1537 // Converted case needs more space than original. Try again.
1538 UErrorCode error(U_ZERO_ERROR);
1539 Vector<UChar> buffer(resultLength);
1540 convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1541 if (U_FAILURE(error))
1542 return throwVMTypeError(state, scope, u_errorName(error));
1543 lower = String(buffer.data(), resultLength);
1545 return throwVMTypeError(state, scope, u_errorName(error));
1549 return JSValue::encode(jsString(state, lower));
1552 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1554 // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1555 // http://ecma-international.org/publications/standards/Ecma-402.htm
1556 return toLocaleCase(state, u_strToLower);
1559 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1561 // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1562 // http://ecma-international.org/publications/standards/Ecma-402.htm
1563 // This function interprets a string value as a sequence of code points, as described in ES2015, 6.1.4. This function behaves in exactly the same way as String.prototype.toLocaleLowerCase, except that characters are mapped to their uppercase equivalents as specified in the Unicode character database.
1564 return toLocaleCase(state, u_strToUpper);
1566 #endif // ENABLE(INTL)
1568 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1570 VM& vm = exec->vm();
1571 auto scope = DECLARE_THROW_SCOPE(vm);
1573 JSValue thisValue = exec->thisValue();
1574 if (!checkObjectCoercible(thisValue))
1575 return throwVMTypeError(exec, scope);
1576 String s = thisValue.toWTFString(exec);
1577 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1579 return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1582 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1584 VM& vm = exec->vm();
1585 auto scope = DECLARE_THROW_SCOPE(vm);
1587 JSValue thisValue = exec->thisValue();
1588 if (!checkObjectCoercible(thisValue))
1589 return throwVMTypeError(exec, scope);
1590 String s = thisValue.toWTFString(exec);
1591 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1593 return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1596 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1598 VM& vm = exec->vm();
1599 auto scope = DECLARE_THROW_SCOPE(vm);
1601 JSValue thisValue = exec->thisValue();
1602 if (!checkObjectCoercible(thisValue))
1603 return throwVMTypeError(exec, scope);
1604 String s = thisValue.toWTFString(exec);
1605 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1607 return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1610 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1612 VM& vm = exec->vm();
1613 auto scope = DECLARE_THROW_SCOPE(vm);
1615 JSValue thisValue = exec->thisValue();
1616 if (!checkObjectCoercible(thisValue))
1617 return throwVMTypeError(exec, scope);
1618 String s = thisValue.toWTFString(exec);
1619 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1621 return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1624 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1626 VM& vm = exec->vm();
1627 auto scope = DECLARE_THROW_SCOPE(vm);
1629 JSValue thisValue = exec->thisValue();
1630 if (!checkObjectCoercible(thisValue))
1631 return throwVMTypeError(exec, scope);
1632 String s = thisValue.toWTFString(exec);
1633 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1635 return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1638 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1640 VM& vm = exec->vm();
1641 auto scope = DECLARE_THROW_SCOPE(vm);
1643 JSValue thisValue = exec->thisValue();
1644 if (!checkObjectCoercible(thisValue))
1645 return throwVMTypeError(exec, scope);
1646 String s = thisValue.toWTFString(exec);
1647 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1649 return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1652 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1654 VM& vm = exec->vm();
1655 auto scope = DECLARE_THROW_SCOPE(vm);
1657 JSValue thisValue = exec->thisValue();
1658 if (!checkObjectCoercible(thisValue))
1659 return throwVMTypeError(exec, scope);
1660 String s = thisValue.toWTFString(exec);
1661 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1663 return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1666 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1668 VM& vm = exec->vm();
1669 auto scope = DECLARE_THROW_SCOPE(vm);
1671 JSValue thisValue = exec->thisValue();
1672 if (!checkObjectCoercible(thisValue))
1673 return throwVMTypeError(exec, scope);
1674 String s = thisValue.toWTFString(exec);
1675 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1677 return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1680 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1682 VM& vm = exec->vm();
1683 auto scope = DECLARE_THROW_SCOPE(vm);
1685 JSValue thisValue = exec->thisValue();
1686 if (!checkObjectCoercible(thisValue))
1687 return throwVMTypeError(exec, scope);
1688 String s = thisValue.toWTFString(exec);
1689 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1691 return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1694 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1696 VM& vm = exec->vm();
1697 auto scope = DECLARE_THROW_SCOPE(vm);
1699 JSValue thisValue = exec->thisValue();
1700 if (!checkObjectCoercible(thisValue))
1701 return throwVMTypeError(exec, scope);
1702 String s = thisValue.toWTFString(exec);
1703 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1705 JSValue a0 = exec->argument(0);
1706 String color = a0.toWTFString(exec);
1707 color.replaceWithLiteral('"', """);
1710 return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1713 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1715 VM& vm = exec->vm();
1716 auto scope = DECLARE_THROW_SCOPE(vm);
1718 JSValue thisValue = exec->thisValue();
1719 if (!checkObjectCoercible(thisValue))
1720 return throwVMTypeError(exec, scope);
1721 String s = thisValue.toWTFString(exec);
1722 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1724 JSValue a0 = exec->argument(0);
1726 uint32_t smallInteger;
1727 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1728 unsigned stringSize = s.length();
1729 unsigned bufferSize = 22 + stringSize;
1730 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1732 auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1734 return JSValue::encode(jsUndefined());
1747 buffer[12] = '0' + smallInteger;
1750 StringView(s).getCharactersWithUpconvert(&buffer[15]);
1751 buffer[15 + stringSize] = '<';
1752 buffer[16 + stringSize] = '/';
1753 buffer[17 + stringSize] = 'f';
1754 buffer[18 + stringSize] = 'o';
1755 buffer[19 + stringSize] = 'n';
1756 buffer[20 + stringSize] = 't';
1757 buffer[21 + stringSize] = '>';
1758 return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1761 String fontSize = a0.toWTFString(exec);
1762 fontSize.replaceWithLiteral('"', """);
1765 return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1768 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1770 VM& vm = exec->vm();
1771 auto scope = DECLARE_THROW_SCOPE(vm);
1773 JSValue thisValue = exec->thisValue();
1774 if (!checkObjectCoercible(thisValue))
1775 return throwVMTypeError(exec, scope);
1776 String s = thisValue.toWTFString(exec);
1777 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1779 JSValue a0 = exec->argument(0);
1780 String anchor = a0.toWTFString(exec);
1781 anchor.replaceWithLiteral('"', """);
1784 return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1787 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1789 VM& vm = exec->vm();
1790 auto scope = DECLARE_THROW_SCOPE(vm);
1792 JSValue thisValue = exec->thisValue();
1793 if (!checkObjectCoercible(thisValue))
1794 return throwVMTypeError(exec, scope);
1795 String s = thisValue.toWTFString(exec);
1796 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1798 JSValue a0 = exec->argument(0);
1799 String linkText = a0.toWTFString(exec);
1800 linkText.replaceWithLiteral('"', """);
1802 unsigned linkTextSize = linkText.length();
1803 unsigned stringSize = s.length();
1804 unsigned bufferSize = 15 + linkTextSize + stringSize;
1805 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1807 auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1809 return JSValue::encode(jsUndefined());
1819 StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1820 buffer[9 + linkTextSize] = '"';
1821 buffer[10 + linkTextSize] = '>';
1822 StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1823 buffer[11 + linkTextSize + stringSize] = '<';
1824 buffer[12 + linkTextSize + stringSize] = '/';
1825 buffer[13 + linkTextSize + stringSize] = 'a';
1826 buffer[14 + linkTextSize + stringSize] = '>';
1827 return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1835 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1837 VM& vm = exec->vm();
1838 auto scope = DECLARE_THROW_SCOPE(vm);
1840 if (!checkObjectCoercible(thisValue))
1841 return throwTypeError(exec, scope);
1842 String str = thisValue.toWTFString(exec);
1843 RETURN_IF_EXCEPTION(scope, { });
1846 if (trimKind & TrimLeft) {
1847 while (left < str.length() && isStrWhiteSpace(str[left]))
1850 unsigned right = str.length();
1851 if (trimKind & TrimRight) {
1852 while (right > left && isStrWhiteSpace(str[right - 1]))
1856 // Don't gc allocate a new string if we don't have to.
1857 if (left == 0 && right == str.length() && thisValue.isString())
1861 return jsString(exec, str.substringSharingImpl(left, right - left));
1864 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1866 JSValue thisValue = exec->thisValue();
1867 return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1870 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1872 JSValue thisValue = exec->thisValue();
1873 return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1876 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1878 JSValue thisValue = exec->thisValue();
1879 return JSValue::encode(trimString(exec, thisValue, TrimRight));
1882 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1888 return static_cast<unsigned>(value);
1891 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1893 VM& vm = exec->vm();
1894 auto scope = DECLARE_THROW_SCOPE(vm);
1896 JSValue thisValue = exec->thisValue();
1897 if (!checkObjectCoercible(thisValue))
1898 return throwVMTypeError(exec, scope);
1900 String stringToSearchIn = thisValue.toWTFString(exec);
1901 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1903 JSValue a0 = exec->argument(0);
1904 bool isRegularExpression = isRegExp(vm, exec, a0);
1905 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1906 if (isRegularExpression)
1907 return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
1909 String searchString = a0.toWTFString(exec);
1910 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1912 JSValue positionArg = exec->argument(1);
1914 if (positionArg.isInt32())
1915 start = std::max(0, positionArg.asInt32());
1917 unsigned length = stringToSearchIn.length();
1918 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1919 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1922 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1925 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1927 VM& vm = exec->vm();
1928 auto scope = DECLARE_THROW_SCOPE(vm);
1930 JSValue thisValue = exec->thisValue();
1931 if (!checkObjectCoercible(thisValue))
1932 return throwVMTypeError(exec, scope);
1934 String stringToSearchIn = thisValue.toWTFString(exec);
1935 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1937 JSValue a0 = exec->argument(0);
1938 bool isRegularExpression = isRegExp(vm, exec, a0);
1939 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1940 if (isRegularExpression)
1941 return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
1943 String searchString = a0.toWTFString(exec);
1944 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1946 unsigned length = stringToSearchIn.length();
1948 JSValue endPositionArg = exec->argument(1);
1949 unsigned end = length;
1950 if (endPositionArg.isInt32())
1951 end = std::max(0, endPositionArg.asInt32());
1952 else if (!endPositionArg.isUndefined()) {
1953 end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1954 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1957 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1960 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1962 auto scope = DECLARE_THROW_SCOPE(vm);
1964 if (positionArg.isInt32())
1965 start = std::max(0, positionArg.asInt32());
1967 unsigned length = stringToSearchIn.length();
1968 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1969 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1972 return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1975 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1977 VM& vm = exec->vm();
1978 auto scope = DECLARE_THROW_SCOPE(vm);
1980 JSValue thisValue = exec->thisValue();
1981 if (!checkObjectCoercible(thisValue))
1982 return throwVMTypeError(exec, scope);
1984 String stringToSearchIn = thisValue.toWTFString(exec);
1985 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1987 JSValue a0 = exec->argument(0);
1988 bool isRegularExpression = isRegExp(vm, exec, a0);
1989 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1990 if (isRegularExpression)
1991 return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
1993 String searchString = a0.toWTFString(exec);
1994 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1996 JSValue positionArg = exec->argument(1);
1999 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2002 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
2004 VM& vm = exec->vm();
2005 auto scope = DECLARE_THROW_SCOPE(vm);
2007 JSValue thisValue = exec->thisValue();
2008 ASSERT(checkObjectCoercible(thisValue));
2010 String stringToSearchIn = thisValue.toWTFString(exec);
2011 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2013 JSValue a0 = exec->uncheckedArgument(0);
2014 String searchString = a0.toWTFString(exec);
2015 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2017 JSValue positionArg = exec->argument(1);
2020 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2023 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
2025 VM& vm = exec->vm();
2026 auto scope = DECLARE_THROW_SCOPE(vm);
2028 JSValue thisValue = exec->thisValue();
2029 if (!checkObjectCoercible(thisValue))
2030 return throwVMTypeError(exec, scope);
2031 JSString* string = thisValue.toString(exec);
2032 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2033 return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
2036 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
2038 VM& vm = exec->vm();
2039 auto scope = DECLARE_THROW_SCOPE(vm);
2041 UErrorCode status = U_ZERO_ERROR;
2042 int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
2044 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
2045 // The behavior is not specified when normalize fails.
2046 // Now we throw a type erorr since it seems that the contents of the string are invalid.
2047 return throwTypeError(exec, scope);
2050 UChar* buffer = nullptr;
2051 auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
2053 return throwOutOfMemoryError(exec, scope);
2055 status = U_ZERO_ERROR;
2056 unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
2057 if (U_FAILURE(status))
2058 return throwTypeError(exec, scope);
2061 return jsString(exec, WTFMove(impl));
2064 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
2066 VM& vm = exec->vm();
2067 auto scope = DECLARE_THROW_SCOPE(vm);
2069 JSValue thisValue = exec->thisValue();
2070 if (!checkObjectCoercible(thisValue))
2071 return throwVMTypeError(exec, scope);
2072 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
2073 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2074 StringView view = viewWithString.view;
2076 UNormalizationMode form = UNORM_NFC;
2077 // Verify that the argument is provided and is not undefined.
2078 if (!exec->argument(0).isUndefined()) {
2079 String formString = exec->uncheckedArgument(0).toWTFString(exec);
2080 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2082 if (formString == "NFC")
2084 else if (formString == "NFD")
2086 else if (formString == "NFKC")
2088 else if (formString == "NFKD")
2091 return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2095 return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));