2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 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"
47 #include <unicode/uconfig.h>
48 #include <unicode/unorm.h>
49 #include <unicode/ustring.h>
50 #include <wtf/ASCIICType.h>
51 #include <wtf/MathExtras.h>
52 #include <wtf/text/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 stringProtoFuncMatch(ExecState*);
69 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState*);
70 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
71 EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*);
72 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
73 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*);
74 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
75 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
76 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
77 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
78 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
79 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
80 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState*);
81 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
82 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
83 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
84 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
85 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
86 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
87 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
88 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
89 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
90 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
91 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
92 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
93 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
94 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
95 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
96 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
97 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState*);
98 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState*);
99 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*);
100 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState*);
101 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
105 #include "StringPrototype.lut.h"
109 const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, CREATE_METHOD_TABLE(StringPrototype) };
111 /* Source for StringConstructor.lut.h
112 @begin stringPrototypeTable
113 search JSBuiltin DontEnum|Function 1
118 StringPrototype::StringPrototype(VM& vm, Structure* structure)
119 : StringObject(vm, structure)
123 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
125 Base::finishCreation(vm, nameAndMessage);
126 ASSERT(inherits(info()));
128 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
129 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
130 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
131 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
132 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
133 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", stringProtoFuncConcat, DontEnum, 1);
134 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
135 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
136 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("match", stringProtoFuncMatch, DontEnum, 1);
137 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("repeat", stringProtoFuncRepeat, DontEnum, 1);
138 JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("replace", stringProtoFuncReplace, DontEnum, 2, StringPrototypeReplaceIntrinsic);
139 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
140 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("split", stringProtoFuncSplit, DontEnum, 2);
141 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
142 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
143 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
144 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
146 JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);
147 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
148 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, DontEnum, 0);
150 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
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 // Helper for producing a JSString for 'string', where 'string' was been produced by
198 // calling ToString on 'originalValue'. In cases where 'originalValue' already was a
199 // string primitive we can just use this, otherwise we need to allocate a new JSString.
200 static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const String& string)
202 if (originalValue.isString()) {
203 ASSERT(asString(originalValue)->value(exec) == string);
204 return asString(originalValue);
206 return jsString(exec, string);
209 // Helper that tries to use the JSString substring sharing mechanism if 'originalValue' is a JSString.
210 static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, const String& string, unsigned offset, unsigned length)
212 if (originalValue.isString()) {
213 ASSERT(asString(originalValue)->value(exec) == string);
214 return jsSubstring(exec, asString(originalValue), offset, length);
216 return jsSubstring(exec, string, offset, length);
219 static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
221 StringBuilder substitutedReplacement;
224 if (i + 1 == replacement.length())
227 UChar ref = replacement[i + 1];
231 substitutedReplacement.append(replacement.substring(offset, i - offset));
240 backrefStart = ovector[0];
241 backrefLength = ovector[1] - backrefStart;
242 } else if (ref == '`') {
244 backrefLength = ovector[0];
245 } else if (ref == '\'') {
246 backrefStart = ovector[1];
247 backrefLength = source.length() - backrefStart;
248 } else if (reg && ref >= '0' && ref <= '9') {
249 // 1- and 2-digit back references are allowed
250 unsigned backrefIndex = ref - '0';
251 if (backrefIndex > reg->numSubpatterns())
253 if (replacement.length() > i + 2) {
254 ref = replacement[i + 2];
255 if (ref >= '0' && ref <= '9') {
256 backrefIndex = 10 * backrefIndex + ref - '0';
257 if (backrefIndex > reg->numSubpatterns())
258 backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference
265 backrefStart = ovector[2 * backrefIndex];
266 backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
271 substitutedReplacement.append(replacement.substring(offset, i - offset));
274 if (backrefStart >= 0)
275 substitutedReplacement.append(source.substring(backrefStart, backrefLength));
276 } while ((i = replacement.find('$', i + 1)) != notFound);
278 if (replacement.length() - offset)
279 substitutedReplacement.append(replacement.substring(offset));
281 return substitutedReplacement.toString();
284 static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
286 size_t i = replacement.find('$');
287 if (UNLIKELY(i != notFound))
288 return substituteBackreferencesSlow(replacement, source, ovector, reg, i);
294 StringRange(int pos, int len)
308 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
310 if (rangeCount == 1) {
311 int sourceSize = source.length();
312 int position = substringRanges[0].position;
313 int length = substringRanges[0].length;
314 if (position <= 0 && length >= sourceSize)
316 // We could call String::substringSharingImpl(), but this would result in redundant checks.
317 return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
321 for (int i = 0; i < rangeCount; i++)
322 totalLength += substringRanges[i].length;
325 return jsEmptyString(exec);
327 if (source.is8Bit()) {
329 const LChar* sourceData = source.characters8();
330 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
332 return throwOutOfMemoryError(exec);
335 for (int i = 0; i < rangeCount; i++) {
336 if (int srcLen = substringRanges[i].length) {
337 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
342 return jsString(exec, impl.release());
346 const UChar* sourceData = source.characters16();
348 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
350 return throwOutOfMemoryError(exec);
353 for (int i = 0; i < rangeCount; i++) {
354 if (int srcLen = substringRanges[i].length) {
355 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
360 return jsString(exec, impl.release());
363 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
365 if (rangeCount == 1 && separatorCount == 0) {
366 int sourceSize = source.length();
367 int position = substringRanges[0].position;
368 int length = substringRanges[0].length;
369 if (position <= 0 && length >= sourceSize)
371 // We could call String::substringSharingImpl(), but this would result in redundant checks.
372 return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
375 Checked<int, RecordOverflow> totalLength = 0;
376 bool allSeparators8Bit = true;
377 for (int i = 0; i < rangeCount; i++)
378 totalLength += substringRanges[i].length;
379 for (int i = 0; i < separatorCount; i++) {
380 totalLength += separators[i].length();
381 if (separators[i].length() && !separators[i].is8Bit())
382 allSeparators8Bit = false;
384 if (totalLength.hasOverflowed())
385 return throwOutOfMemoryError(exec);
388 return jsEmptyString(exec);
390 if (source.is8Bit() && allSeparators8Bit) {
392 const LChar* sourceData = source.characters8();
394 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
396 return throwOutOfMemoryError(exec);
398 int maxCount = std::max(rangeCount, separatorCount);
400 for (int i = 0; i < maxCount; i++) {
401 if (i < rangeCount) {
402 if (int srcLen = substringRanges[i].length) {
403 StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen);
407 if (i < separatorCount) {
408 if (int sepLen = separators[i].length()) {
409 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
415 return jsString(exec, impl.release());
419 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
421 return throwOutOfMemoryError(exec);
423 int maxCount = std::max(rangeCount, separatorCount);
425 for (int i = 0; i < maxCount; i++) {
426 if (i < rangeCount) {
427 if (int srcLen = substringRanges[i].length) {
429 StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen);
431 StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen);
435 if (i < separatorCount) {
436 if (int sepLen = separators[i].length()) {
437 if (separators[i].is8Bit())
438 StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen);
440 StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen);
446 return jsString(exec, impl.release());
449 static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
451 size_t lastIndex = 0;
452 unsigned startPosition = 0;
454 Vector<StringRange, 16> sourceRanges;
455 VM* vm = &exec->vm();
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 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
467 lastIndex = result.end;
468 startPosition = lastIndex;
470 // special case of empty match
471 if (result.empty()) {
473 if (startPosition > sourceLen)
479 return JSValue::encode(string);
481 if (static_cast<unsigned>(lastIndex) < sourceLen)
482 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
484 return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
487 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
488 ExecState* exec, JSString* string, JSValue searchValue, CallData& callData, CallType callType,
489 String& replacementString, JSValue replaceValue)
491 const String& source = string->value(exec);
492 unsigned sourceLen = source.length();
493 if (exec->hadException())
494 return JSValue::encode(jsUndefined());
495 RegExpObject* regExpObject = asRegExpObject(searchValue);
496 RegExp* regExp = regExpObject->regExp();
497 bool global = regExp->global();
500 // ES5.1 15.5.4.10 step 8.a.
501 regExpObject->setLastIndex(exec, 0);
502 if (exec->hadException())
503 return JSValue::encode(jsUndefined());
505 if (callType == CallTypeNone && !replacementString.length())
506 return removeUsingRegExpSearch(exec, string, source, regExp);
509 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
511 size_t lastIndex = 0;
512 unsigned startPosition = 0;
514 Vector<StringRange, 16> sourceRanges;
515 Vector<String, 16> replacements;
517 // This is either a loop (if global is set) or a one-way (if not).
518 if (global && callType == CallTypeJS) {
519 // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
520 int argCount = regExp->numSubpatterns() + 1 + 2;
521 JSFunction* func = jsCast<JSFunction*>(replaceValue);
522 CachedCall cachedCall(exec, func, argCount);
523 if (exec->hadException())
524 return JSValue::encode(jsUndefined());
525 VM* vm = &exec->vm();
526 if (source.is8Bit()) {
529 MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
533 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
536 for (; i < regExp->numSubpatterns() + 1; ++i) {
537 int matchStart = ovector[i * 2];
538 int matchLen = ovector[i * 2 + 1] - matchStart;
541 cachedCall.setArgument(i, jsUndefined());
543 cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
546 cachedCall.setArgument(i++, jsNumber(result.start));
547 cachedCall.setArgument(i++, string);
549 cachedCall.setThis(jsUndefined());
550 JSValue jsResult = cachedCall.call();
551 replacements.append(jsResult.toString(exec)->value(exec));
552 if (exec->hadException())
553 return JSValue::encode(jsUndefined());
555 lastIndex = result.end;
556 startPosition = lastIndex;
558 // special case of empty match
559 if (result.empty()) {
561 if (startPosition > sourceLen)
568 MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
572 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
575 for (; i < regExp->numSubpatterns() + 1; ++i) {
576 int matchStart = ovector[i * 2];
577 int matchLen = ovector[i * 2 + 1] - matchStart;
580 cachedCall.setArgument(i, jsUndefined());
582 cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
585 cachedCall.setArgument(i++, jsNumber(result.start));
586 cachedCall.setArgument(i++, string);
588 cachedCall.setThis(jsUndefined());
589 JSValue jsResult = cachedCall.call();
590 replacements.append(jsResult.toString(exec)->value(exec));
591 if (exec->hadException())
592 return JSValue::encode(jsUndefined());
594 lastIndex = result.end;
595 startPosition = lastIndex;
597 // special case of empty match
598 if (result.empty()) {
600 if (startPosition > sourceLen)
606 VM* vm = &exec->vm();
609 MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
613 if (callType != CallTypeNone) {
614 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
616 MarkedArgumentBuffer args;
618 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
619 int matchStart = ovector[i * 2];
620 int matchLen = ovector[i * 2 + 1] - matchStart;
623 args.append(jsUndefined());
625 args.append(jsSubstring(exec, source, matchStart, matchLen));
628 args.append(jsNumber(result.start));
631 replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
632 if (exec->hadException())
633 return JSValue::encode(jsUndefined());
635 int replLen = replacementString.length();
636 if (lastIndex < result.start || replLen) {
637 sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
640 replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
642 replacements.append(String());
646 lastIndex = result.end;
647 startPosition = lastIndex;
649 // special case of empty match
650 if (result.empty()) {
652 if (startPosition > sourceLen)
658 if (!lastIndex && replacements.isEmpty())
659 return JSValue::encode(string);
661 if (static_cast<unsigned>(lastIndex) < sourceLen)
662 sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
664 return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
667 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
668 ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
671 String replacementString = replaceString->value(exec);
672 return replaceUsingRegExpSearch(
673 exec, thisValue, searchValue, callData, CallTypeNone, replacementString, replaceString);
676 static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
678 String replacementString;
680 CallType callType = getCallData(replaceValue, callData);
681 if (callType == CallTypeNone) {
682 replacementString = replaceValue.toString(exec)->value(exec);
683 if (exec->hadException())
684 return JSValue::encode(jsUndefined());
687 return replaceUsingRegExpSearch(
688 exec, string, searchValue, callData, callType, replacementString, replaceValue);
691 static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
693 const String& string = jsString->value(exec);
694 String searchString = searchValue.toString(exec)->value(exec);
695 if (exec->hadException())
696 return JSValue::encode(jsUndefined());
698 size_t matchStart = string.find(searchString);
700 if (matchStart == notFound)
701 return JSValue::encode(jsString);
704 CallType callType = getCallData(replaceValue, callData);
705 if (callType != CallTypeNone) {
706 MarkedArgumentBuffer args;
707 args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
708 args.append(jsNumber(matchStart));
709 args.append(jsString);
710 replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
711 if (exec->hadException())
712 return JSValue::encode(jsUndefined());
715 String replaceString = replaceValue.toString(exec)->value(exec);
716 if (exec->hadException())
717 return JSValue::encode(jsUndefined());
719 StringImpl* stringImpl = string.impl();
720 String leftPart(StringImpl::createSubstringSharingImpl(stringImpl, 0, matchStart));
722 size_t matchEnd = matchStart + searchString.impl()->length();
723 int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
724 String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
726 size_t leftLength = stringImpl->length() - matchEnd;
727 String rightPart(StringImpl::createSubstringSharingImpl(stringImpl, matchEnd, leftLength));
728 return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
731 static inline bool checkObjectCoercible(JSValue thisValue)
733 if (thisValue.isString())
736 if (thisValue.isUndefinedOrNull())
739 if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isEnvironmentRecord())
745 template <typename CharacterType>
746 static inline JSValue repeatCharacter(ExecState* exec, CharacterType character, unsigned repeatCount)
748 CharacterType* buffer = nullptr;
749 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
751 return throwOutOfMemoryError(exec);
753 std::fill_n(buffer, repeatCount, character);
755 return jsString(exec, impl.release());
758 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState* exec)
760 JSValue thisValue = exec->thisValue();
761 if (!checkObjectCoercible(thisValue))
762 return throwVMTypeError(exec);
764 JSString* string = thisValue.toString(exec);
765 if (exec->hadException())
766 return JSValue::encode(jsUndefined());
768 double repeatCountDouble = exec->argument(0).toInteger(exec);
769 if (exec->hadException())
770 return JSValue::encode(jsUndefined());
771 if (repeatCountDouble < 0 || std::isinf(repeatCountDouble))
772 return throwVMError(exec, createRangeError(exec, ASCIILiteral("repeat() argument must be greater than or equal to 0 and not be infinity")));
776 if (!string->length() || !repeatCountDouble)
777 return JSValue::encode(jsEmptyString(&vm));
779 if (repeatCountDouble == 1)
780 return JSValue::encode(string);
782 // JSString requires the limitation that its length is in the range of int32_t.
783 if (repeatCountDouble > std::numeric_limits<int32_t>::max() / string->length())
784 return JSValue::encode(throwOutOfMemoryError(exec));
785 unsigned repeatCount = static_cast<unsigned>(repeatCountDouble);
787 // For a string which length is small, instead of creating ropes,
788 // allocating a sequential buffer and fill with the repeated string for efficiency.
789 if (string->length() == 1) {
790 String repeatedString = string->value(exec);
791 UChar character = repeatedString.at(0);
792 if (!(character & ~0xff))
793 return JSValue::encode(repeatCharacter(exec, static_cast<LChar>(character), repeatCount));
794 return JSValue::encode(repeatCharacter(exec, character, repeatCount));
797 JSRopeString::RopeBuilder ropeBuilder(vm);
798 for (unsigned i = 0; i < repeatCount; ++i) {
799 if (!ropeBuilder.append(string))
800 return JSValue::encode(throwOutOfMemoryError(exec));
802 return JSValue::encode(ropeBuilder.release());
805 ALWAYS_INLINE EncodedJSValue replace(
806 ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
808 if (searchValue.inherits(RegExpObject::info()))
809 return replaceUsingRegExpSearch(exec, string, searchValue, replaceValue);
810 return replaceUsingStringSearch(exec, string, searchValue, replaceValue);
813 ALWAYS_INLINE EncodedJSValue replace(
814 ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
816 if (!checkObjectCoercible(thisValue))
817 return throwVMTypeError(exec);
818 JSString* string = thisValue.toString(exec);
819 if (exec->hadException())
820 return JSValue::encode(jsUndefined());
821 return replace(exec, string, searchValue, replaceValue);
824 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
826 return replace(exec, exec->thisValue(), exec->argument(0), exec->argument(1));
829 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
830 ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
831 EncodedJSValue replaceValue)
834 exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
835 JSValue::decode(replaceValue));
838 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
840 JSValue thisValue = exec->thisValue();
841 // Also used for valueOf.
843 if (thisValue.isString())
844 return JSValue::encode(thisValue);
846 if (thisValue.inherits(StringObject::info()))
847 return JSValue::encode(asStringObject(thisValue)->internalValue());
849 return throwVMTypeError(exec);
852 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
854 JSValue thisValue = exec->thisValue();
855 if (!checkObjectCoercible(thisValue))
856 return throwVMTypeError(exec);
857 JSString::SafeView string = thisValue.toString(exec)->view(exec);
858 JSValue a0 = exec->argument(0);
860 uint32_t i = a0.asUInt32();
861 if (i < string.length())
862 return JSValue::encode(jsSingleCharacterString(exec, string[i]));
863 return JSValue::encode(jsEmptyString(exec));
865 double dpos = a0.toInteger(exec);
866 if (dpos >= 0 && dpos < string.length())
867 return JSValue::encode(jsSingleCharacterString(exec, string[static_cast<unsigned>(dpos)]));
868 return JSValue::encode(jsEmptyString(exec));
871 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
873 JSValue thisValue = exec->thisValue();
874 if (!checkObjectCoercible(thisValue))
875 return throwVMTypeError(exec);
876 JSString::SafeView string = thisValue.toString(exec)->view(exec);
877 JSValue a0 = exec->argument(0);
879 uint32_t i = a0.asUInt32();
880 if (i < string.length())
881 return JSValue::encode(jsNumber(string[i]));
882 return JSValue::encode(jsNaN());
884 double dpos = a0.toInteger(exec);
885 if (dpos >= 0 && dpos < string.length())
886 return JSValue::encode(jsNumber(string[static_cast<int>(dpos)]));
887 return JSValue::encode(jsNaN());
890 static inline UChar32 codePointAt(const String& string, unsigned position, unsigned length)
892 RELEASE_ASSERT(position < length);
894 return string.characters8()[position];
896 U16_NEXT(string.characters16(), position, length, character);
900 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
902 JSValue thisValue = exec->thisValue();
903 if (!checkObjectCoercible(thisValue))
904 return throwVMTypeError(exec);
906 String string = thisValue.toWTFString(exec);
907 unsigned length = string.length();
909 JSValue argument0 = exec->argument(0);
910 if (argument0.isUInt32()) {
911 unsigned position = argument0.asUInt32();
912 if (position < length)
913 return JSValue::encode(jsNumber(codePointAt(string, position, length)));
914 return JSValue::encode(jsUndefined());
917 if (UNLIKELY(exec->hadException()))
918 return JSValue::encode(jsUndefined());
920 double doublePosition = argument0.toInteger(exec);
921 if (doublePosition >= 0 && doublePosition < length)
922 return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
923 return JSValue::encode(jsUndefined());
926 EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
928 JSValue thisValue = exec->thisValue();
929 if (thisValue.isString() && exec->argumentCount() == 1)
930 return JSValue::encode(jsString(exec, asString(thisValue), exec->uncheckedArgument(0).toString(exec)));
932 if (!checkObjectCoercible(thisValue))
933 return throwVMTypeError(exec);
934 return JSValue::encode(jsStringFromArguments(exec, thisValue));
937 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
939 JSValue thisValue = exec->thisValue();
940 if (!checkObjectCoercible(thisValue))
941 return throwVMTypeError(exec);
943 JSValue a0 = exec->argument(0);
944 JSValue a1 = exec->argument(1);
946 JSString* thisJSString = thisValue.toString(exec);
947 JSString* otherJSString = a0.toString(exec);
950 if (!a1.isUndefined()) {
951 int len = thisJSString->length();
952 RELEASE_ASSERT(len >= 0);
954 pos = std::min<uint32_t>(a1.asUInt32(), len);
956 double dpos = a1.toInteger(exec);
961 pos = static_cast<unsigned>(dpos);
965 if (thisJSString->length() < otherJSString->length() + pos)
966 return JSValue::encode(jsNumber(-1));
968 size_t result = thisJSString->view(exec).get().find(otherJSString->view(exec).get(), pos);
969 if (result == notFound)
970 return JSValue::encode(jsNumber(-1));
971 return JSValue::encode(jsNumber(result));
974 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
976 JSValue thisValue = exec->thisValue();
977 if (!checkObjectCoercible(thisValue))
978 return throwVMTypeError(exec);
980 JSValue a0 = exec->argument(0);
981 JSValue a1 = exec->argument(1);
983 JSString* thisJSString = thisValue.toString(exec);
984 unsigned len = thisJSString->length();
985 JSString* otherJSString = a0.toString(exec);
987 double dpos = a1.toIntegerPreserveNaN(exec);
988 unsigned startPosition;
991 else if (!(dpos <= len)) // true for NaN
994 startPosition = static_cast<unsigned>(dpos);
996 if (len < otherJSString->length())
997 return JSValue::encode(jsNumber(-1));
999 String thisString = thisJSString->value(exec);
1000 String otherString = otherJSString->value(exec);
1003 result = thisString.startsWith(otherString) ? 0 : notFound;
1005 result = thisString.reverseFind(otherString, startPosition);
1006 if (result == notFound)
1007 return JSValue::encode(jsNumber(-1));
1008 return JSValue::encode(jsNumber(result));
1011 EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
1013 JSValue thisValue = exec->thisValue();
1014 if (!checkObjectCoercible(thisValue))
1015 return throwVMTypeError(exec);
1016 JSString* string = thisValue.toString(exec);
1017 String s = string->value(exec);
1018 VM* vm = &exec->vm();
1020 JSValue a0 = exec->argument(0);
1023 bool global = false;
1024 if (a0.inherits(RegExpObject::info())) {
1025 RegExpObject* regExpObject = asRegExpObject(a0);
1026 regExp = regExpObject->regExp();
1027 if ((global = regExp->global())) {
1028 // ES5.1 15.5.4.10 step 8.a.
1029 regExpObject->setLastIndex(exec, 0);
1030 if (exec->hadException())
1031 return JSValue::encode(jsUndefined());
1035 * ECMA 15.5.4.12 String.prototype.search (regexp)
1036 * If regexp is not an object whose [[Class]] property is "RegExp", it is
1037 * replaced with the result of the expression new RegExp(regexp).
1038 * Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
1040 String patternString = emptyString();
1041 if (!a0.isUndefined()) {
1042 patternString = a0.toString(exec)->value(exec);
1043 if (exec->hadException())
1044 return JSValue::encode(jsUndefined());
1046 regExp = RegExp::create(exec->vm(), patternString, NoFlags);
1047 if (!regExp->isValid())
1048 return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage()));
1050 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
1051 MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, s, 0);
1052 // case without 'g' flag is handled like RegExp.prototype.exec
1054 return JSValue::encode(result ? createRegExpMatchesArray(exec, string, regExp, result) : jsNull());
1056 // return array of matches
1057 MarkedArgumentBuffer list;
1059 // We defend ourselves from crazy.
1060 const size_t maximumReasonableMatchSize = 1000000000;
1061 if (list.size() > maximumReasonableMatchSize) {
1062 throwOutOfMemoryError(exec);
1063 return JSValue::encode(jsUndefined());
1066 size_t end = result.end;
1067 size_t length = end - result.start;
1068 list.append(jsSubstring(exec, s, result.start, length));
1071 result = regExpConstructor->performMatch(*vm, regExp, string, s, end);
1073 if (list.isEmpty()) {
1074 // if there are no matches at all, it's important to return
1075 // Null instead of an empty array, because this matches
1076 // other browsers and because Null is a false value.
1077 return JSValue::encode(jsNull());
1080 return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list));
1083 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
1085 JSValue thisValue = exec->thisValue();
1086 if (!checkObjectCoercible(thisValue))
1087 return throwVMTypeError(exec);
1088 String s = thisValue.toString(exec)->value(exec);
1089 if (exec->hadException())
1090 return JSValue::encode(jsUndefined());
1092 int len = s.length();
1093 RELEASE_ASSERT(len >= 0);
1095 JSValue a0 = exec->argument(0);
1096 JSValue a1 = exec->argument(1);
1098 // The arg processing is very much like ArrayProtoFunc::Slice
1099 double start = a0.toInteger(exec);
1100 double end = a1.isUndefined() ? len : a1.toInteger(exec);
1101 double from = start < 0 ? len + start : start;
1102 double to = end < 0 ? len + end : end;
1103 if (to > from && to > 0 && from < len) {
1108 return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)));
1111 return JSValue::encode(jsEmptyString(exec));
1114 // Return true in case of early return (resultLength got to limitLength).
1115 template<typename CharacterType>
1116 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)
1119 size_t matchPosition;
1120 const CharacterType* characters = string->characters<CharacterType>();
1121 // 13. Repeat, while q != s
1122 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1123 // b. If z is failure, then let q = q+1.
1124 // c. Else, z is not failure
1125 while ((matchPosition = WTF::find(characters, string->length(), separatorCharacter, position)) != notFound) {
1126 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1127 // through q (exclusive).
1128 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1129 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1130 result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
1131 // 3. Increment lengthA by 1.
1132 // 4. If lengthA == lim, return A.
1133 if (++resultLength == limitLength)
1138 position = matchPosition + 1;
1143 // ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
1144 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
1146 // 1. Call CheckObjectCoercible passing the this value as its argument.
1147 JSValue thisValue = exec->thisValue();
1148 if (!checkObjectCoercible(thisValue))
1149 return throwVMTypeError(exec);
1151 // 2. Let S be the result of calling ToString, giving it the this value as its argument.
1152 // 6. Let s be the number of characters in S.
1153 String input = thisValue.toString(exec)->value(exec);
1154 if (exec->hadException())
1155 return JSValue::encode(jsUndefined());
1156 ASSERT(!input.isNull());
1158 // 3. Let A be a new array created as if by the expression new Array()
1159 // where Array is the standard built-in constructor with that name.
1160 JSArray* result = constructEmptyArray(exec, 0);
1162 // 4. Let lengthA be 0.
1163 unsigned resultLength = 0;
1165 // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
1166 JSValue limitValue = exec->argument(1);
1167 unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
1170 size_t position = 0;
1172 // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
1173 // otherwise let R = ToString(separator).
1174 JSValue separatorValue = exec->argument(0);
1175 if (separatorValue.inherits(RegExpObject::info())) {
1176 VM* vm = &exec->vm();
1177 RegExp* reg = asRegExpObject(separatorValue)->regExp();
1179 // 9. If lim == 0, return A.
1181 return JSValue::encode(result);
1183 // 10. If separator is undefined, then
1184 if (separatorValue.isUndefined()) {
1185 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1186 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1187 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1189 return JSValue::encode(result);
1192 // 11. If s == 0, then
1193 if (input.isEmpty()) {
1194 // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
1195 // b. If z is not failure, return A.
1196 // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1197 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1199 if (!reg->match(*vm, input, 0))
1200 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1201 return JSValue::encode(result);
1205 size_t matchPosition = 0;
1206 // 13. Repeat, while q != s
1207 while (matchPosition < input.length()) {
1208 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1209 Vector<int, 32> ovector;
1210 int mpos = reg->match(*vm, input, matchPosition, ovector);
1212 // b. If z is a failure then we can break because there are no matches
1215 matchPosition = mpos;
1217 // if the match is the empty match at the end, break.
1218 if (matchPosition >= input.length())
1221 // c. Else, z is not failure
1222 // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
1223 size_t matchEnd = ovector[1];
1225 // ii. If e == p, then let q = q + 1.
1226 if (matchEnd == position) {
1230 // iii. if matchEnd == 0 then position should also be zero and thus matchEnd should equal position.
1233 // iii. Else, e != p
1235 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1236 // through q (exclusive).
1237 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1238 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1239 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1241 // 3. Increment lengthA by 1.
1242 // 4. If lengthA == lim, return A.
1244 if (resultLength == limit)
1245 return JSValue::encode(result);
1246 if (resultLength >= MAX_STORAGE_VECTOR_INDEX) {
1247 // Let's consider what's best for users here. We're about to increase the length of
1248 // the split array beyond the maximum length that we can support efficiently. This
1249 // will cause us to use a HashMap for the new entries after this point. That's going
1250 // to result in a very long running time of this function and very large memory
1251 // usage. In my experiments, JSC will sit spinning for minutes after getting here and
1252 // it was using >4GB of memory and eventually grew to 8GB. It kept running without
1253 // finishing until I killed it. That's probably not what the user wanted. The user,
1254 // or the program that the user is running, probably made a mistake by calling this
1255 // method in such a way that it resulted in such an obnoxious array. Therefore, to
1256 // protect ourselves, we bail at this point.
1257 throwOutOfMemoryError(exec);
1258 return JSValue::encode(jsUndefined());
1263 position = matchEnd;
1264 matchPosition = matchEnd;
1267 // 7. Repeat, while i is not equal to the number of elements in cap.
1269 for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
1270 // b Call the [[DefineOwnProperty]] internal method of A with arguments
1271 // ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
1272 // true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1273 int sub = ovector[i * 2];
1274 result->putDirectIndex(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, thisValue, input, sub, ovector[i * 2 + 1] - sub));
1275 // c Increment lengthA by 1.
1276 // d If lengthA == lim, return A.
1277 if (++resultLength == limit)
1278 return JSValue::encode(result);
1282 String separator = separatorValue.toString(exec)->value(exec);
1283 if (exec->hadException())
1284 return JSValue::encode(jsUndefined());
1286 // 9. If lim == 0, return A.
1288 return JSValue::encode(result);
1290 // 10. If separator is undefined, then
1291 JSValue separatorValue = exec->argument(0);
1292 if (separatorValue.isUndefined()) {
1293 // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1294 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1295 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1297 return JSValue::encode(result);
1300 // 11. If s == 0, then
1301 if (input.isEmpty()) {
1302 // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
1303 // b. If z is not failure, return A.
1304 // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
1305 // Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1307 if (!separator.isEmpty())
1308 result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
1309 return JSValue::encode(result);
1312 // Optimized case for splitting on the empty string.
1313 if (separator.isEmpty()) {
1314 limit = std::min(limit, input.length());
1315 // Zero limt/input length handled in steps 9/11 respectively, above.
1319 result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
1320 } while (++position < limit);
1322 return JSValue::encode(result);
1326 // -separator length == 1, 8 bits
1327 // -separator length == 1, 16 bits
1328 // -separator length > 1
1329 StringImpl* stringImpl = input.impl();
1330 StringImpl* separatorImpl = separator.impl();
1331 size_t separatorLength = separatorImpl->length();
1333 if (separatorLength == 1) {
1334 UChar separatorCharacter;
1335 if (separatorImpl->is8Bit())
1336 separatorCharacter = separatorImpl->characters8()[0];
1338 separatorCharacter = separatorImpl->characters16()[0];
1340 if (stringImpl->is8Bit()) {
1341 if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1342 return JSValue::encode(result);
1344 if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
1345 return JSValue::encode(result);
1349 size_t matchPosition;
1350 // 13. Repeat, while q != s
1351 // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
1352 // b. If z is failure, then let q = q+1.
1353 // c. Else, z is not failure
1354 while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
1355 // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1356 // through q (exclusive).
1357 // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
1358 // Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1359 result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
1360 // 3. Increment lengthA by 1.
1361 // 4. If lengthA == lim, return A.
1362 if (++resultLength == limit)
1363 return JSValue::encode(result);
1367 position = matchPosition + separator.length();
1372 // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
1373 // through s (exclusive).
1374 // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
1375 // {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
1376 result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
1379 return JSValue::encode(result);
1382 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
1384 JSValue thisValue = exec->thisValue();
1385 if (!checkObjectCoercible(thisValue))
1386 return throwVMTypeError(exec);
1388 JSString* jsString = 0;
1390 if (thisValue.isString()) {
1391 jsString = jsCast<JSString*>(thisValue.asCell());
1392 len = jsString->length();
1394 uString = thisValue.toString(exec)->value(exec);
1395 if (exec->hadException())
1396 return JSValue::encode(jsUndefined());
1397 len = uString.length();
1400 JSValue a0 = exec->argument(0);
1401 JSValue a1 = exec->argument(1);
1403 double start = a0.toInteger(exec);
1404 double length = a1.isUndefined() ? len : a1.toInteger(exec);
1405 if (start >= len || length <= 0)
1406 return JSValue::encode(jsEmptyString(exec));
1412 if (start + length > len)
1413 length = len - start;
1414 unsigned substringStart = static_cast<unsigned>(start);
1415 unsigned substringLength = static_cast<unsigned>(length);
1417 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1418 return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
1421 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
1423 SamplingRegion samplingRegion("Doing substringing");
1424 JSValue thisValue = exec->thisValue();
1425 if (!checkObjectCoercible(thisValue))
1426 return throwVMTypeError(exec);
1428 JSString* jsString = thisValue.toString(exec);
1429 if (exec->hadException())
1430 return JSValue::encode(jsUndefined());
1432 JSValue a0 = exec->argument(0);
1433 JSValue a1 = exec->argument(1);
1434 int len = jsString->length();
1435 RELEASE_ASSERT(len >= 0);
1437 double start = a0.toNumber(exec);
1439 if (!(start >= 0)) // check for negative values or NaN
1441 else if (start > len)
1443 if (a1.isUndefined())
1446 end = a1.toNumber(exec);
1447 if (!(end >= 0)) // check for negative values or NaN
1457 unsigned substringStart = static_cast<unsigned>(start);
1458 unsigned substringLength = static_cast<unsigned>(end) - substringStart;
1459 return JSValue::encode(jsSubstring(exec, jsString, substringStart, substringLength));
1462 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
1464 JSValue thisValue = exec->thisValue();
1465 if (!checkObjectCoercible(thisValue))
1466 return throwVMTypeError(exec);
1467 JSString* sVal = thisValue.toString(exec);
1468 const String& s = sVal->value(exec);
1469 String lowercasedString = s.convertToLowercaseWithoutLocale();
1470 if (lowercasedString.impl() == s.impl())
1471 return JSValue::encode(sVal);
1472 return JSValue::encode(jsString(exec, lowercasedString));
1475 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
1477 JSValue thisValue = exec->thisValue();
1478 if (!checkObjectCoercible(thisValue))
1479 return throwVMTypeError(exec);
1480 JSString* sVal = thisValue.toString(exec);
1481 const String& s = sVal->value(exec);
1482 String uppercasedString = s.convertToUppercaseWithoutLocale();
1483 if (uppercasedString.impl() == s.impl())
1484 return JSValue::encode(sVal);
1485 return JSValue::encode(jsString(exec, uppercasedString));
1488 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
1490 JSValue thisValue = exec->thisValue();
1491 if (!checkObjectCoercible(thisValue))
1492 return throwVMTypeError(exec);
1493 String s = thisValue.toString(exec)->value(exec);
1494 if (exec->hadException())
1495 return JSValue::encode(jsUndefined());
1497 JSValue a0 = exec->argument(0);
1498 String str = a0.toString(exec)->value(exec);
1499 if (exec->hadException())
1500 return JSValue::encode(jsUndefined());
1501 return JSValue::encode(jsNumber(Collator().collate(s, str)));
1505 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
1507 // 1. Let O be RequireObjectCoercible(this value).
1508 JSValue thisValue = state->thisValue();
1509 if (!checkObjectCoercible(thisValue))
1510 return throwVMTypeError(state);
1512 // 2. Let S be ToString(O).
1513 JSString* sVal = thisValue.toString(state);
1514 const String& s = sVal->value(state);
1516 // 3. ReturnIfAbrupt(S).
1517 if (state->hadException())
1518 return JSValue::encode(jsUndefined());
1520 // Optimization for empty strings.
1522 return JSValue::encode(sVal);
1524 // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
1525 Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
1527 // 5. ReturnIfAbrupt(requestedLocales).
1528 if (state->hadException())
1529 return JSValue::encode(jsUndefined());
1531 // 6. Let len be the number of elements in requestedLocales.
1532 size_t len = requestedLocales.size();
1534 // 7. If len > 0, then
1535 // a. Let requestedLocale be the first element of requestedLocales.
1537 // a. Let requestedLocale be DefaultLocale().
1538 String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
1540 // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
1541 String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
1543 // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
1544 // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
1545 const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
1547 // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1548 String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
1550 // 12. If locale is undefined, let locale be "und".
1551 if (locale.isNull())
1552 locale = ASCIILiteral("und");
1554 CString utf8LocaleBuffer = locale.utf8();
1555 const StringView view(s);
1556 const int32_t viewLength = view.length();
1558 // Delegate the following steps to icu u_strToLower or u_strToUpper.
1559 // 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.
1560 // 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).
1561 // 15. Let cuList be a new List.
1562 // 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.
1563 // 17. Let L be a String whose elements are, in order, the elements of cuList.
1565 // Most strings lower/upper case will be the same size as original, so try that first.
1566 UErrorCode error(U_ZERO_ERROR);
1567 Vector<UChar> buffer(viewLength);
1569 const int32_t resultLength = convertCase(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1570 if (U_SUCCESS(error))
1571 lower = String(buffer.data(), resultLength);
1572 else if (error == U_BUFFER_OVERFLOW_ERROR) {
1573 // Converted case needs more space than original. Try again.
1574 UErrorCode error(U_ZERO_ERROR);
1575 Vector<UChar> buffer(resultLength);
1576 convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
1577 if (U_FAILURE(error))
1578 return throwVMTypeError(state, u_errorName(error));
1579 lower = String(buffer.data(), resultLength);
1581 return throwVMTypeError(state, u_errorName(error));
1584 return JSValue::encode(jsString(state, lower));
1587 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
1589 // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
1590 // http://ecma-international.org/publications/standards/Ecma-402.htm
1591 return toLocaleCase(state, u_strToLower);
1594 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
1596 // 13.1.3 String.prototype.toLocaleUpperCase ([locales])
1597 // http://ecma-international.org/publications/standards/Ecma-402.htm
1598 // 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.
1599 return toLocaleCase(state, u_strToUpper);
1601 #endif // ENABLE(INTL)
1603 EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
1605 JSValue thisValue = exec->thisValue();
1606 if (!checkObjectCoercible(thisValue))
1607 return throwVMTypeError(exec);
1608 String s = thisValue.toString(exec)->value(exec);
1609 if (exec->hadException())
1610 return JSValue::encode(jsUndefined());
1611 return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
1614 EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
1616 JSValue thisValue = exec->thisValue();
1617 if (!checkObjectCoercible(thisValue))
1618 return throwVMTypeError(exec);
1619 String s = thisValue.toString(exec)->value(exec);
1620 if (exec->hadException())
1621 return JSValue::encode(jsUndefined());
1622 return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
1625 EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
1627 JSValue thisValue = exec->thisValue();
1628 if (!checkObjectCoercible(thisValue))
1629 return throwVMTypeError(exec);
1630 String s = thisValue.toString(exec)->value(exec);
1631 if (exec->hadException())
1632 return JSValue::encode(jsUndefined());
1633 return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
1636 EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
1638 JSValue thisValue = exec->thisValue();
1639 if (!checkObjectCoercible(thisValue))
1640 return throwVMTypeError(exec);
1641 String s = thisValue.toString(exec)->value(exec);
1642 if (exec->hadException())
1643 return JSValue::encode(jsUndefined());
1644 return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
1647 EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
1649 JSValue thisValue = exec->thisValue();
1650 if (!checkObjectCoercible(thisValue))
1651 return throwVMTypeError(exec);
1652 String s = thisValue.toString(exec)->value(exec);
1653 if (exec->hadException())
1654 return JSValue::encode(jsUndefined());
1655 return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
1658 EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
1660 JSValue thisValue = exec->thisValue();
1661 if (!checkObjectCoercible(thisValue))
1662 return throwVMTypeError(exec);
1663 String s = thisValue.toString(exec)->value(exec);
1664 if (exec->hadException())
1665 return JSValue::encode(jsUndefined());
1666 return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
1669 EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
1671 JSValue thisValue = exec->thisValue();
1672 if (!checkObjectCoercible(thisValue))
1673 return throwVMTypeError(exec);
1674 String s = thisValue.toString(exec)->value(exec);
1675 if (exec->hadException())
1676 return JSValue::encode(jsUndefined());
1677 return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
1680 EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
1682 JSValue thisValue = exec->thisValue();
1683 if (!checkObjectCoercible(thisValue))
1684 return throwVMTypeError(exec);
1685 String s = thisValue.toString(exec)->value(exec);
1686 if (exec->hadException())
1687 return JSValue::encode(jsUndefined());
1688 return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
1691 EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(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());
1699 return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
1702 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
1704 JSValue thisValue = exec->thisValue();
1705 if (!checkObjectCoercible(thisValue))
1706 return throwVMTypeError(exec);
1707 String s = thisValue.toString(exec)->value(exec);
1708 if (exec->hadException())
1709 return JSValue::encode(jsUndefined());
1711 JSValue a0 = exec->argument(0);
1712 String color = a0.toWTFString(exec);
1713 color.replaceWithLiteral('"', """);
1715 return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
1718 EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
1720 JSValue thisValue = exec->thisValue();
1721 if (!checkObjectCoercible(thisValue))
1722 return throwVMTypeError(exec);
1723 String s = thisValue.toString(exec)->value(exec);
1724 if (exec->hadException())
1725 return JSValue::encode(jsUndefined());
1727 JSValue a0 = exec->argument(0);
1729 uint32_t smallInteger;
1730 if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
1731 unsigned stringSize = s.length();
1732 unsigned bufferSize = 22 + stringSize;
1733 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1735 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1737 return JSValue::encode(jsUndefined());
1750 buffer[12] = '0' + smallInteger;
1753 StringView(s).getCharactersWithUpconvert(&buffer[15]);
1754 buffer[15 + stringSize] = '<';
1755 buffer[16 + stringSize] = '/';
1756 buffer[17 + stringSize] = 'f';
1757 buffer[18 + stringSize] = 'o';
1758 buffer[19 + stringSize] = 'n';
1759 buffer[20 + stringSize] = 't';
1760 buffer[21 + stringSize] = '>';
1761 return JSValue::encode(jsNontrivialString(exec, impl));
1764 String fontSize = a0.toWTFString(exec);
1765 fontSize.replaceWithLiteral('"', """);
1767 return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
1770 EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
1772 JSValue thisValue = exec->thisValue();
1773 if (!checkObjectCoercible(thisValue))
1774 return throwVMTypeError(exec);
1775 String s = thisValue.toString(exec)->value(exec);
1776 if (exec->hadException())
1777 return JSValue::encode(jsUndefined());
1779 JSValue a0 = exec->argument(0);
1780 String anchor = a0.toWTFString(exec);
1781 anchor.replaceWithLiteral('"', """);
1783 return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
1786 EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
1788 JSValue thisValue = exec->thisValue();
1789 if (!checkObjectCoercible(thisValue))
1790 return throwVMTypeError(exec);
1791 String s = thisValue.toString(exec)->value(exec);
1792 if (exec->hadException())
1793 return JSValue::encode(jsUndefined());
1795 JSValue a0 = exec->argument(0);
1796 String linkText = a0.toWTFString(exec);
1797 linkText.replaceWithLiteral('"', """);
1799 unsigned linkTextSize = linkText.length();
1800 unsigned stringSize = s.length();
1801 unsigned bufferSize = 15 + linkTextSize + stringSize;
1802 // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
1804 PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
1806 return JSValue::encode(jsUndefined());
1816 StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
1817 buffer[9 + linkTextSize] = '"';
1818 buffer[10 + linkTextSize] = '>';
1819 StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
1820 buffer[11 + linkTextSize + stringSize] = '<';
1821 buffer[12 + linkTextSize + stringSize] = '/';
1822 buffer[13 + linkTextSize + stringSize] = 'a';
1823 buffer[14 + linkTextSize + stringSize] = '>';
1824 return JSValue::encode(jsNontrivialString(exec, impl));
1832 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
1834 if (!checkObjectCoercible(thisValue))
1835 return throwTypeError(exec);
1836 String str = thisValue.toString(exec)->value(exec);
1837 if (exec->hadException())
1838 return jsUndefined();
1841 if (trimKind & TrimLeft) {
1842 while (left < str.length() && isStrWhiteSpace(str[left]))
1845 unsigned right = str.length();
1846 if (trimKind & TrimRight) {
1847 while (right > left && isStrWhiteSpace(str[right - 1]))
1851 // Don't gc allocate a new string if we don't have to.
1852 if (left == 0 && right == str.length() && thisValue.isString())
1855 return jsString(exec, str.substringSharingImpl(left, right - left));
1858 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec)
1860 JSValue thisValue = exec->thisValue();
1861 return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight));
1864 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec)
1866 JSValue thisValue = exec->thisValue();
1867 return JSValue::encode(trimString(exec, thisValue, TrimLeft));
1870 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec)
1872 JSValue thisValue = exec->thisValue();
1873 return JSValue::encode(trimString(exec, thisValue, TrimRight));
1876 static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, unsigned max)
1882 return static_cast<unsigned>(value);
1885 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
1887 JSValue thisValue = exec->thisValue();
1888 if (!checkObjectCoercible(thisValue))
1889 return throwVMTypeError(exec);
1891 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1892 if (exec->hadException())
1893 return JSValue::encode(jsUndefined());
1895 JSValue a0 = exec->argument(0);
1896 if (jsDynamicCast<RegExpObject*>(a0))
1897 return throwVMTypeError(exec);
1899 String searchString = a0.toString(exec)->value(exec);
1900 if (exec->hadException())
1901 return JSValue::encode(jsUndefined());
1903 JSValue positionArg = exec->argument(1);
1905 if (positionArg.isInt32())
1906 start = std::max(0, positionArg.asInt32());
1908 unsigned length = stringToSearchIn.length();
1909 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1910 if (exec->hadException())
1911 return JSValue::encode(jsUndefined());
1914 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
1917 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
1919 JSValue thisValue = exec->thisValue();
1920 if (!checkObjectCoercible(thisValue))
1921 return throwVMTypeError(exec);
1923 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1924 if (exec->hadException())
1925 return JSValue::encode(jsUndefined());
1927 JSValue a0 = exec->argument(0);
1928 if (jsDynamicCast<RegExpObject*>(a0))
1929 return throwVMTypeError(exec);
1931 String searchString = a0.toString(exec)->value(exec);
1932 if (exec->hadException())
1933 return JSValue::encode(jsUndefined());
1935 unsigned length = stringToSearchIn.length();
1937 JSValue endPositionArg = exec->argument(1);
1938 unsigned end = length;
1939 if (endPositionArg.isInt32())
1940 end = std::max(0, endPositionArg.asInt32());
1941 else if (!endPositionArg.isUndefined()) {
1942 end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
1943 if (exec->hadException())
1944 return JSValue::encode(jsUndefined());
1947 return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
1950 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
1952 JSValue thisValue = exec->thisValue();
1953 if (!checkObjectCoercible(thisValue))
1954 return throwVMTypeError(exec);
1956 String stringToSearchIn = thisValue.toString(exec)->value(exec);
1957 if (exec->hadException())
1958 return JSValue::encode(jsUndefined());
1960 JSValue a0 = exec->argument(0);
1961 if (jsDynamicCast<RegExpObject*>(a0))
1962 return throwVMTypeError(exec);
1964 String searchString = a0.toString(exec)->value(exec);
1965 if (exec->hadException())
1966 return JSValue::encode(jsUndefined());
1968 JSValue positionArg = exec->argument(1);
1970 if (positionArg.isInt32())
1971 start = std::max(0, positionArg.asInt32());
1973 unsigned length = stringToSearchIn.length();
1974 start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
1975 if (exec->hadException())
1976 return JSValue::encode(jsUndefined());
1979 return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
1982 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
1984 JSValue thisValue = exec->thisValue();
1985 if (!checkObjectCoercible(thisValue))
1986 return throwVMTypeError(exec);
1987 JSString* string = thisValue.toString(exec);
1988 return JSValue::encode(JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), string));
1991 static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
1993 UErrorCode status = U_ZERO_ERROR;
1994 int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
1996 if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
1997 // The behavior is not specified when normalize fails.
1998 // Now we throw a type erorr since it seems that the contents of the string are invalid.
1999 return throwTypeError(exec);
2002 UChar* buffer = nullptr;
2003 RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
2005 return throwOutOfMemoryError(exec);
2007 status = U_ZERO_ERROR;
2008 unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
2009 if (U_FAILURE(status))
2010 return throwTypeError(exec);
2012 return jsString(exec, impl.release());
2015 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
2017 JSValue thisValue = exec->thisValue();
2018 if (!checkObjectCoercible(thisValue))
2019 return throwVMTypeError(exec);
2020 JSString::SafeView source = thisValue.toString(exec)->view(exec);
2021 if (exec->hadException())
2022 return JSValue::encode(jsUndefined());
2024 UNormalizationMode form = UNORM_NFC;
2025 // Verify that the argument is provided and is not undefined.
2026 if (!exec->argument(0).isUndefined()) {
2027 String formString = exec->uncheckedArgument(0).toString(exec)->value(exec);
2028 if (exec->hadException())
2029 return JSValue::encode(jsUndefined());
2031 if (formString == "NFC")
2033 else if (formString == "NFD")
2035 else if (formString == "NFKC")
2037 else if (formString == "NFKD")
2040 return throwVMError(exec, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
2043 return JSValue::encode(normalize(exec, source.get().upconvertedCharacters(), source.length(), form));