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 "ButterflyInlines.h"
27 #include "CachedCall.h"
28 #include "CopiedSpaceInlines.h"
30 #include "Executable.h"
31 #include "IntlObject.h"
32 #include "JSCBuiltins.h"
33 #include "JSCInlines.h"
34 #include "JSGlobalObjectFunctions.h"
36 #include "JSFunction.h"
37 #include "JSStringBuilder.h"
38 #include "JSStringIterator.h"
40 #include "ObjectPrototype.h"
41 #include "PropertyNameArray.h"
42 #include "RegExpCache.h"
43 #include "RegExpConstructor.h"
44 #include "RegExpMatchesArray.h"
45 #include "RegExpObject.h"
46 #include "SuperSampler.h"
48 #include <unicode/uconfig.h>
49 #include <unicode/unorm.h>
50 #include <unicode/ustring.h>
51 #include <wtf/ASCIICType.h>
52 #include <wtf/MathExtras.h>
53 #include <wtf/text/StringView.h>
54 #include <wtf/unicode/Collator.h>
60 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(StringPrototype);
62 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
63 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
64 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
65 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*);
66 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
67 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
68 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
69 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadEnd(ExecState*);
70 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState*);
71 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
72 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
73 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
74 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
75 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
76 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
77 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
78 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
79 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState*);
80 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
81 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
82 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
83 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
84 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
85 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
86 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
87 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
88 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
89 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
90 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
91 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
92 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
93 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
94 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
95 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
96 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState*);
97 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState*);
98 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*);
99 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState*);
100 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
104 #include "StringPrototype.lut.h"
108 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, CREATE_METHOD_TABLE(StringPrototype) };
110 /* Source for StringConstructor.lut.h
111 @begin stringPrototypeTable
112 match JSBuiltin DontEnum|Function 1
113 repeat JSBuiltin DontEnum|Function 1
114 search JSBuiltin DontEnum|Function 1
115 split JSBuiltin DontEnum|Function 1
120 StringPrototype::StringPrototype(VM& vm, Structure* structure)
121 : StringObject(vm, structure)
125 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
127 Base::finishCreation(vm, nameAndMessage);
128 ASSERT(inherits(info()));
130 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
131 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
132 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
133 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
134 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
135 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", stringProtoFuncConcat, DontEnum, 1);
136 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
137 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
138 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padEnd", stringProtoFuncPadEnd, DontEnum, 1);
139 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padStart", stringProtoFuncPadStart, DontEnum, 1);
140 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("replace", stringProtoFuncReplace, DontEnum, 2, StringPrototypeReplaceIntrinsic);
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_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
145 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
146 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
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("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
152 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
154 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("big", stringProtoFuncBig, DontEnum, 0);
155 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("small", stringProtoFuncSmall, DontEnum, 0);
156 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("blink", stringProtoFuncBlink, DontEnum, 0);
157 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("bold", stringProtoFuncBold, DontEnum, 0);
158 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fixed", stringProtoFuncFixed, DontEnum, 0);
159 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("italics", stringProtoFuncItalics, DontEnum, 0);
160 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("strike", stringProtoFuncStrike, DontEnum, 0);
161 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sub", stringProtoFuncSub, DontEnum, 0);
162 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sup", stringProtoFuncSup, DontEnum, 0);
163 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
164 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
165 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("anchor", stringProtoFuncAnchor, DontEnum, 1);
166 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("link", stringProtoFuncLink, DontEnum, 1);
167 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trim", stringProtoFuncTrim, DontEnum, 0);
168 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
169 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
170 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("startsWith", stringProtoFuncStartsWith, DontEnum, 1);
171 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("endsWith", stringProtoFuncEndsWith, DontEnum, 1);
172 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("includes", stringProtoFuncIncludes, DontEnum, 1);
173 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("normalize", stringProtoFuncNormalize, DontEnum, 1);
174 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->iteratorSymbol, stringProtoFuncIterator, DontEnum, 0);
176 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->charCodeAtPrivateName, stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
178 // The constructor will be added later, after StringConstructor has been built
179 putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
182 StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
184 JSString* empty = jsEmptyString(&vm);
185 StringPrototype* prototype = new (NotNull, allocateCell<StringPrototype>(vm.heap)) StringPrototype(vm, structure);
186 prototype->finishCreation(vm, globalObject, empty);
190 bool StringPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
192 return getStaticFunctionSlot<Base>(exec, stringPrototypeTable, jsCast<StringPrototype*>(object), propertyName, slot);
195 // ------------------------------ Functions --------------------------
197 static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
199 StringBuilder substitutedReplacement;
202 if (i + 1 == replacement.length())
205 UChar ref = replacement[i + 1];
209 substitutedReplacement.append(replacement.substring(offset, i - offset));
218 backrefStart = ovector[0];
219 backrefLength = ovector[1] - backrefStart;
220 } else if (ref == '`') {
222 backrefLength = ovector[0];
223 } else if (ref == '\'') {
224 backrefStart = ovector[1];
225 backrefLength = source.length() - backrefStart;
226 } else if (reg && ref >= '0' && ref <= '9') {
227 // 1- and 2-digit back references are allowed
228 unsigned backrefIndex = ref - '0';
229 if (backrefIndex > reg->numSubpatterns())
231 if (replacement.length() > i + 2) {
232 ref = replacement[i + 2];
233 if (ref >= '0' && ref <= '9') {
234 backrefIndex = 10 * backrefIndex + ref - '0';
235 if (backrefIndex > reg->numSubpatterns())
236 backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
243 backrefStart = ovector[2 * backrefIndex];
244 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
249 substitutedReplacement.append(replacement.substring(offset, i - offset));
252 if (backrefStart >= 0)
253 substitutedReplacement.append(source.substring(backrefStart, backrefLength));
254 } while ((i = replacement.find('$', i + 1)) != notFound);
256 if (replacement.length() - offset)
257 substitutedReplacement.append(replacement.substring(offset));
259 return substitutedReplacement.toString();
262 inline String substituteBackreferencesInline(const String& replacement, StringView source, const int* ovector, RegExp* reg)
264 size_t i = replacement.find('$');
265 if (UNLIKELY(i != notFound))
266 return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
271 String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
273 return substituteBackreferencesInline(replacement, source, ovector, reg);
277 StringRange(int pos, int len)
291 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
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.
300 return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
304 for (int i = 0; i < rangeCount; i++)
305 totalLength += substringRanges[i].length;
308 return jsEmptyString(exec);
310 if (source.is8Bit()) {
312 const LChar* sourceData = source.characters8();
313 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
315 return throwOutOfMemoryError(exec);
318 for (int i = 0; i < rangeCount; i++) {
319 if (int srcLen = substringRanges[i].length) {
320 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
325 return jsString(exec, impl.release());
329 const UChar* sourceData = source.characters16();
331 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
333 return throwOutOfMemoryError(exec);
336 for (int i = 0; i < rangeCount; i++) {
337 if (int srcLen = substringRanges[i].length) {
338 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
343 return jsString(exec, impl.release());
346 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
348 if (rangeCount == 1 && separatorCount == 0) {
349 int sourceSize = source.length();
350 int position = substringRanges[0].position;
351 int length = substringRanges[0].length;
352 if (position <= 0 && length >= sourceSize)
354 // We could call String::substringSharingImpl(), but this would result in redundant checks.
355 return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
358 Checked<int, RecordOverflow> totalLength = 0;
359 bool allSeparators8Bit = true;
360 for (int i = 0; i < rangeCount; i++)
361 totalLength += substringRanges[i].length;
362 for (int i = 0; i < separatorCount; i++) {
363 totalLength += separators[i].length();
364 if (separators[i].length() && !separators[i].is8Bit())
365 allSeparators8Bit = false;
367 if (totalLength.hasOverflowed())
368 return throwOutOfMemoryError(exec);
371 return jsEmptyString(exec);
373 if (source.is8Bit() && allSeparators8Bit) {
375 const LChar* sourceData = source.characters8();
377 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
379 return throwOutOfMemoryError(exec);
381 int maxCount = std::max(rangeCount, separatorCount);
383 for (int i = 0; i < maxCount; i++) {
384 if (i < rangeCount) {
385 if (int srcLen = substringRanges[i].length) {
386 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
390 if (i < separatorCount) {
391 if (int sepLen = separators[i].length()) {
392 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
398 return jsString(exec, impl.release());
402 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
404 return throwOutOfMemoryError(exec);
406 int maxCount = std::max(rangeCount, separatorCount);
408 for (int i = 0; i < maxCount; i++) {
409 if (i < rangeCount) {
410 if (int srcLen = substringRanges[i].length) {
412 StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
414 StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
418 if (i < separatorCount) {
419 if (int sepLen = separators[i].length()) {
420 if (separators[i].is8Bit())
421 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
423 StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
429 return jsString(exec, impl.release());
432 static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
434 SuperSamplerScope superSamplerScope(false);
436 size_t lastIndex = 0;
437 unsigned startPosition = 0;
439 Vector<StringRange, 16> sourceRanges;
440 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
441 unsigned sourceLen = source.length();
444 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition);
448 if (lastIndex < result.start)
449 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
451 lastIndex = result.end;
452 startPosition = lastIndex;
454 // special case of empty match
455 if (result.empty()) {
457 if (startPosition > sourceLen)
463 return JSValue::encode(string);
465 if (static_cast<unsigned>(lastIndex) < sourceLen)
466 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
468 return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
471 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
472 VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
473 CallType callType, String& replacementString, JSValue replaceValue)
475 const String& source = string->value(exec);
476 unsigned sourceLen = source.length();
477 if (exec->hadException())
478 return JSValue::encode(jsUndefined());
479 RegExpObject* regExpObject = asRegExpObject(searchValue);
480 RegExp* regExp = regExpObject->regExp();
481 bool global = regExp->global();
484 // ES5.1 15.5.4.10 step 8.a.
485 regExpObject->setLastIndex(exec, 0);
486 if (exec->hadException())
487 return JSValue::encode(jsUndefined());
489 if (callType == CallType::None && !replacementString.length())
490 return removeUsingRegExpSearch(vm, exec, string, source, regExp);
493 // FIXME: This is wrong because we may be called directly from the FTL.
494 // https://bugs.webkit.org/show_bug.cgi?id=154874
495 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
497 size_t lastIndex = 0;
498 unsigned startPosition = 0;
500 Vector<StringRange, 16> sourceRanges;
501 Vector<String, 16> replacements;
503 // This is either a loop (if global is set) or a one-way (if not).
504 if (global && callType == CallType::JS) {
505 // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
506 int argCount = regExp->numSubpatterns() + 1 + 2;
507 JSFunction* func = jsCast<JSFunction*>(replaceValue);
508 CachedCall cachedCall(exec, func, argCount);
509 if (exec->hadException())
510 return JSValue::encode(jsUndefined());
511 if (source.is8Bit()) {
514 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
518 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
521 for (; i < regExp->numSubpatterns() + 1; ++i) {
522 int matchStart = ovector[i * 2];
523 int matchLen = ovector[i * 2 + 1] - matchStart;
526 cachedCall.setArgument(i, jsUndefined());
528 cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
531 cachedCall.setArgument(i++, jsNumber(result.start));
532 cachedCall.setArgument(i++, string);
534 cachedCall.setThis(jsUndefined());
535 JSValue jsResult = cachedCall.call();
536 replacements.append(jsResult.toString(exec)->value(exec));
537 if (exec->hadException())
538 return JSValue::encode(jsUndefined());
540 lastIndex = result.end;
541 startPosition = lastIndex;
543 // special case of empty match
544 if (result.empty()) {
546 if (startPosition > sourceLen)
553 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
557 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
560 for (; i < regExp->numSubpatterns() + 1; ++i) {
561 int matchStart = ovector[i * 2];
562 int matchLen = ovector[i * 2 + 1] - matchStart;
565 cachedCall.setArgument(i, jsUndefined());
567 cachedCall.setArgument(i, jsSubstring(&vm, source, matchStart, matchLen));
570 cachedCall.setArgument(i++, jsNumber(result.start));
571 cachedCall.setArgument(i++, string);
573 cachedCall.setThis(jsUndefined());
574 JSValue jsResult = cachedCall.call();
575 replacements.append(jsResult.toString(exec)->value(exec));
576 if (exec->hadException())
577 return JSValue::encode(jsUndefined());
579 lastIndex = result.end;
580 startPosition = lastIndex;
582 // special case of empty match
583 if (result.empty()) {
585 if (startPosition > sourceLen)
593 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
597 if (callType != CallType::None) {
598 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
600 MarkedArgumentBuffer args;
602 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
603 int matchStart = ovector[i * 2];
604 int matchLen = ovector[i * 2 + 1] - matchStart;
607 args.append(jsUndefined());
609 args.append(jsSubstring(exec, source, matchStart, matchLen));
612 args.append(jsNumber(result.start));
615 replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
616 if (exec->hadException())
617 return JSValue::encode(jsUndefined());
619 int replLen = replacementString.length();
620 if (lastIndex < result.start || replLen) {
621 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
624 replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
626 replacements.append(String());
630 lastIndex = result.end;
631 startPosition = lastIndex;
633 // special case of empty match
634 if (result.empty()) {
636 if (startPosition > sourceLen)
642 if (!lastIndex && replacements.isEmpty())
643 return JSValue::encode(string);
645 if (static_cast<unsigned>(lastIndex) < sourceLen)
646 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
648 return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
651 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
652 ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
655 NativeCallFrameTracer tracer(&vm, exec);
657 RegExp* regExp = searchValue->regExp();
658 if (regExp->global()) {
659 // ES5.1 15.5.4.10 step 8.a.
660 searchValue->setLastIndex(exec, 0);
661 if (exec->hadException())
662 return JSValue::encode(jsUndefined());
663 return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
667 String replacementString = emptyString();
668 return replaceUsingRegExpSearch(
669 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
672 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
673 ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
676 NativeCallFrameTracer tracer(&vm, exec);
679 String replacementString = replaceString->value(exec);
680 return replaceUsingRegExpSearch(
681 vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
684 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
686 String replacementString;
688 CallType callType = getCallData(replaceValue, callData);
689 if (callType == CallType::None) {
690 replacementString = replaceValue.toString(exec)->value(exec);
691 if (exec->hadException())
692 return JSValue::encode(jsUndefined());
695 return replaceUsingRegExpSearch(
696 vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
699 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM&, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
701 const String& string = jsString->value(exec);
702 String searchString = searchValue.toString(exec)->value(exec);
703 if (exec->hadException())
704 return JSValue::encode(jsUndefined());
706 size_t matchStart = string.find(searchString);
708 if (matchStart == notFound)
709 return JSValue::encode(jsString);
712 CallType callType = getCallData(replaceValue, callData);
713 if (callType != CallType::None) {
714 MarkedArgumentBuffer args;
715 args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
716 args.append(jsNumber(matchStart));
717 args.append(jsString);
718 replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
719 if (exec->hadException())
720 return JSValue::encode(jsUndefined());
723 String replaceString = replaceValue.toString(exec)->value(exec);
724 if (exec->hadException())
725 return JSValue::encode(jsUndefined());
727 StringImpl* stringImpl = string.impl();
728 String leftPart(StringImpl::createSubstringSharingImpl(stringImpl, 0, matchStart));
730 size_t matchEnd = matchStart + searchString.impl()->length();
731 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
732 String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
734 size_t leftLength = stringImpl->length() - matchEnd;
735 String rightPart(StringImpl::createSubstringSharingImpl(stringImpl, matchEnd, leftLength));
736 return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
739 static inline bool checkObjectCoercible(JSValue thisValue)
741 if (thisValue.isString())
744 if (thisValue.isUndefinedOrNull())
747 if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isEnvironmentRecord())
753 template <typename CharacterType>
754 static inline JSValue repeatCharacter(ExecState* exec, CharacterType character, unsigned repeatCount)
756 CharacterType* buffer = nullptr;
757 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
759 return throwOutOfMemoryError(exec);
761 std::fill_n(buffer, repeatCount, character);
763 return jsString(exec, impl.release());
766 template <typename CharacterType>
767 static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
769 CharacterType* buffer = nullptr;
770 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
772 return throwOutOfMemoryError(&exec), nullptr;
774 std::fill_n(buffer, repeatCount, character);
776 return jsString(&exec, impl.release());
779 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
781 // For a string which length is single, instead of creating ropes,
782 // allocating a sequential buffer and fill with the repeated string for efficiency.
783 ASSERT(exec->argumentCount() == 2);
785 ASSERT(exec->uncheckedArgument(0).isString());
786 JSString* string = jsCast<JSString*>(exec->uncheckedArgument(0));
787 ASSERT(string->length() == 1);
789 if (!exec->uncheckedArgument(1).isInt32())
790 return JSValue::encode(jsNull());
792 int32_t repeatCount = exec->uncheckedArgument(1).asInt32();
793 UChar character = string->view(exec)[0];
794 if (!(character & ~0xff))
795 return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
796 return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
799 static inline bool repeatStringPattern(ExecState& exec, unsigned maxLength, JSString* string, JSRopeString::RopeBuilder& ropeBuilder)
801 unsigned repeatCount = maxLength / string->length();
802 unsigned remainingCharacters = maxLength - repeatCount * string->length();
803 for (unsigned i = 0; i < repeatCount; ++i) {
804 if (!ropeBuilder.append(string)) {
805 throwOutOfMemoryError(&exec);
809 if (remainingCharacters) {
810 JSString* substr = jsSubstring(&exec, string, 0, remainingCharacters);
811 if (!substr || !ropeBuilder.append(substr)) {
812 throwOutOfMemoryError(&exec);
819 enum class StringPaddingLocation { Start, End };
821 static EncodedJSValue padString(ExecState& exec, StringPaddingLocation paddingLocation)
823 JSValue thisValue = exec.thisValue();
824 if (!thisValue.requireObjectCoercible(&exec))
825 return JSValue::encode(jsUndefined());
826 JSString* thisString = thisValue.toString(&exec);
827 if (exec.hadException())
828 return JSValue::encode(jsUndefined());
830 double maxLengthAsDouble = exec.argument(0).toLength(&exec);
831 if (exec.hadException())
832 return JSValue::encode(jsUndefined());
833 ASSERT(maxLengthAsDouble >= 0.0);
834 ASSERT(maxLengthAsDouble == std::trunc(maxLengthAsDouble));
836 if (maxLengthAsDouble <= thisString->length())
837 return JSValue::encode(thisString);
839 if (maxLengthAsDouble > JSString::MaxLength)
840 return JSValue::encode(throwOutOfMemoryError(&exec));
842 unsigned maxLength = static_cast<unsigned>(maxLengthAsDouble);
844 JSValue fillString = exec.argument(1);
845 JSString* filler = nullptr;
846 if (!fillString.isUndefined()) {
847 filler = fillString.toString(&exec);
849 return JSValue::encode(jsUndefined());
852 unsigned fillLength = static_cast<unsigned>(maxLength) - thisString->length();
854 JSRopeString::RopeBuilder ropeBuilder(exec.vm());
855 if (paddingLocation == StringPaddingLocation::End) {
856 if (!ropeBuilder.append(thisString))
857 return JSValue::encode(throwOutOfMemoryError(&exec));
860 if (!filler || filler->length() <= 1) {
861 UChar character = filler && filler->length() ? filler->view(&exec)[0] : ' ';
862 if (!(character & ~0xff))
863 filler = repeatCharacter(exec, static_cast<LChar>(character), fillLength);
865 filler = repeatCharacter(exec, character, fillLength);
866 if (!filler || !ropeBuilder.append(filler))
867 return JSValue::encode(throwOutOfMemoryError(&exec));
868 ASSERT(filler->length() == fillLength);
870 if (!repeatStringPattern(exec, fillLength, filler, ropeBuilder))
871 return JSValue::encode(throwOutOfMemoryError(&exec));
874 if (paddingLocation == StringPaddingLocation::Start) {
875 if (!ropeBuilder.append(thisString))
876 return JSValue::encode(throwOutOfMemoryError(&exec));
878 ASSERT(!exec.hadException());
879 return JSValue::encode(ropeBuilder.release());
882 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadEnd(ExecState* exec)
884 return padString(*exec, StringPaddingLocation::End);
887 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState* exec)
889 return padString(*exec, StringPaddingLocation::Start);
892 ALWAYS_INLINE EncodedJSValue replace(
893 VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
895 if (searchValue.inherits(RegExpObject::info()))
896 return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
897 return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
900 ALWAYS_INLINE EncodedJSValue replace(
901 VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
903 if (!checkObjectCoercible(thisValue))
904 return throwVMTypeError(exec);
905 JSString* string = thisValue.toString(exec);
906 if (exec->hadException())
907 return JSValue::encode(jsUndefined());
908 return replace(vm, exec, string, searchValue, replaceValue);
911 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
913 return replace(exec->vm(), exec, exec->thisValue(), exec->argument(0), exec->argument(1));
916 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
917 ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
918 EncodedJSValue replaceValue)
921 NativeCallFrameTracer tracer(&vm, exec);
924 vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
925 JSValue::decode(replaceValue));
928 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
930 JSValue thisValue = exec->thisValue();
931 // Also used for valueOf.
933 if (thisValue.isString())
934 return JSValue::encode(thisValue);
936 if (thisValue.inherits(StringObject::info()))
937 return JSValue::encode(asStringObject(thisValue)->internalValue());
939 return throwVMTypeError(exec);
942 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
944 JSValue thisValue = exec->thisValue();
945 if (!checkObjectCoercible(thisValue))
946 return throwVMTypeError(exec);
947 JSString::SafeView string = thisValue.toString(exec)->view(exec);
948 JSValue a0 = exec->argument(0);
950 uint32_t i = a0.asUInt32();
951 if (i < string.length())
952 return JSValue::encode(jsSingleCharacterString(exec, string[i]));
953 return JSValue::encode(jsEmptyString(exec));
955 double dpos = a0.toInteger(exec);
956 if (dpos >= 0 && dpos < string.length())
957 return JSValue::encode(jsSingleCharacterString(exec, string[static_cast<unsigned>(dpos)]));
958 return JSValue::encode(jsEmptyString(exec));
961 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
963 JSValue thisValue = exec->thisValue();
964 if (!checkObjectCoercible(thisValue))
965 return throwVMTypeError(exec);
966 JSString::SafeView string = thisValue.toString(exec)->view(exec);
967 JSValue a0 = exec->argument(0);
969 uint32_t i = a0.asUInt32();
970 if (i < string.length())
971 return JSValue::encode(jsNumber(string[i]));
972 return JSValue::encode(jsNaN());
974 double dpos = a0.toInteger(exec);
975 if (dpos >= 0 && dpos < string.length())
976 return JSValue::encode(jsNumber(string[static_cast<int>(dpos)]));
977 return JSValue::encode(jsNaN());
980 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
982 RELEASE_ASSERT(position < length);
984 return string.characters8()[position];
986 U16_NEXT(string.characters16(), position, length, character);
990 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
992 JSValue thisValue = exec->thisValue();
993 if (!checkObjectCoercible(thisValue))
994 return throwVMTypeError(exec);
996 String string = thisValue.toWTFString(exec);
997 unsigned length = string.length();
999 JSValue argument0 = exec->argument(0);
1000 if (argument0.isUInt32()) {
1001 unsigned position = argument0.asUInt32();
1002 if (position < length)
1003 return JSValue::encode(jsNumber(codePointAt(string, position, length)));
1004 return JSValue::encode(jsUndefined());
1007 if (UNLIKELY(exec->hadException()))
1008 return JSValue::encode(jsUndefined());
1010 double doublePosition = argument0.toInteger(exec);
1011 if (doublePosition >= 0 && doublePosition < length)
1012 return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
1013 return JSValue::encode(jsUndefined());
1016 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
1018 JSValue thisValue = exec->thisValue();
1019 if (thisValue.isString() && exec->argumentCount() == 1)
1020 return JSValue::encode(jsString(exec, asString(thisValue), exec->uncheckedArgument(0).toString(exec)));
1022 if (!checkObjectCoercible(thisValue))
1023 return throwVMTypeError(exec);
1024 return JSValue::encode(jsStringFromArguments(exec, thisValue));
1027 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
1029 JSValue thisValue = exec->thisValue();
1030 if (!checkObjectCoercible(thisValue))
1031 return throwVMTypeError(exec);
1033 JSValue a0 = exec->argument(0);
1034 JSValue a1 = exec->argument(1);
1036 JSString* thisJSString = thisValue.toString(exec);
1037 JSString* otherJSString = a0.toString(exec);
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 size_t result = thisJSString->view(exec).get().find(otherJSString->view(exec).get(), pos);
1059 if (result == notFound)
1060 return JSValue::encode(jsNumber(-1));
1061 return JSValue::encode(jsNumber(result));
1064 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
1066 JSValue thisValue = exec->thisValue();
1067 if (!checkObjectCoercible(thisValue))
1068 return throwVMTypeError(exec);
1070 JSValue a0 = exec->argument(0);
1071 JSValue a1 = exec->argument(1);
1073 JSString* thisJSString = thisValue.toString(exec);
1074 unsigned len = thisJSString->length();
1075 JSString* otherJSString = a0.toString(exec);
1077 double dpos = a1.toIntegerPreserveNaN(exec);
1078 unsigned startPosition;
1081 else if (!(dpos <= len)) // true for NaN
1082 startPosition = len;
1084 startPosition = static_cast<unsigned>(dpos);
1086 if (len < otherJSString->length())
1087 return JSValue::encode(jsNumber(-1));
1089 String thisString = thisJSString->value(exec);
1090 String otherString = otherJSString->value(exec);
1093 result = thisString.startsWith(otherString) ? 0 : notFound;
1095 result = thisString.reverseFind(otherString, startPosition);
1096 if (result == notFound)
1097 return JSValue::encode(jsNumber(-1));
1098 return JSValue::encode(jsNumber(result));
1101 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1103 JSValue thisValue = exec->thisValue();
1104 if (!checkObjectCoercible(thisValue))
1105 return throwVMTypeError(exec);
1106 String s = thisValue.toString(exec)->value(exec);
1107 if (exec->hadException())
1108 return JSValue::encode(jsUndefined());
1110 int len = s.length();
1111 RELEASE_ASSERT(len >= 0);
1113 JSValue a0 = exec->argument(0);
1114 JSValue a1 = exec->argument(1);
1116 // The arg processing is very much like ArrayProtoFunc::Slice
1117 double start = a0.toInteger(exec);
1118 double end = a1.isUndefined() ? len : a1.toInteger(exec);
1119 double from = start < 0 ? len + start : start;
1120 double to = end < 0 ? len + end : end;
1121 if (to > from && to > 0 && from < len) {
1126 return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1129 return JSValue::encode(jsEmptyString(exec));
1132 // Return true in case of early return (resultLength got to limitLength).
1133 template<typename CharacterType>
1134 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)
1137 size_t matchPosition;
1138 const CharacterType* characters = string->characters<CharacterType>();
1139 // 13. Repeat, while q != s
1140 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1141 // b. If z is failure, then let q = q+1.
1142 // c. Else, z is not failure
1143 while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1144 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1145 // through q (exclusive).
1146 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1147 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1148 result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1149 // 3. Increment lengthA by 1.
1150 // 4. If lengthA == lim, return A.
1151 if (++resultLength == limitLength)
1156 position = matchPosition + 1;
1161 // ES 21.1.3.17 String.prototype.split(separator, limit)
1162 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
1164 JSValue thisValue = exec->thisValue();
1165 ASSERT(checkObjectCoercible(thisValue));
1167 // 3. Let S be the result of calling ToString, giving it the this value as its argument.
1168 // 7. Let s be the number of characters in S.
1169 String input = thisValue.toString(exec)->value(exec);
1170 if (exec->hadException())
1171 return JSValue::encode(jsUndefined());
1172 ASSERT(!input.isNull());
1174 // 4. Let A be a new array created as if by the expression new Array()
1175 // where Array is the standard built-in constructor with that name.
1176 JSArray* result = constructEmptyArray(exec, 0);
1178 // 5. Let lengthA be 0.
1179 unsigned resultLength = 0;
1181 // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1182 JSValue limitValue = exec->uncheckedArgument(1);
1183 unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1186 size_t position = 0;
1188 // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1189 // otherwise let R = ToString(separator).
1190 JSValue separatorValue = exec->uncheckedArgument(0);
1191 { // FIXME: Keeping this indentation here to minimize the diff. Will unindent and remove this later.
1192 String separator = separatorValue.toString(exec)->value(exec);
1193 if (exec->hadException())
1194 return JSValue::encode(jsUndefined());
1196 // 10. If lim == 0, return A.
1198 return JSValue::encode(result);
1200 // 11. If separator is undefined, then
1201 if (separatorValue.isUndefined()) {
1202 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1203 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1205 return JSValue::encode(result);
1208 // 12. If s == 0, then
1209 if (input.isEmpty()) {
1210 // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
1211 // b. If z is not false, return A.
1212 // c. Call CreateDataProperty(A, "0", S).
1214 if (!separator.isEmpty())
1215 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1216 return JSValue::encode(result);
1219 // Optimized case for splitting on the empty string.
1220 if (separator.isEmpty()) {
1221 limit = std::min(limit, input.length());
1222 // Zero limt/input length handled in steps 9/11 respectively, above.
1226 result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1227 } while (++position < limit);
1229 return JSValue::encode(result);
1233 // -separator length == 1, 8 bits
1234 // -separator length == 1, 16 bits
1235 // -separator length > 1
1236 StringImpl* stringImpl = input.impl();
1237 StringImpl* separatorImpl = separator.impl();
1238 size_t separatorLength = separatorImpl->length();
1240 if (separatorLength == 1) {
1241 UChar separatorCharacter;
1242 if (separatorImpl->is8Bit())
1243 separatorCharacter = separatorImpl->characters8()[0];
1245 separatorCharacter = separatorImpl->characters16()[0];
1247 if (stringImpl->is8Bit()) {
1248 if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1249 return JSValue::encode(result);
1251 if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1252 return JSValue::encode(result);
1256 size_t matchPosition;
1257 // 14. Repeat, while q != s
1258 // a. let e be SplitMatch(S, q, R).
1259 // b. If e is failure, then let q = q+1.
1260 // c. Else, e is an integer index <= s.
1261 while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1262 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1263 // through q (exclusive).
1264 // 2. Call CreateDataProperty(A, ToString(lengthA), T).
1265 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1266 // 3. Increment lengthA by 1.
1267 // 4. If lengthA == lim, return A.
1268 if (++resultLength == limit)
1269 return JSValue::encode(result);
1273 position = matchPosition + separator.length();
1276 } // FIXME: Keeping this indentation here to minimize the diff. Will unindent and remove this later.
1278 // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1279 // through s (exclusive).
1280 // 16. Call CreateDataProperty(A, ToString(lengthA), T).
1281 result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1284 return JSValue::encode(result);
1287 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1289 JSValue thisValue = exec->thisValue();
1290 if (!checkObjectCoercible(thisValue))
1291 return throwVMTypeError(exec);
1293 JSString* jsString = 0;
1295 if (thisValue.isString()) {
1296 jsString = jsCast<JSString*>(thisValue.asCell());
1297 len = jsString->length();
1299 uString = thisValue.toString(exec)->value(exec);
1300 if (exec->hadException())
1301 return JSValue::encode(jsUndefined());
1302 len = uString.length();
1305 JSValue a0 = exec->argument(0);
1306 JSValue a1 = exec->argument(1);
1308 double start = a0.toInteger(exec);
1309 double length = a1.isUndefined() ? len : a1.toInteger(exec);
1310 if (start >= len || length <= 0)
1311 return JSValue::encode(jsEmptyString(exec));
1317 if (start + length > len)
1318 length = len - start;
1319 unsigned substringStart = static_cast<unsigned>(start);
1320 unsigned substringLength = static_cast<unsigned>(length);
1322 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1323 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1326 EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
1328 // @substrInternal should not have any observable side effects (e.g. it should not call
1329 // GetMethod(..., @@toPrimitive) on the thisValue).
1331 // It is ok to use the default stringProtoFuncSubstr as the implementation of
1332 // @substrInternal because @substrInternal will only be called by builtins, which will
1333 // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
1334 // will not need to call toString() on the thisValue, and there will be no observable
1336 #if !ASSERT_DISABLED
1337 JSValue thisValue = exec->thisValue();
1338 ASSERT(thisValue.isString());
1340 return stringProtoFuncSubstr(exec);
1343 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1345 JSValue thisValue = exec->thisValue();
1346 if (!checkObjectCoercible(thisValue))
1347 return throwVMTypeError(exec);
1349 JSString* jsString = thisValue.toString(exec);
1350 if (exec->hadException())
1351 return JSValue::encode(jsUndefined());
1353 JSValue a0 = exec->argument(0);
1354 JSValue a1 = exec->argument(1);
1355 int len = jsString->length();
1356 RELEASE_ASSERT(len >= 0);
1358 double start = a0.toNumber(exec);
1360 if (!(start >= 0)) // check for negative values or NaN
1362 else if (start > len)
1364 if (a1.isUndefined())
1367 end = a1.toNumber(exec);
1368 if (!(end >= 0)) // check for negative values or NaN
1378 unsigned substringStart = static_cast<unsigned>(start);
1379 unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1380 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1383 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1385 JSValue thisValue = exec->thisValue();
1386 if (!checkObjectCoercible(thisValue))
1387 return throwVMTypeError(exec);
1388 JSString* sVal = thisValue.toString(exec);
1389 const String& s = sVal->value(exec);
1390 String lowercasedString = s.convertToLowercaseWithoutLocale();
1391 if (lowercasedString.impl() == s.impl())
1392 return JSValue::encode(sVal);
1393 return JSValue::encode(jsString(exec, lowercasedString));
1396 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1398 JSValue thisValue = exec->thisValue();
1399 if (!checkObjectCoercible(thisValue))
1400 return throwVMTypeError(exec);
1401 JSString* sVal = thisValue.toString(exec);
1402 const String& s = sVal->value(exec);
1403 String uppercasedString = s.convertToUppercaseWithoutLocale();
1404 if (uppercasedString.impl() == s.impl())
1405 return JSValue::encode(sVal);
1406 return JSValue::encode(jsString(exec, uppercasedString));
1409 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1411 JSValue thisValue = exec->thisValue();
1412 if (!checkObjectCoercible(thisValue))
1413 return throwVMTypeError(exec);
1414 String s = thisValue.toString(exec)->value(exec);
1415 if (exec->hadException())
1416 return JSValue::encode(jsUndefined());
1418 JSValue a0 = exec->argument(0);
1419 String str = a0.toString(exec)->value(exec);
1420 if (exec->hadException())
1421 return JSValue::encode(jsUndefined());
1422 return JSValue::encode(jsNumber(Collator().collate(s, str)));
1426 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1428 // 1. Let O be RequireObjectCoercible(this value).
1429 JSValue thisValue = state->thisValue();
1430 if (!checkObjectCoercible(thisValue))
1431 return throwVMTypeError(state);
1433 // 2. Let S be ToString(O).
1434 JSString* sVal = thisValue.toString(state);
1435 const String& s = sVal->value(state);
1437 // 3. ReturnIfAbrupt(S).
1438 if (state->hadException())
1439 return JSValue::encode(jsUndefined());
1441 // Optimization for empty strings.
1443 return JSValue::encode(sVal);
1445 // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1446 Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1448 // 5. ReturnIfAbrupt(requestedLocales).
1449 if (state->hadException())
1450 return JSValue::encode(jsUndefined());
1452 // 6. Let len be the number of elements in requestedLocales.
1453 size_t len = requestedLocales.size();
1455 // 7. If len > 0, then
1456 // a. Let requestedLocale be the first element of requestedLocales.
1458 // a. Let requestedLocale be DefaultLocale().
1459 String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1461 // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1462 String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1464 // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1465 // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1466 const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1468 // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1469 String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1471 // 12. If locale is undefined, let locale be "und".
1472 if (locale.isNull())
1473 locale = ASCIILiteral("und");
1475 CString utf8LocaleBuffer = locale.utf8();
1476 const StringView view(s);
1477 const int32_t viewLength = view.length();
1479 // Delegate the following steps to icu u_strToLower or u_strToUpper.
1480 // 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.
1481 // 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).
1482 // 15. Let cuList be a new List.
1483 // 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.
1484 // 17. Let L be a String whose elements are, in order, the elements of cuList.
1486 // Most strings lower/upper case will be the same size as original, so try that first.
1487 UErrorCode error(U_ZERO_ERROR);
1488 Vector<UChar> buffer(viewLength);
1490 const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1491 if (U_SUCCESS(error))
1492 lower = String(buffer.data(), resultLength);
1493 else if (error == U_BUFFER_OVERFLOW_ERROR) {
1494 // Converted case needs more space than original. Try again.
1495 UErrorCode error(U_ZERO_ERROR);
1496 Vector<UChar> buffer(resultLength);
1497 convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1498 if (U_FAILURE(error))
1499 return throwVMTypeError(state, u_errorName(error));
1500 lower = String(buffer.data(), resultLength);
1502 return throwVMTypeError(state, u_errorName(error));
1505 return JSValue::encode(jsString(state, lower));
1508 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1510 // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1511 // http://ecma-international.org/publications/standards/Ecma-402.htm
1512 return toLocaleCase(state, u_strToLower);
1515 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1517 // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1518 // http://ecma-international.org/publications/standards/Ecma-402.htm
1519 // 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.
1520 return toLocaleCase(state, u_strToUpper);
1522 #endif // ENABLE(INTL)
1524 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1526 JSValue thisValue = exec->thisValue();
1527 if (!checkObjectCoercible(thisValue))
1528 return throwVMTypeError(exec);
1529 String s = thisValue.toString(exec)->value(exec);
1530 if (exec->hadException())
1531 return JSValue::encode(jsUndefined());
1532 return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1535 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1537 JSValue thisValue = exec->thisValue();
1538 if (!checkObjectCoercible(thisValue))
1539 return throwVMTypeError(exec);
1540 String s = thisValue.toString(exec)->value(exec);
1541 if (exec->hadException())
1542 return JSValue::encode(jsUndefined());
1543 return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1546 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1548 JSValue thisValue = exec->thisValue();
1549 if (!checkObjectCoercible(thisValue))
1550 return throwVMTypeError(exec);
1551 String s = thisValue.toString(exec)->value(exec);
1552 if (exec->hadException())
1553 return JSValue::encode(jsUndefined());
1554 return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1557 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1559 JSValue thisValue = exec->thisValue();
1560 if (!checkObjectCoercible(thisValue))
1561 return throwVMTypeError(exec);
1562 String s = thisValue.toString(exec)->value(exec);
1563 if (exec->hadException())
1564 return JSValue::encode(jsUndefined());
1565 return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1568 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1570 JSValue thisValue = exec->thisValue();
1571 if (!checkObjectCoercible(thisValue))
1572 return throwVMTypeError(exec);
1573 String s = thisValue.toString(exec)->value(exec);
1574 if (exec->hadException())
1575 return JSValue::encode(jsUndefined());
1576 return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1579 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1581 JSValue thisValue = exec->thisValue();
1582 if (!checkObjectCoercible(thisValue))
1583 return throwVMTypeError(exec);
1584 String s = thisValue.toString(exec)->value(exec);
1585 if (exec->hadException())
1586 return JSValue::encode(jsUndefined());
1587 return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1590 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1592 JSValue thisValue = exec->thisValue();
1593 if (!checkObjectCoercible(thisValue))
1594 return throwVMTypeError(exec);
1595 String s = thisValue.toString(exec)->value(exec);
1596 if (exec->hadException())
1597 return JSValue::encode(jsUndefined());
1598 return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1601 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1603 JSValue thisValue = exec->thisValue();
1604 if (!checkObjectCoercible(thisValue))
1605 return throwVMTypeError(exec);
1606 String s = thisValue.toString(exec)->value(exec);
1607 if (exec->hadException())
1608 return JSValue::encode(jsUndefined());
1609 return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1612 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
1614 JSValue thisValue = exec->thisValue();
1615 if (!checkObjectCoercible(thisValue))
1616 return throwVMTypeError(exec);
1617 String s = thisValue.toString(exec)->value(exec);
1618 if (exec->hadException())
1619 return JSValue::encode(jsUndefined());
1620 return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1623 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1625 JSValue thisValue = exec->thisValue();
1626 if (!checkObjectCoercible(thisValue))
1627 return throwVMTypeError(exec);
1628 String s = thisValue.toString(exec)->value(exec);
1629 if (exec->hadException())
1630 return JSValue::encode(jsUndefined());
1632 JSValue a0 = exec->argument(0);
1633 String color = a0.toWTFString(exec);
1634 color.replaceWithLiteral('"', """);
1636 return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1639 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1641 JSValue thisValue = exec->thisValue();
1642 if (!checkObjectCoercible(thisValue))
1643 return throwVMTypeError(exec);
1644 String s = thisValue.toString(exec)->value(exec);
1645 if (exec->hadException())
1646 return JSValue::encode(jsUndefined());
1648 JSValue a0 = exec->argument(0);
1650 uint32_t smallInteger;
1651 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1652 unsigned stringSize = s.length();
1653 unsigned bufferSize = 22 + stringSize;
1654 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1656 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1658 return JSValue::encode(jsUndefined());
1671 buffer[12] = '0' + smallInteger;
1674 StringView(s).getCharactersWithUpconvert(&buffer[15]);
1675 buffer[15 + stringSize] = '<';
1676 buffer[16 + stringSize] = '/';
1677 buffer[17 + stringSize] = 'f';
1678 buffer[18 + stringSize] = 'o';
1679 buffer[19 + stringSize] = 'n';
1680 buffer[20 + stringSize] = 't';
1681 buffer[21 + stringSize] = '>';
1682 return JSValue::encode(jsNontrivialString(exec, impl));
1685 String fontSize = a0.toWTFString(exec);
1686 fontSize.replaceWithLiteral('"', """);
1688 return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1691 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1693 JSValue thisValue = exec->thisValue();
1694 if (!checkObjectCoercible(thisValue))
1695 return throwVMTypeError(exec);
1696 String s = thisValue.toString(exec)->value(exec);
1697 if (exec->hadException())
1698 return JSValue::encode(jsUndefined());
1700 JSValue a0 = exec->argument(0);
1701 String anchor = a0.toWTFString(exec);
1702 anchor.replaceWithLiteral('"', """);
1704 return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1707 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1709 JSValue thisValue = exec->thisValue();
1710 if (!checkObjectCoercible(thisValue))
1711 return throwVMTypeError(exec);
1712 String s = thisValue.toString(exec)->value(exec);
1713 if (exec->hadException())
1714 return JSValue::encode(jsUndefined());
1716 JSValue a0 = exec->argument(0);
1717 String linkText = a0.toWTFString(exec);
1718 linkText.replaceWithLiteral('"', """);
1720 unsigned linkTextSize = linkText.length();
1721 unsigned stringSize = s.length();
1722 unsigned bufferSize = 15 + linkTextSize + stringSize;
1723 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1725 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1727 return JSValue::encode(jsUndefined());
1737 StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1738 buffer[9 + linkTextSize] = '"';
1739 buffer[10 + linkTextSize] = '>';
1740 StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1741 buffer[11 + linkTextSize + stringSize] = '<';
1742 buffer[12 + linkTextSize + stringSize] = '/';
1743 buffer[13 + linkTextSize + stringSize] = 'a';
1744 buffer[14 + linkTextSize + stringSize] = '>';
1745 return JSValue::encode(jsNontrivialString(exec, impl));
1753 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1755 if (!checkObjectCoercible(thisValue))
1756 return throwTypeError(exec);
1757 String str = thisValue.toString(exec)->value(exec);
1758 if (exec->hadException())
1759 return jsUndefined();
1762 if (trimKind & TrimLeft) {
1763 while (left < str.length() && isStrWhiteSpace(str[left]))
1766 unsigned right = str.length();
1767 if (trimKind & TrimRight) {
1768 while (right > left && isStrWhiteSpace(str[right - 1]))
1772 // Don't gc allocate a new string if we don't have to.
1773 if (left == 0 && right == str.length() && thisValue.isString())
1776 return jsString(exec, str.substringSharingImpl(left, right - left));
1779 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1781 JSValue thisValue = exec->thisValue();
1782 return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1785 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1787 JSValue thisValue = exec->thisValue();
1788 return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1791 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1793 JSValue thisValue = exec->thisValue();
1794 return JSValue::encode(trimString(exec, thisValue, TrimRight));
1797 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1803 return static_cast<unsigned>(value);
1806 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1808 JSValue thisValue = exec->thisValue();
1809 if (!checkObjectCoercible(thisValue))
1810 return throwVMTypeError(exec);
1812 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1813 if (exec->hadException())
1814 return JSValue::encode(jsUndefined());
1816 JSValue a0 = exec->argument(0);
1817 VM& vm = exec->vm();
1818 bool isRegularExpression = isRegExp(vm, exec, a0);
1820 return JSValue::encode(JSValue());
1821 if (isRegularExpression)
1822 return throwVMTypeError(exec, "Argument to String.prototype.startsWith cannot be a RegExp");
1824 String searchString = a0.toString(exec)->value(exec);
1825 if (exec->hadException())
1826 return JSValue::encode(jsUndefined());
1828 JSValue positionArg = exec->argument(1);
1830 if (positionArg.isInt32())
1831 start = std::max(0, positionArg.asInt32());
1833 unsigned length = stringToSearchIn.length();
1834 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1835 if (exec->hadException())
1836 return JSValue::encode(jsUndefined());
1839 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1842 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1844 JSValue thisValue = exec->thisValue();
1845 if (!checkObjectCoercible(thisValue))
1846 return throwVMTypeError(exec);
1848 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1849 if (exec->hadException())
1850 return JSValue::encode(jsUndefined());
1852 JSValue a0 = exec->argument(0);
1853 VM& vm = exec->vm();
1854 bool isRegularExpression = isRegExp(vm, exec, a0);
1856 return JSValue::encode(JSValue());
1857 if (isRegularExpression)
1858 return throwVMTypeError(exec, "Argument to String.prototype.endsWith cannot be a RegExp");
1860 String searchString = a0.toString(exec)->value(exec);
1861 if (exec->hadException())
1862 return JSValue::encode(jsUndefined());
1864 unsigned length = stringToSearchIn.length();
1866 JSValue endPositionArg = exec->argument(1);
1867 unsigned end = length;
1868 if (endPositionArg.isInt32())
1869 end = std::max(0, endPositionArg.asInt32());
1870 else if (!endPositionArg.isUndefined()) {
1871 end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1872 if (exec->hadException())
1873 return JSValue::encode(jsUndefined());
1876 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1879 static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
1882 if (positionArg.isInt32())
1883 start = std::max(0, positionArg.asInt32());
1885 unsigned length = stringToSearchIn.length();
1886 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1888 return JSValue::encode(jsUndefined());
1891 return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1894 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1896 JSValue thisValue = exec->thisValue();
1897 if (!checkObjectCoercible(thisValue))
1898 return throwVMTypeError(exec);
1900 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1901 if (exec->hadException())
1902 return JSValue::encode(jsUndefined());
1904 JSValue a0 = exec->argument(0);
1905 VM& vm = exec->vm();
1906 bool isRegularExpression = isRegExp(vm, exec, a0);
1908 return JSValue::encode(JSValue());
1909 if (isRegularExpression)
1910 return throwVMTypeError(exec, "Argument to String.prototype.includes cannot be a RegExp");
1912 String searchString = a0.toString(exec)->value(exec);
1913 if (exec->hadException())
1914 return JSValue::encode(jsUndefined());
1916 JSValue positionArg = exec->argument(1);
1918 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1921 EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
1923 JSValue thisValue = exec->thisValue();
1924 ASSERT(checkObjectCoercible(thisValue));
1926 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1927 if (exec->hadException())
1928 return JSValue::encode(jsUndefined());
1930 JSValue a0 = exec->uncheckedArgument(0);
1931 VM& vm = exec->vm();
1932 String searchString = a0.toString(exec)->value(exec);
1933 if (exec->hadException())
1934 return JSValue::encode(jsUndefined());
1936 JSValue positionArg = exec->argument(1);
1938 return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
1941 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
1943 JSValue thisValue = exec->thisValue();
1944 if (!checkObjectCoercible(thisValue))
1945 return throwVMTypeError(exec);
1946 JSString* string = thisValue.toString(exec);
1947 return JSValue::encode(JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), string));
1950 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
1952 UErrorCode status = U_ZERO_ERROR;
1953 int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
1955 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1956 // The behavior is not specified when normalize fails.
1957 // Now we throw a type erorr since it seems that the contents of the string are invalid.
1958 return throwTypeError(exec);
1961 UChar* buffer = nullptr;
1962 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
1964 return throwOutOfMemoryError(exec);
1966 status = U_ZERO_ERROR;
1967 unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
1968 if (U_FAILURE(status))
1969 return throwTypeError(exec);
1971 return jsString(exec, impl.release());
1974 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
1976 JSValue thisValue = exec->thisValue();
1977 if (!checkObjectCoercible(thisValue))
1978 return throwVMTypeError(exec);
1979 JSString::SafeView source = thisValue.toString(exec)->view(exec);
1980 if (exec->hadException())
1981 return JSValue::encode(jsUndefined());
1983 UNormalizationMode form = UNORM_NFC;
1984 // Verify that the argument is provided and is not undefined.
1985 if (!exec->argument(0).isUndefined()) {
1986 String formString = exec->uncheckedArgument(0).toString(exec)->value(exec);
1987 if (exec->hadException())
1988 return JSValue::encode(jsUndefined());
1990 if (formString == "NFC")
1992 else if (formString == "NFD")
1994 else if (formString == "NFKC")
1996 else if (formString == "NFKD")
1999 return throwVMError(exec, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2002 return JSValue::encode(normalize(exec, source.get().upconvertedCharacters(), source.length(), form));