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)
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 for (; i < regExp->numSubpatterns() + 1; ++i) {
543 int matchStart = ovector[i * 2];
544 int matchLen = ovector[i * 2 + 1] - matchStart;
547 cachedCall.setArgument(i, jsUndefined());
549 cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
552 cachedCall.setArgument(i++, jsNumber(result.start));
553 cachedCall.setArgument(i++, string);
555 cachedCall.setThis(jsUndefined());
556 JSValue jsResult = cachedCall.call();
557 replacements.append(jsResult.toWTFString(exec));
558 RETURN_IF_EXCEPTION(scope, encodedJSValue());
560 lastIndex = result.end;
561 startPosition = lastIndex;
563 // special case of empty match
564 if (result.empty()) {
566 if (startPosition > sourceLen)
573 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
577 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
578 OUT_OF_MEMORY(exec, scope);
581 for (; i < regExp->numSubpatterns() + 1; ++i) {
582 int matchStart = ovector[i * 2];
583 int matchLen = ovector[i * 2 + 1] - matchStart;
586 cachedCall.setArgument(i, jsUndefined());
588 cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
591 cachedCall.setArgument(i++, jsNumber(result.start));
592 cachedCall.setArgument(i++, string);
594 cachedCall.setThis(jsUndefined());
595 JSValue jsResult = cachedCall.call();
596 replacements.append(jsResult.toWTFString(exec));
597 RETURN_IF_EXCEPTION(scope, encodedJSValue());
599 lastIndex = result.end;
600 startPosition = lastIndex;
602 // special case of empty match
603 if (result.empty()) {
605 if (startPosition > sourceLen)
613 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
617 if (callType != CallType::None) {
618 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
619 OUT_OF_MEMORY(exec, scope);
621 MarkedArgumentBuffer args;
623 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
624 int matchStart = ovector[i * 2];
625 int matchLen = ovector[i * 2 + 1] - matchStart;
628 args.append(jsUndefined());
630 args.append(jsSubstring(exec, source, matchStart, matchLen));
633 args.append(jsNumber(result.start));
636 JSValue replacement = call(exec, replaceValue, callType, callData, jsUndefined(), args);
637 RETURN_IF_EXCEPTION(scope, encodedJSValue());
638 String replacementString = replacement.toWTFString(exec);
639 RETURN_IF_EXCEPTION(scope, encodedJSValue());
640 replacements.append(replacementString);
641 RETURN_IF_EXCEPTION(scope, encodedJSValue());
643 int replLen = replacementString.length();
644 if (lastIndex < result.start || replLen) {
645 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
646 OUT_OF_MEMORY(exec, scope);
649 replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
651 replacements.append(String());
655 lastIndex = result.end;
656 startPosition = lastIndex;
658 // special case of empty match
659 if (result.empty()) {
661 if (startPosition > sourceLen)
667 if (!lastIndex && replacements.isEmpty())
668 return JSValue::encode(string);
670 if (static_cast<unsigned>(lastIndex) < sourceLen) {
671 if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
672 OUT_OF_MEMORY(exec, scope);
675 return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
678 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
679 ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
682 NativeCallFrameTracer tracer(&vm, exec);
683 auto scope = DECLARE_THROW_SCOPE(vm);
685 RegExp* regExp = searchValue->regExp();
686 if (regExp->global()) {
687 // ES5.1 15.5.4.10 step 8.a.
688 searchValue->setLastIndex(exec, 0);
689 RETURN_IF_EXCEPTION(scope, encodedJSValue());
691 return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
695 String replacementString = emptyString();
697 return replaceUsingRegExpSearch(
698 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
701 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
702 ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
705 NativeCallFrameTracer tracer(&vm, exec);
708 String replacementString = replaceString->value(exec);
709 return replaceUsingRegExpSearch(
710 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
713 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
715 auto scope = DECLARE_THROW_SCOPE(vm);
717 String replacementString;
719 CallType callType = getCallData(replaceValue, callData);
720 if (callType == CallType::None) {
721 replacementString = replaceValue.toWTFString(exec);
722 RETURN_IF_EXCEPTION(scope, encodedJSValue());
726 return replaceUsingRegExpSearch(
727 vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
730 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM& vm, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
732 auto scope = DECLARE_THROW_SCOPE(vm);
734 const String& string = jsString->value(exec);
735 String searchString = searchValue.toWTFString(exec);
736 RETURN_IF_EXCEPTION(scope, encodedJSValue());
738 size_t matchStart = string.find(searchString);
740 if (matchStart == notFound)
741 return JSValue::encode(jsString);
744 CallType callType = getCallData(replaceValue, callData);
745 if (callType != CallType::None) {
746 MarkedArgumentBuffer args;
747 args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
748 args.append(jsNumber(matchStart));
749 args.append(jsString);
750 replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
751 RETURN_IF_EXCEPTION(scope, encodedJSValue());
754 String replaceString = replaceValue.toWTFString(exec);
755 RETURN_IF_EXCEPTION(scope, encodedJSValue());
757 StringImpl* stringImpl = string.impl();
758 String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart));
760 size_t matchEnd = matchStart + searchString.impl()->length();
761 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
762 String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
764 size_t leftLength = stringImpl->length() - matchEnd;
765 String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength));
767 return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
770 static inline bool checkObjectCoercible(JSValue thisValue)
772 if (thisValue.isString())
775 if (thisValue.isUndefinedOrNull())
778 if (thisValue.isObject() && asObject(thisValue)->isEnvironmentRecord())
784 template <typename CharacterType>
785 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
788 auto scope = DECLARE_THROW_SCOPE(vm);
790 CharacterType* buffer = nullptr;
791 auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
793 throwOutOfMemoryError(&exec, scope);
797 std::fill_n(buffer, repeatCount, character);
800 return jsString(&exec, WTFMove(impl));
803 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
806 auto scope = DECLARE_THROW_SCOPE(vm);
808 // For a string which length is single, instead of creating ropes,
809 // allocating a sequential buffer and fill with the repeated string for efficiency.
810 ASSERT(exec->argumentCount() == 2);
812 ASSERT(exec->uncheckedArgument(0).isString());
813 JSString* string = asString(exec->uncheckedArgument(0));
814 ASSERT(string->length() == 1);
816 JSValue repeatCountValue = exec->uncheckedArgument(1);
817 RELEASE_ASSERT(repeatCountValue.isNumber());
819 double value = repeatCountValue.asNumber();
820 if (value > JSString::MaxLength)
821 return JSValue::encode(throwOutOfMemoryError(exec, scope));
822 repeatCount = static_cast<int32_t>(value);
823 ASSERT(repeatCount >= 0);
824 ASSERT(!repeatCountValue.isDouble() || repeatCountValue.asDouble() == repeatCount);
826 auto viewWithString = string->viewWithUnderlyingString(*exec);
827 StringView view = viewWithString.view;
828 ASSERT(view.length() == 1 && !scope.exception());
829 UChar character = view[0];
831 if (!(character & ~0xff))
832 return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
833 return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
836 ALWAYS_INLINE EncodedJSValue replace(
837 VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
839 if (searchValue.inherits(vm, RegExpObject::info()))
840 return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
841 return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
844 ALWAYS_INLINE EncodedJSValue replace(
845 VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
847 auto scope = DECLARE_THROW_SCOPE(vm);
849 if (!checkObjectCoercible(thisValue))
850 return throwVMTypeError(exec, scope);
851 JSString* string = thisValue.toString(exec);
852 RETURN_IF_EXCEPTION(scope, encodedJSValue());
854 return replace(vm, exec, string, searchValue, replaceValue);
857 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
860 auto scope = DECLARE_THROW_SCOPE(vm);
862 JSString* string = exec->thisValue().toString(exec);
863 RETURN_IF_EXCEPTION(scope, encodedJSValue());
865 JSValue searchValue = exec->argument(0);
866 if (!searchValue.inherits(vm, RegExpObject::info()))
867 return JSValue::encode(jsUndefined());
870 return replaceUsingRegExpSearch(vm, exec, string, searchValue, exec->argument(1));
873 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
876 auto scope = DECLARE_THROW_SCOPE(vm);
878 JSString* string = exec->thisValue().toString(exec);
879 RETURN_IF_EXCEPTION(scope, encodedJSValue());
882 return replaceUsingStringSearch(vm, exec, string, exec->argument(0), exec->argument(1));
885 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
886 ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
887 EncodedJSValue replaceValue)
890 NativeCallFrameTracer tracer(&vm, exec);
893 vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
894 JSValue::decode(replaceValue));
897 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
900 auto scope = DECLARE_THROW_SCOPE(vm);
902 JSValue thisValue = exec->thisValue();
903 // Also used for valueOf.
905 if (thisValue.isString())
906 return JSValue::encode(thisValue);
908 if (thisValue.inherits(vm, StringObject::info()))
909 return JSValue::encode(asStringObject(thisValue)->internalValue());
911 return throwVMTypeError(exec, scope);
914 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
917 auto scope = DECLARE_THROW_SCOPE(vm);
919 JSValue thisValue = exec->thisValue();
920 if (!checkObjectCoercible(thisValue))
921 return throwVMTypeError(exec, scope);
922 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
923 RETURN_IF_EXCEPTION(scope, encodedJSValue());
924 StringView view = viewWithString.view;
925 RETURN_IF_EXCEPTION(scope, encodedJSValue());
926 JSValue a0 = exec->argument(0);
928 uint32_t i = a0.asUInt32();
929 if (i < view.length())
930 return JSValue::encode(jsSingleCharacterString(exec, view[i]));
931 return JSValue::encode(jsEmptyString(exec));
933 double dpos = a0.toInteger(exec);
934 if (dpos >= 0 && dpos < view.length())
935 return JSValue::encode(jsSingleCharacterString(exec, view[static_cast<unsigned>(dpos)]));
936 return JSValue::encode(jsEmptyString(exec));
939 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
942 auto scope = DECLARE_THROW_SCOPE(vm);
944 JSValue thisValue = exec->thisValue();
945 if (!checkObjectCoercible(thisValue))
946 return throwVMTypeError(exec, scope);
947 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
948 RETURN_IF_EXCEPTION(scope, encodedJSValue());
949 StringView view = viewWithString.view;
950 JSValue a0 = exec->argument(0);
952 uint32_t i = a0.asUInt32();
953 if (i < view.length())
954 return JSValue::encode(jsNumber(view[i]));
955 return JSValue::encode(jsNaN());
957 double dpos = a0.toInteger(exec);
958 if (dpos >= 0 && dpos < view.length())
959 return JSValue::encode(jsNumber(view[static_cast<int>(dpos)]));
960 return JSValue::encode(jsNaN());
963 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
965 RELEASE_ASSERT(position < length);
967 return string.characters8()[position];
969 U16_NEXT(string.characters16(), position, length, character);
973 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
976 auto scope = DECLARE_THROW_SCOPE(vm);
978 JSValue thisValue = exec->thisValue();
979 if (!checkObjectCoercible(thisValue))
980 return throwVMTypeError(exec, scope);
982 String string = thisValue.toWTFString(exec);
983 RETURN_IF_EXCEPTION(scope, encodedJSValue());
984 unsigned length = string.length();
986 JSValue argument0 = exec->argument(0);
987 if (argument0.isUInt32()) {
988 unsigned position = argument0.asUInt32();
989 if (position < length)
990 return JSValue::encode(jsNumber(codePointAt(string, position, length)));
991 return JSValue::encode(jsUndefined());
994 RETURN_IF_EXCEPTION(scope, encodedJSValue());
996 double doublePosition = argument0.toInteger(exec);
997 RETURN_IF_EXCEPTION(scope, encodedJSValue());
998 if (doublePosition >= 0 && doublePosition < length)
999 return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
1000 return JSValue::encode(jsUndefined());
1003 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
1005 VM& vm = exec->vm();
1006 auto scope = DECLARE_THROW_SCOPE(vm);
1008 JSValue thisValue = exec->thisValue();
1009 if (thisValue.isString() && exec->argumentCount() == 1) {
1010 JSString* str = exec->uncheckedArgument(0).toString(exec);
1011 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1013 return JSValue::encode(jsString(exec, asString(thisValue), str));
1016 if (!checkObjectCoercible(thisValue))
1017 return throwVMTypeError(exec, scope);
1019 return JSValue::encode(jsStringFromArguments(exec, thisValue));
1022 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1024 VM& vm = exec->vm();
1025 auto scope = DECLARE_THROW_SCOPE(vm);
1027 JSValue thisValue = exec->thisValue();
1028 if (!checkObjectCoercible(thisValue))
1029 return throwVMTypeError(exec, scope);
1031 JSValue a0 = exec->argument(0);
1032 JSValue a1 = exec->argument(1);
1034 JSString* thisJSString = thisValue.toString(exec);
1035 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1036 JSString* otherJSString = a0.toString(exec);
1037 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1040 if (!a1.isUndefined()) {
1041 int len = thisJSString->length();
1042 RELEASE_ASSERT(len >= 0);
1044 pos = std::min<uint32_t>(a1.asUInt32(), len);
1046 double dpos = a1.toInteger(exec);
1049 else if (dpos > len)
1051 pos = static_cast<unsigned>(dpos);
1055 if (thisJSString->length() < otherJSString->length() + pos)
1056 return JSValue::encode(jsNumber(-1));
1058 auto thisViewWithString = thisJSString->viewWithUnderlyingString(*exec);
1059 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1060 auto otherViewWithString = otherJSString->viewWithUnderlyingString(*exec);
1061 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1062 size_t result = thisViewWithString.view.find(otherViewWithString.view, pos);
1063 if (result == notFound)
1064 return JSValue::encode(jsNumber(-1));
1065 return JSValue::encode(jsNumber(result));
1068 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1070 VM& vm = exec->vm();
1071 auto scope = DECLARE_THROW_SCOPE(vm);
1073 JSValue thisValue = exec->thisValue();
1074 if (!checkObjectCoercible(thisValue))
1075 return throwVMTypeError(exec, scope);
1077 JSValue a0 = exec->argument(0);
1078 JSValue a1 = exec->argument(1);
1080 JSString* thisJSString = thisValue.toString(exec);
1081 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1082 unsigned len = thisJSString->length();
1083 JSString* otherJSString = a0.toString(exec);
1084 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1086 double dpos = a1.toIntegerPreserveNaN(exec);
1087 unsigned startPosition;
1090 else if (!(dpos <= len)) // true for NaN
1091 startPosition = len;
1093 startPosition = static_cast<unsigned>(dpos);
1095 if (len < otherJSString->length())
1096 return JSValue::encode(jsNumber(-1));
1098 String thisString = thisJSString->value(exec);
1099 String otherString = otherJSString->value(exec);
1102 result = thisString.startsWith(otherString) ? 0 : notFound;
1104 result = thisString.reverseFind(otherString, startPosition);
1105 if (result == notFound)
1106 return JSValue::encode(jsNumber(-1));
1107 return JSValue::encode(jsNumber(result));
1110 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1112 VM& vm = exec->vm();
1113 auto scope = DECLARE_THROW_SCOPE(vm);
1115 JSValue thisValue = exec->thisValue();
1116 if (!checkObjectCoercible(thisValue))
1117 return throwVMTypeError(exec, scope);
1118 String s = thisValue.toWTFString(exec);
1119 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1121 int len = s.length();
1122 RELEASE_ASSERT(len >= 0);
1124 JSValue a0 = exec->argument(0);
1125 JSValue a1 = exec->argument(1);
1127 // The arg processing is very much like ArrayProtoFunc::Slice
1128 double start = a0.toInteger(exec);
1129 double end = a1.isUndefined() ? len : a1.toInteger(exec);
1130 double from = start < 0 ? len + start : start;
1131 double to = end < 0 ? len + end : end;
1132 if (to > from && to > 0 && from < len) {
1137 return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1140 return JSValue::encode(jsEmptyString(exec));
1143 // Return true in case of early return (resultLength got to limitLength).
1144 template<typename CharacterType>
1145 static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, JSValue originalValue, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength)
1147 VM& vm = exec->vm();
1148 auto scope = DECLARE_THROW_SCOPE(vm);
1151 size_t matchPosition;
1152 const CharacterType* characters = string->characters<CharacterType>();
1153 // 13. Repeat, while q != s
1154 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1155 // b. If z is failure, then let q = q+1.
1156 // c. Else, z is not failure
1157 while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1158 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1159 // through q (exclusive).
1160 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1161 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1162 result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1163 RETURN_IF_EXCEPTION(scope, false);
1164 // 3. Increment lengthA by 1.
1165 // 4. If lengthA == lim, return A.
1166 if (++resultLength == limitLength)
1171 position = matchPosition + 1;
1176 // ES 21.1.3.17 String.prototype.split(separator, limit)
1177 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1179 VM& vm = exec->vm();
1180 auto scope = DECLARE_THROW_SCOPE(vm);
1181 JSValue thisValue = exec->thisValue();
1182 ASSERT(checkObjectCoercible(thisValue));
1184 // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1185 // 7. Let s be the number of characters in S.
1186 String input = thisValue.toWTFString(exec);
1187 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1188 ASSERT(!input.isNull());
1190 // 4. Let A be a new array created as if by the expression new Array()
1191 // where Array is the standard built-in constructor with that name.
1192 JSArray* result = constructEmptyArray(exec, 0);
1193 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1195 // 5. Let lengthA be 0.
1196 unsigned resultLength = 0;
1198 // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1199 JSValue limitValue = exec->uncheckedArgument(1);
1200 unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1203 size_t position = 0;
1205 // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1206 // otherwise let R = ToString(separator).
1207 JSValue separatorValue = exec->uncheckedArgument(0);
1208 String separator = separatorValue.toWTFString(exec);
1209 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1211 // 10. If lim == 0, return A.
1213 return JSValue::encode(result);
1215 // 11. If separator is undefined, then
1216 if (separatorValue.isUndefined()) {
1217 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1219 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1221 return JSValue::encode(result);
1224 // 12. If s == 0, then
1225 if (input.isEmpty()) {
1226 // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1227 // b. If z is not false, return A.
1228 // c. Call CreateDataProperty(A, "0", S).
1230 if (!separator.isEmpty()) {
1232 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1234 return JSValue::encode(result);
1237 // Optimized case for splitting on the empty string.
1238 if (separator.isEmpty()) {
1239 limit = std::min(limit, input.length());
1240 // Zero limt/input length handled in steps 9/11 respectively, above.
1244 result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1245 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1246 } while (++position < limit);
1248 return JSValue::encode(result);
1252 // -separator length == 1, 8 bits
1253 // -separator length == 1, 16 bits
1254 // -separator length > 1
1255 StringImpl* stringImpl = input.impl();
1256 StringImpl* separatorImpl = separator.impl();
1257 size_t separatorLength = separatorImpl->length();
1259 if (separatorLength == 1) {
1260 UChar separatorCharacter;
1261 if (separatorImpl->is8Bit())
1262 separatorCharacter = separatorImpl->characters8()[0];
1264 separatorCharacter = separatorImpl->characters16()[0];
1266 if (stringImpl->is8Bit()) {
1267 if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1269 return JSValue::encode(result);
1272 if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
1274 return JSValue::encode(result);
1277 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1280 size_t matchPosition;
1281 // 14. Repeat, while q != s
1282 // a. let e be SplitMatch(S, q, R).
1283 // b. If e is failure, then let q = q+1.
1284 // c. Else, e is an integer index <= s.
1285 while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1286 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1287 // through q (exclusive).
1288 // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1289 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1290 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1291 // 3. Increment lengthA by 1.
1292 // 4. If lengthA == lim, return A.
1293 if (++resultLength == limit)
1294 return JSValue::encode(result);
1298 position = matchPosition + separator.length();
1302 // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1303 // through s (exclusive).
1304 // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1306 result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1309 return JSValue::encode(result);
1312 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1314 VM& vm = exec->vm();
1315 auto scope = DECLARE_THROW_SCOPE(vm);
1317 JSValue thisValue = exec->thisValue();
1318 if (!checkObjectCoercible(thisValue))
1319 return throwVMTypeError(exec, scope);
1321 JSString* jsString = 0;
1323 if (thisValue.isString()) {
1324 jsString = asString(thisValue);
1325 len = jsString->length();
1327 uString = thisValue.toWTFString(exec);
1328 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1329 len = uString.length();
1332 JSValue a0 = exec->argument(0);
1333 JSValue a1 = exec->argument(1);
1335 double start = a0.toInteger(exec);
1336 double length = a1.isUndefined() ? len : a1.toInteger(exec);
1337 if (start >= len || length <= 0)
1338 return JSValue::encode(jsEmptyString(exec));
1344 if (start + length > len)
1345 length = len - start;
1346 unsigned substringStart = static_cast<unsigned>(start);
1347 unsigned substringLength = static_cast<unsigned>(length);
1349 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1350 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1353 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1355 // @substrInternal should not have any observable side effects (e.g. it should not call
1356 // GetMethod(..., @@toPrimitive) on the thisValue).
1358 // It is ok to use the default stringProtoFuncSubstr as the implementation of
1359 // @substrInternal because @substrInternal will only be called by builtins, which will
1360 // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1361 // will not need to call toString() on the thisValue, and there will be no observable
1363 ASSERT(exec->thisValue().isString());
1364 return stringProtoFuncSubstr(exec);
1367 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1369 VM& vm = exec->vm();
1370 auto scope = DECLARE_THROW_SCOPE(vm);
1372 JSValue thisValue = exec->thisValue();
1373 if (!checkObjectCoercible(thisValue))
1374 return throwVMTypeError(exec, scope);
1376 JSString* jsString = thisValue.toString(exec);
1377 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1379 JSValue a0 = exec->argument(0);
1380 JSValue a1 = exec->argument(1);
1381 int len = jsString->length();
1382 RELEASE_ASSERT(len >= 0);
1384 double start = a0.toNumber(exec);
1385 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1387 if (!(start >= 0)) // check for negative values or NaN
1389 else if (start > len)
1391 if (a1.isUndefined())
1394 end = a1.toNumber(exec);
1395 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1396 if (!(end >= 0)) // check for negative values or NaN
1406 unsigned substringStart = static_cast<unsigned>(start);
1407 unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1408 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1411 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1413 VM& vm = exec->vm();
1414 auto scope = DECLARE_THROW_SCOPE(vm);
1416 JSValue thisValue = exec->thisValue();
1417 if (!checkObjectCoercible(thisValue))
1418 return throwVMTypeError(exec, scope);
1419 JSString* sVal = thisValue.toString(exec);
1420 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1421 const String& s = sVal->value(exec);
1422 String lowercasedString = s.convertToLowercaseWithoutLocale();
1423 if (lowercasedString.impl() == s.impl())
1424 return JSValue::encode(sVal);
1426 return JSValue::encode(jsString(exec, lowercasedString));
1429 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1431 VM& vm = exec->vm();
1432 auto scope = DECLARE_THROW_SCOPE(vm);
1434 JSValue thisValue = exec->thisValue();
1435 if (!checkObjectCoercible(thisValue))
1436 return throwVMTypeError(exec, scope);
1437 JSString* sVal = thisValue.toString(exec);
1438 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1439 const String& s = sVal->value(exec);
1440 String uppercasedString = s.convertToUppercaseWithoutLocale();
1441 if (uppercasedString.impl() == s.impl())
1442 return JSValue::encode(sVal);
1444 return JSValue::encode(jsString(exec, uppercasedString));
1447 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1449 VM& vm = exec->vm();
1450 auto scope = DECLARE_THROW_SCOPE(vm);
1452 JSValue thisValue = exec->thisValue();
1453 if (!checkObjectCoercible(thisValue))
1454 return throwVMTypeError(exec, scope);
1455 String s = thisValue.toWTFString(exec);
1456 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1458 JSValue a0 = exec->argument(0);
1459 String str = a0.toWTFString(exec);
1460 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1461 return JSValue::encode(jsNumber(Collator().collate(s, str)));
1465 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1467 VM& vm = state->vm();
1468 auto scope = DECLARE_THROW_SCOPE(vm);
1470 // 1. Let O be RequireObjectCoercible(this value).
1471 JSValue thisValue = state->thisValue();
1472 if (!checkObjectCoercible(thisValue))
1473 return throwVMTypeError(state, scope);
1475 // 2. Let S be ToString(O).
1476 JSString* sVal = thisValue.toString(state);
1477 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1478 const String& s = sVal->value(state);
1480 // 3. ReturnIfAbrupt(S).
1481 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1483 // Optimization for empty strings.
1485 return JSValue::encode(sVal);
1487 // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1488 Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1490 // 5. ReturnIfAbrupt(requestedLocales).
1491 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1493 // 6. Let len be the number of elements in requestedLocales.
1494 size_t len = requestedLocales.size();
1496 // 7. If len > 0, then
1497 // a. Let requestedLocale be the first element of requestedLocales.
1499 // a. Let requestedLocale be DefaultLocale().
1500 String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1502 // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1503 String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1505 // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1506 // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1507 const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1509 // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1510 String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1512 // 12. If locale is undefined, let locale be "und".
1513 if (locale.isNull())
1514 locale = ASCIILiteral("und");
1516 CString utf8LocaleBuffer = locale.utf8();
1517 const StringView view(s);
1518 const int32_t viewLength = view.length();
1520 // Delegate the following steps to icu u_strToLower or u_strToUpper.
1521 // 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.
1522 // 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).
1523 // 15. Let cuList be a new List.
1524 // 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.
1525 // 17. Let L be a String whose elements are, in order, the elements of cuList.
1527 // Most strings lower/upper case will be the same size as original, so try that first.
1528 UErrorCode error(U_ZERO_ERROR);
1529 Vector<UChar> buffer(viewLength);
1531 const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1532 if (U_SUCCESS(error))
1533 lower = String(buffer.data(), resultLength);
1534 else if (error == U_BUFFER_OVERFLOW_ERROR) {
1535 // Converted case needs more space than original. Try again.
1536 UErrorCode error(U_ZERO_ERROR);
1537 Vector<UChar> buffer(resultLength);
1538 convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1539 if (U_FAILURE(error))
1540 return throwVMTypeError(state, scope, u_errorName(error));
1541 lower = String(buffer.data(), resultLength);
1543 return throwVMTypeError(state, scope, u_errorName(error));
1547 return JSValue::encode(jsString(state, lower));
1550 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1552 // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1553 // http://ecma-international.org/publications/standards/Ecma-402.htm
1554 return toLocaleCase(state, u_strToLower);
1557 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1559 // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1560 // http://ecma-international.org/publications/standards/Ecma-402.htm
1561 // 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.
1562 return toLocaleCase(state, u_strToUpper);
1564 #endif // ENABLE(INTL)
1566 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1568 VM& vm = exec->vm();
1569 auto scope = DECLARE_THROW_SCOPE(vm);
1571 JSValue thisValue = exec->thisValue();
1572 if (!checkObjectCoercible(thisValue))
1573 return throwVMTypeError(exec, scope);
1574 String s = thisValue.toWTFString(exec);
1575 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1577 return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1580 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1582 VM& vm = exec->vm();
1583 auto scope = DECLARE_THROW_SCOPE(vm);
1585 JSValue thisValue = exec->thisValue();
1586 if (!checkObjectCoercible(thisValue))
1587 return throwVMTypeError(exec, scope);
1588 String s = thisValue.toWTFString(exec);
1589 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1591 return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1594 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1596 VM& vm = exec->vm();
1597 auto scope = DECLARE_THROW_SCOPE(vm);
1599 JSValue thisValue = exec->thisValue();
1600 if (!checkObjectCoercible(thisValue))
1601 return throwVMTypeError(exec, scope);
1602 String s = thisValue.toWTFString(exec);
1603 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1605 return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1608 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1610 VM& vm = exec->vm();
1611 auto scope = DECLARE_THROW_SCOPE(vm);
1613 JSValue thisValue = exec->thisValue();
1614 if (!checkObjectCoercible(thisValue))
1615 return throwVMTypeError(exec, scope);
1616 String s = thisValue.toWTFString(exec);
1617 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1619 return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1622 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1624 VM& vm = exec->vm();
1625 auto scope = DECLARE_THROW_SCOPE(vm);
1627 JSValue thisValue = exec->thisValue();
1628 if (!checkObjectCoercible(thisValue))
1629 return throwVMTypeError(exec, scope);
1630 String s = thisValue.toWTFString(exec);
1631 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1633 return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1636 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1638 VM& vm = exec->vm();
1639 auto scope = DECLARE_THROW_SCOPE(vm);
1641 JSValue thisValue = exec->thisValue();
1642 if (!checkObjectCoercible(thisValue))
1643 return throwVMTypeError(exec, scope);
1644 String s = thisValue.toWTFString(exec);
1645 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1647 return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1650 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1652 VM& vm = exec->vm();
1653 auto scope = DECLARE_THROW_SCOPE(vm);
1655 JSValue thisValue = exec->thisValue();
1656 if (!checkObjectCoercible(thisValue))
1657 return throwVMTypeError(exec, scope);
1658 String s = thisValue.toWTFString(exec);
1659 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1661 return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1664 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1666 VM& vm = exec->vm();
1667 auto scope = DECLARE_THROW_SCOPE(vm);
1669 JSValue thisValue = exec->thisValue();
1670 if (!checkObjectCoercible(thisValue))
1671 return throwVMTypeError(exec, scope);
1672 String s = thisValue.toWTFString(exec);
1673 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1675 return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1678 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1680 VM& vm = exec->vm();
1681 auto scope = DECLARE_THROW_SCOPE(vm);
1683 JSValue thisValue = exec->thisValue();
1684 if (!checkObjectCoercible(thisValue))
1685 return throwVMTypeError(exec, scope);
1686 String s = thisValue.toWTFString(exec);
1687 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1689 return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1692 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1694 VM& vm = exec->vm();
1695 auto scope = DECLARE_THROW_SCOPE(vm);
1697 JSValue thisValue = exec->thisValue();
1698 if (!checkObjectCoercible(thisValue))
1699 return throwVMTypeError(exec, scope);
1700 String s = thisValue.toWTFString(exec);
1701 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1703 JSValue a0 = exec->argument(0);
1704 String color = a0.toWTFString(exec);
1705 color.replaceWithLiteral('"', """);
1708 return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1711 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1713 VM& vm = exec->vm();
1714 auto scope = DECLARE_THROW_SCOPE(vm);
1716 JSValue thisValue = exec->thisValue();
1717 if (!checkObjectCoercible(thisValue))
1718 return throwVMTypeError(exec, scope);
1719 String s = thisValue.toWTFString(exec);
1720 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1722 JSValue a0 = exec->argument(0);
1724 uint32_t smallInteger;
1725 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1726 unsigned stringSize = s.length();
1727 unsigned bufferSize = 22 + stringSize;
1728 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1730 auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1732 return JSValue::encode(jsUndefined());
1745 buffer[12] = '0' + smallInteger;
1748 StringView(s).getCharactersWithUpconvert(&buffer[15]);
1749 buffer[15 + stringSize] = '<';
1750 buffer[16 + stringSize] = '/';
1751 buffer[17 + stringSize] = 'f';
1752 buffer[18 + stringSize] = 'o';
1753 buffer[19 + stringSize] = 'n';
1754 buffer[20 + stringSize] = 't';
1755 buffer[21 + stringSize] = '>';
1756 return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1759 String fontSize = a0.toWTFString(exec);
1760 fontSize.replaceWithLiteral('"', """);
1763 return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1766 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1768 VM& vm = exec->vm();
1769 auto scope = DECLARE_THROW_SCOPE(vm);
1771 JSValue thisValue = exec->thisValue();
1772 if (!checkObjectCoercible(thisValue))
1773 return throwVMTypeError(exec, scope);
1774 String s = thisValue.toWTFString(exec);
1775 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1777 JSValue a0 = exec->argument(0);
1778 String anchor = a0.toWTFString(exec);
1779 anchor.replaceWithLiteral('"', """);
1782 return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1785 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1787 VM& vm = exec->vm();
1788 auto scope = DECLARE_THROW_SCOPE(vm);
1790 JSValue thisValue = exec->thisValue();
1791 if (!checkObjectCoercible(thisValue))
1792 return throwVMTypeError(exec, scope);
1793 String s = thisValue.toWTFString(exec);
1794 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1796 JSValue a0 = exec->argument(0);
1797 String linkText = a0.toWTFString(exec);
1798 linkText.replaceWithLiteral('"', """);
1800 unsigned linkTextSize = linkText.length();
1801 unsigned stringSize = s.length();
1802 unsigned bufferSize = 15 + linkTextSize + stringSize;
1803 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1805 auto impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1807 return JSValue::encode(jsUndefined());
1817 StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1818 buffer[9 + linkTextSize] = '"';
1819 buffer[10 + linkTextSize] = '>';
1820 StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1821 buffer[11 + linkTextSize + stringSize] = '<';
1822 buffer[12 + linkTextSize + stringSize] = '/';
1823 buffer[13 + linkTextSize + stringSize] = 'a';
1824 buffer[14 + linkTextSize + stringSize] = '>';
1825 return JSValue::encode(jsNontrivialString(exec, WTFMove(impl)));
1833 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1835 VM& vm = exec->vm();
1836 auto scope = DECLARE_THROW_SCOPE(vm);
1838 if (!checkObjectCoercible(thisValue))
1839 return throwTypeError(exec, scope);
1840 String str = thisValue.toWTFString(exec);
1841 RETURN_IF_EXCEPTION(scope, { });
1844 if (trimKind & TrimLeft) {
1845 while (left < str.length() && isStrWhiteSpace(str[left]))
1848 unsigned right = str.length();
1849 if (trimKind & TrimRight) {
1850 while (right > left && isStrWhiteSpace(str[right - 1]))
1854 // Don't gc allocate a new string if we don't have to.
1855 if (left == 0 && right == str.length() && thisValue.isString())
1859 return jsString(exec, str.substringSharingImpl(left, right - left));
1862 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1864 JSValue thisValue = exec->thisValue();
1865 return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1868 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1870 JSValue thisValue = exec->thisValue();
1871 return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1874 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1876 JSValue thisValue = exec->thisValue();
1877 return JSValue::encode(trimString(exec, thisValue, TrimRight));
1880 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1886 return static_cast<unsigned>(value);
1889 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1891 VM& vm = exec->vm();
1892 auto scope = DECLARE_THROW_SCOPE(vm);
1894 JSValue thisValue = exec->thisValue();
1895 if (!checkObjectCoercible(thisValue))
1896 return throwVMTypeError(exec, scope);
1898 String stringToSearchIn = thisValue.toWTFString(exec);
1899 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1901 JSValue a0 = exec->argument(0);
1902 bool isRegularExpression = isRegExp(vm, exec, a0);
1903 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1904 if (isRegularExpression)
1905 return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
1907 String searchString = a0.toWTFString(exec);
1908 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1910 JSValue positionArg = exec->argument(1);
1912 if (positionArg.isInt32())
1913 start = std::max(0, positionArg.asInt32());
1915 unsigned length = stringToSearchIn.length();
1916 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1917 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1920 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1923 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1925 VM& vm = exec->vm();
1926 auto scope = DECLARE_THROW_SCOPE(vm);
1928 JSValue thisValue = exec->thisValue();
1929 if (!checkObjectCoercible(thisValue))
1930 return throwVMTypeError(exec, scope);
1932 String stringToSearchIn = thisValue.toWTFString(exec);
1933 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1935 JSValue a0 = exec->argument(0);
1936 bool isRegularExpression = isRegExp(vm, exec, a0);
1937 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1938 if (isRegularExpression)
1939 return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
1941 String searchString = a0.toWTFString(exec);
1942 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1944 unsigned length = stringToSearchIn.length();
1946 JSValue endPositionArg = exec->argument(1);
1947 unsigned end = length;
1948 if (endPositionArg.isInt32())
1949 end = std::max(0, endPositionArg.asInt32());
1950 else if (!endPositionArg.isUndefined()) {
1951 end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1952 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1955 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1958 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1960 auto scope = DECLARE_THROW_SCOPE(vm);
1962 if (positionArg.isInt32())
1963 start = std::max(0, positionArg.asInt32());
1965 unsigned length = stringToSearchIn.length();
1966 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1967 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1970 return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1973 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1975 VM& vm = exec->vm();
1976 auto scope = DECLARE_THROW_SCOPE(vm);
1978 JSValue thisValue = exec->thisValue();
1979 if (!checkObjectCoercible(thisValue))
1980 return throwVMTypeError(exec, scope);
1982 String stringToSearchIn = thisValue.toWTFString(exec);
1983 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1985 JSValue a0 = exec->argument(0);
1986 bool isRegularExpression = isRegExp(vm, exec, a0);
1987 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1988 if (isRegularExpression)
1989 return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
1991 String searchString = a0.toWTFString(exec);
1992 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1994 JSValue positionArg = exec->argument(1);
1997 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2000 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
2002 VM& vm = exec->vm();
2003 auto scope = DECLARE_THROW_SCOPE(vm);
2005 JSValue thisValue = exec->thisValue();
2006 ASSERT(checkObjectCoercible(thisValue));
2008 String stringToSearchIn = thisValue.toWTFString(exec);
2009 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2011 JSValue a0 = exec->uncheckedArgument(0);
2012 String searchString = a0.toWTFString(exec);
2013 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2015 JSValue positionArg = exec->argument(1);
2018 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
2021 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
2023 VM& vm = exec->vm();
2024 auto scope = DECLARE_THROW_SCOPE(vm);
2026 JSValue thisValue = exec->thisValue();
2027 if (!checkObjectCoercible(thisValue))
2028 return throwVMTypeError(exec, scope);
2029 JSString* string = thisValue.toString(exec);
2030 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2031 return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
2034 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
2036 VM& vm = exec->vm();
2037 auto scope = DECLARE_THROW_SCOPE(vm);
2039 UErrorCode status = U_ZERO_ERROR;
2040 int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
2042 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
2043 // The behavior is not specified when normalize fails.
2044 // Now we throw a type erorr since it seems that the contents of the string are invalid.
2045 return throwTypeError(exec, scope);
2048 UChar* buffer = nullptr;
2049 auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
2051 return throwOutOfMemoryError(exec, scope);
2053 status = U_ZERO_ERROR;
2054 unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
2055 if (U_FAILURE(status))
2056 return throwTypeError(exec, scope);
2059 return jsString(exec, WTFMove(impl));
2062 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
2064 VM& vm = exec->vm();
2065 auto scope = DECLARE_THROW_SCOPE(vm);
2067 JSValue thisValue = exec->thisValue();
2068 if (!checkObjectCoercible(thisValue))
2069 return throwVMTypeError(exec, scope);
2070 auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(*exec);
2071 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2072 StringView view = viewWithString.view;
2074 UNormalizationMode form = UNORM_NFC;
2075 // Verify that the argument is provided and is not undefined.
2076 if (!exec->argument(0).isUndefined()) {
2077 String formString = exec->uncheckedArgument(0).toWTFString(exec);
2078 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2080 if (formString == "NFC")
2082 else if (formString == "NFD")
2084 else if (formString == "NFKC")
2086 else if (formString == "NFKD")
2089 return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2093 return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));