Diff
Modified: trunk/JSTests/ChangeLog (252682 => 252683)
--- trunk/JSTests/ChangeLog 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/JSTests/ChangeLog 2019-11-20 05:12:14 UTC (rev 252683)
@@ -1,3 +1,12 @@
+2019-11-19 Ross Kirsling <ross.kirsl...@sony.com>
+
+ Implement String.prototype.replaceAll
+ https://bugs.webkit.org/show_bug.cgi?id=202471
+
+ Reviewed by Yusuke Suzuki.
+
+ * stress/string-replaceall.js: Added.
+
2019-11-19 Robin Morisset <rmoris...@apple.com>
[ESNext][BigInt] Add support for op_inc
Added: trunk/JSTests/stress/string-replaceall.js (0 => 252683)
--- trunk/JSTests/stress/string-replaceall.js (rev 0)
+++ trunk/JSTests/stress/string-replaceall.js 2019-11-20 05:12:14 UTC (rev 252683)
@@ -0,0 +1,36 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${expected} but got ${actual}`);
+}
+
+function shouldThrowTypeError(func) {
+ let error;
+ try {
+ func();
+ } catch (e) {
+ error = e;
+ }
+
+ if (!(error instanceof TypeError))
+ throw new Error('Expected TypeError!');
+}
+
+shouldThrowTypeError(() => { String.prototype.replaceAll.call(undefined, 'def', 'xyz'); });
+shouldThrowTypeError(() => { String.prototype.replaceAll.call(null, 'def', 'xyz'); });
+
+shouldThrowTypeError(() => { 'abcdefabcdefabc'.replaceAll(/def/, 'xyz'); });
+shouldThrowTypeError(() => { 'abcdefabcdefabc'.replaceAll(new RegExp('def'), 'xyz'); });
+shouldThrowTypeError(() => { 'abcdefabcdefabc'.replaceAll({ [Symbol.match]() {}, toString: () => 'def' }, 'xyz'); });
+
+shouldBe('abcdefabcdefabc'.replaceAll('def', 'xyz'), 'abcxyzabcxyzabc');
+shouldBe('abcdefabcdefabc'.replaceAll(/def/g, 'xyz'), 'abcxyzabcxyzabc');
+shouldBe('abcdefabcdefabc'.replaceAll(new RegExp('def', 'g'), 'xyz'), 'abcxyzabcxyzabc');
+shouldBe('abcdefabcdefabc'.replaceAll({ [Symbol.match]() {}, toString: () => 'def', flags: 'g' }, 'xyz'), 'abcxyzabcxyzabc');
+
+const search = /def/g;
+search[Symbol.replace] = undefined;
+shouldBe('abcdefabcdefabc'.replaceAll(search, 'xyz'), 'abcdefabcdefabc');
+search[Symbol.replace] = () => 'q';
+shouldBe('abcdefabcdefabc'.replaceAll(search, 'xyz'), 'q');
+search[Symbol.replace] = RegExp.prototype[Symbol.replace].bind(search);
+shouldBe('abcdefabcdefabc'.replaceAll(search, 'xyz'), 'abcxyzabcxyzabc');
Modified: trunk/LayoutTests/ChangeLog (252682 => 252683)
--- trunk/LayoutTests/ChangeLog 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/LayoutTests/ChangeLog 2019-11-20 05:12:14 UTC (rev 252683)
@@ -1,3 +1,14 @@
+2019-11-19 Ross Kirsling <ross.kirsl...@sony.com>
+
+ Implement String.prototype.replaceAll
+ https://bugs.webkit.org/show_bug.cgi?id=202471
+
+ Reviewed by Yusuke Suzuki.
+
+ * js/Object-getOwnPropertyNames-expected.txt:
+ * js/script-tests/Object-getOwnPropertyNames.js:
+ Grrr, why is this a layout test...
+
2019-11-19 Youenn Fablet <you...@apple.com>
getUserMedia echoCancellation constraint has no affect
Modified: trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt (252682 => 252683)
--- trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt 2019-11-20 05:12:14 UTC (rev 252683)
@@ -49,7 +49,7 @@
PASS getSortedOwnPropertyNames(Array) is ['from', 'isArray', 'length', 'name', 'of', 'prototype']
PASS getSortedOwnPropertyNames(Array.prototype) is ['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']
PASS getSortedOwnPropertyNames(String) is ['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw']
-PASS getSortedOwnPropertyNames(String.prototype) is ['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']
+PASS getSortedOwnPropertyNames(String.prototype) is ['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']
PASS getSortedOwnPropertyNames(Boolean) is ['length', 'name', 'prototype']
PASS getSortedOwnPropertyNames(Boolean.prototype) is ['constructor', 'toString', 'valueOf']
PASS getSortedOwnPropertyNames(Number) is ['EPSILON', 'MAX_SAFE_INTEGER', 'MAX_VALUE', 'MIN_SAFE_INTEGER', 'MIN_VALUE', 'NEGATIVE_INFINITY', 'NaN', 'POSITIVE_INFINITY', 'isFinite', 'isInteger', 'isNaN', 'isSafeInteger', 'length', 'name', 'parseFloat', 'parseInt', 'prototype']
Modified: trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js (252682 => 252683)
--- trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js 2019-11-20 05:12:14 UTC (rev 252683)
@@ -58,7 +58,7 @@
"Array": "['from', 'isArray', 'length', 'name', 'of', 'prototype']",
"Array.prototype": "['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']",
"String": "['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw']",
- "String.prototype": "['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']",
+ "String.prototype": "['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'matchAll', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'replaceAll', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']",
"Boolean": "['length', 'name', 'prototype']",
"Boolean.prototype": "['constructor', 'toString', 'valueOf']",
"Number": "['EPSILON', 'MAX_SAFE_INTEGER', 'MAX_VALUE', 'MIN_SAFE_INTEGER', 'MIN_VALUE', 'NEGATIVE_INFINITY', 'NaN', 'POSITIVE_INFINITY', 'isFinite', 'isInteger', 'isNaN', 'isSafeInteger', 'length', 'name', 'parseFloat', 'parseInt', 'prototype']",
Modified: trunk/Source/_javascript_Core/ChangeLog (252682 => 252683)
--- trunk/Source/_javascript_Core/ChangeLog 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-11-20 05:12:14 UTC (rev 252683)
@@ -1,3 +1,28 @@
+2019-11-19 Ross Kirsling <ross.kirsl...@sony.com>
+
+ Implement String.prototype.replaceAll
+ https://bugs.webkit.org/show_bug.cgi?id=202471
+
+ Reviewed by Yusuke Suzuki.
+
+ Implement the stage 3 proposal here:
+ https://github.com/tc39/proposal-string-replaceall
+
+ String.prototype.replaceAll is the same as String.prototype.replace, except:
+ 1. When the first argument is a string, all instances of the search string are replaced.
+ 2. When the first argument is a non-global regular _expression_, a TypeError is thrown.
+
+ * builtins/BuiltinNames.h:
+ * builtins/StringPrototype.js:
+ (replaceAll): Added.
+ * runtime/StringPrototype.cpp:
+ (JSC::StringPrototype::finishCreation):
+ (JSC::jsSpliceSubstringsWithSeparators): Add early out for single-replacement case.
+ (JSC::replaceUsingStringSearch): Add global replacement logic, following replaceUsingRegExpSearch.
+ (JSC::replace):
+ (JSC::stringProtoFuncReplaceUsingStringSearch):
+ (JSC::stringProtoFuncReplaceAllUsingStringSearch): Added.
+
2019-11-19 Robin Morisset <rmoris...@apple.com>
[ESNext][BigInt] Add support for op_inc
Modified: trunk/Source/_javascript_Core/builtins/BuiltinNames.h (252682 => 252683)
--- trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2019-11-20 05:12:14 UTC (rev 252683)
@@ -132,6 +132,7 @@
macro(isRegExp) \
macro(replaceUsingRegExp) \
macro(replaceUsingStringSearch) \
+ macro(replaceAllUsingStringSearch) \
macro(makeTypeError) \
macro(mapBucket) \
macro(mapBucketHead) \
Modified: trunk/Source/_javascript_Core/builtins/StringPrototype.js (252682 => 252683)
--- trunk/Source/_javascript_Core/builtins/StringPrototype.js 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/_javascript_Core/builtins/StringPrototype.js 2019-11-20 05:12:14 UTC (rev 252683)
@@ -259,6 +259,30 @@
return thisString.@replaceUsingStringSearch(searchString, replace);
}
+function replaceAll(search, replace)
+{
+ "use strict";
+
+ if (@isUndefinedOrNull(this))
+ @throwTypeError("String.prototype.replaceAll requires |this| not to be null nor undefined");
+
+ if (search != null) {
+ if (@isRegExp(search) && !@stringIncludesInternal.@call(@toString(search.flags), "g"))
+ @throwTypeError("String.prototype.replaceAll argument must not be a non-global regular _expression_");
+
+ var replacer = search.@replaceSymbol;
+ if (replacer !== @undefined) {
+ if (!@hasObservableSideEffectsForStringReplace(search, replacer))
+ return @toString(this).@replaceUsingRegExp(search, replace);
+ return replacer.@call(search, this, replace);
+ }
+ }
+
+ var thisString = @toString(this);
+ var searchString = @toString(search);
+ return thisString.@replaceAllUsingStringSearch(searchString, replace);
+}
+
function search(regexp)
{
"use strict";
Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (252682 => 252683)
--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp 2019-11-20 05:12:14 UTC (rev 252683)
@@ -70,6 +70,7 @@
EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(JSGlobalObject*, CallFrame*);
EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(JSGlobalObject*, CallFrame*);
EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(JSGlobalObject*, CallFrame*);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceAllUsingStringSearch(JSGlobalObject*, CallFrame*);
EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(JSGlobalObject*, CallFrame*);
EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(JSGlobalObject*, CallFrame*);
EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(JSGlobalObject*, CallFrame*);
@@ -97,28 +98,29 @@
/* Source for StringConstructor.lut.h
@begin stringPrototypeTable
- concat JSBuiltin DontEnum|Function 1
- match JSBuiltin DontEnum|Function 1
- matchAll JSBuiltin DontEnum|Function 1
- padStart JSBuiltin DontEnum|Function 1
- padEnd JSBuiltin DontEnum|Function 1
- repeat JSBuiltin DontEnum|Function 1
- replace JSBuiltin DontEnum|Function 2
- search JSBuiltin DontEnum|Function 1
- split JSBuiltin DontEnum|Function 1
- anchor JSBuiltin DontEnum|Function 1
- big JSBuiltin DontEnum|Function 0
- bold JSBuiltin DontEnum|Function 0
- blink JSBuiltin DontEnum|Function 0
- fixed JSBuiltin DontEnum|Function 0
- fontcolor JSBuiltin DontEnum|Function 1
- fontsize JSBuiltin DontEnum|Function 1
- italics JSBuiltin DontEnum|Function 0
- link JSBuiltin DontEnum|Function 1
- small JSBuiltin DontEnum|Function 0
- strike JSBuiltin DontEnum|Function 0
- sub JSBuiltin DontEnum|Function 0
- sup JSBuiltin DontEnum|Function 0
+ concat JSBuiltin DontEnum|Function 1
+ match JSBuiltin DontEnum|Function 1
+ matchAll JSBuiltin DontEnum|Function 1
+ padStart JSBuiltin DontEnum|Function 1
+ padEnd JSBuiltin DontEnum|Function 1
+ repeat JSBuiltin DontEnum|Function 1
+ replace JSBuiltin DontEnum|Function 2
+ replaceAll JSBuiltin DontEnum|Function 2
+ search JSBuiltin DontEnum|Function 1
+ split JSBuiltin DontEnum|Function 1
+ anchor JSBuiltin DontEnum|Function 1
+ big JSBuiltin DontEnum|Function 0
+ bold JSBuiltin DontEnum|Function 0
+ blink JSBuiltin DontEnum|Function 0
+ fixed JSBuiltin DontEnum|Function 0
+ fontcolor JSBuiltin DontEnum|Function 1
+ fontsize JSBuiltin DontEnum|Function 1
+ italics JSBuiltin DontEnum|Function 0
+ link JSBuiltin DontEnum|Function 1
+ small JSBuiltin DontEnum|Function 0
+ strike JSBuiltin DontEnum|Function 0
+ sub JSBuiltin DontEnum|Function 0
+ sup JSBuiltin DontEnum|Function 0
@end
*/
@@ -142,6 +144,7 @@
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeReplaceRegExpIntrinsic);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingStringSearchPrivateName(), stringProtoFuncReplaceUsingStringSearch, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
+ JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceAllUsingStringSearchPrivateName(), stringProtoFuncReplaceAllUsingStringSearch, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, StringPrototypeSliceIntrinsic);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
@@ -394,6 +397,12 @@
RELEASE_AND_RETURN(scope, jsString(vm, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length))));
}
+ if (rangeCount == 2 && separatorCount == 1) {
+ String leftPart(StringImpl::createSubstringSharingImpl(*source.impl(), substringRanges[0].position, substringRanges[0].length));
+ String rightPart(StringImpl::createSubstringSharingImpl(*source.impl(), substringRanges[1].position, substringRanges[1].length));
+ RELEASE_AND_RETURN(scope, jsString(globalObject, leftPart, separators[0], rightPart));
+ }
+
Checked<int, RecordOverflow> totalLength = 0;
bool allSeparators8Bit = true;
for (int i = 0; i < rangeCount; i++)
@@ -767,7 +776,7 @@
vm, globalObject, callFrame, string, searchValue, callData, callType, replacementString, replaceValue));
}
-static ALWAYS_INLINE JSString* replaceUsingStringSearch(VM& vm, JSGlobalObject* globalObject, JSString* jsString, JSValue searchValue, JSValue replaceValue)
+static ALWAYS_INLINE JSString* replaceUsingStringSearch(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSString* jsString, JSValue searchValue, JSValue replaceValue, bool isGlobal)
{
auto scope = DECLARE_THROW_SCOPE(vm);
@@ -777,46 +786,66 @@
RETURN_IF_EXCEPTION(scope, nullptr);
size_t matchStart = string.find(searchString);
-
if (matchStart == notFound)
return jsString;
CallData callData;
CallType callType = getCallData(vm, replaceValue, callData);
- if (callType != CallType::None) {
- MarkedArgumentBuffer args;
- auto* substring = jsSubstring(vm, string, matchStart, searchString.impl()->length());
+ Optional<CachedCall> cachedCall;
+ String replaceString;
+ if (callType == CallType::None) {
+ replaceString = replaceValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
- args.append(substring);
- args.append(jsNumber(matchStart));
- args.append(jsString);
- ASSERT(!args.hasOverflowed());
- replaceValue = call(globalObject, replaceValue, callType, callData, jsUndefined(), args);
- RETURN_IF_EXCEPTION(scope, nullptr);
+ } else {
+ cachedCall.emplace(globalObject, callFrame, jsCast<JSFunction*>(replaceValue), 3);
+ cachedCall->setThis(jsUndefined());
}
- String replaceString = replaceValue.toWTFString(globalObject);
- RETURN_IF_EXCEPTION(scope, nullptr);
+ size_t endOfLastMatch = 0;
+ size_t searchStringLength = searchString.length();
+ Vector<StringRange, 16> sourceRanges;
+ Vector<String, 16> replacements;
+ do {
+ if (cachedCall) {
+ auto* substring = jsSubstring(vm, string, matchStart, searchStringLength);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ cachedCall->clearArguments();
+ cachedCall->appendArgument(substring);
+ cachedCall->appendArgument(jsNumber(matchStart));
+ cachedCall->appendArgument(jsString);
+ if (UNLIKELY(cachedCall->hasOverflowedArguments()))
+ OUT_OF_MEMORY(globalObject, scope);
+ JSValue replacement = cachedCall->call();
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ replaceString = replacement.toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ }
- StringImpl* stringImpl = string.impl();
- String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart));
-
- size_t matchEnd = matchStart + searchString.impl()->length();
- int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
- String middlePart;
- if (callType != CallType::None)
- middlePart = replaceString;
- else {
- StringBuilder replacement(StringBuilder::OverflowHandler::RecordOverflow);
- substituteBackreferences(replacement, replaceString, string, ovector, 0);
- if (UNLIKELY(replacement.hasOverflowed()))
+ if (UNLIKELY(!sourceRanges.tryConstructAndAppend(endOfLastMatch, matchStart - endOfLastMatch)))
OUT_OF_MEMORY(globalObject, scope);
- middlePart = replacement.toString();
- }
- size_t leftLength = stringImpl->length() - matchEnd;
- String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength));
- RELEASE_AND_RETURN(scope, JSC::jsString(globalObject, leftPart, middlePart, rightPart));
+ size_t matchEnd = matchStart + searchStringLength;
+ int ovector[2] = { static_cast<int>(matchStart), static_cast<int>(matchEnd)};
+ if (cachedCall) {
+ replacements.append(replaceString);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ } else {
+ StringBuilder replacement(StringBuilder::OverflowHandler::RecordOverflow);
+ substituteBackreferences(replacement, replaceString, string, ovector, nullptr);
+ if (UNLIKELY(replacement.hasOverflowed()))
+ OUT_OF_MEMORY(globalObject, scope);
+ replacements.append(replacement.toString());
+ }
+
+ endOfLastMatch = matchEnd;
+ if (!isGlobal)
+ break;
+ matchStart = string.find(searchString, UNLIKELY(!searchStringLength) ? endOfLastMatch + 1 : endOfLastMatch);
+ } while (matchStart != notFound);
+
+ if (UNLIKELY(!sourceRanges.tryConstructAndAppend(endOfLastMatch, string.length() - endOfLastMatch)))
+ OUT_OF_MEMORY(globalObject, scope);
+ RELEASE_AND_RETURN(scope, jsSpliceSubstringsWithSeparators(globalObject, jsString, string, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
}
static inline bool checkObjectCoercible(JSValue thisValue)
@@ -872,7 +901,8 @@
{
if (searchValue.inherits<RegExpObject>(vm))
return replaceUsingRegExpSearch(vm, globalObject, callFrame, string, searchValue, replaceValue);
- return replaceUsingStringSearch(vm, globalObject, string, searchValue, replaceValue);
+ constexpr bool isGlobal = false;
+ return replaceUsingStringSearch(vm, globalObject, callFrame, string, searchValue, replaceValue, isGlobal);
}
ALWAYS_INLINE JSString* replace(
@@ -912,9 +942,22 @@
JSString* string = callFrame->thisValue().toString(globalObject);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
- RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, string, callFrame->argument(0), callFrame->argument(1))));
+ constexpr bool isGlobal = false;
+ RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, callFrame, string, callFrame->argument(0), callFrame->argument(1), isGlobal)));
}
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceAllUsingStringSearch(JSGlobalObject* globalObject, CallFrame* callFrame)
+{
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSString* string = callFrame->thisValue().toString(globalObject);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+ constexpr bool isGlobal = true;
+ RELEASE_AND_RETURN(scope, JSValue::encode(replaceUsingStringSearch(vm, globalObject, callFrame, string, callFrame->argument(0), callFrame->argument(1), isGlobal)));
+}
+
JSCell* JIT_OPERATION operationStringProtoFuncReplaceGeneric(JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue searchValue, EncodedJSValue replaceValue)
{
VM& vm = globalObject->vm();
Modified: trunk/Source/WTF/ChangeLog (252682 => 252683)
--- trunk/Source/WTF/ChangeLog 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/WTF/ChangeLog 2019-11-20 05:12:14 UTC (rev 252683)
@@ -1,3 +1,14 @@
+2019-11-19 Ross Kirsling <ross.kirsl...@sony.com>
+
+ Implement String.prototype.replaceAll
+ https://bugs.webkit.org/show_bug.cgi?id=202471
+
+ Reviewed by Yusuke Suzuki.
+
+ * wtf/text/StringCommon.h:
+ (WTF::findCommon):
+ Fix logic: "start > length" early out should come before "empty search string" early out.
+
2019-11-19 Yusuke Suzuki <ysuz...@apple.com>
[IndexedDB] IndexedDB's threading assertion should respect Web thread
Modified: trunk/Source/WTF/wtf/text/StringCommon.h (252682 => 252683)
--- trunk/Source/WTF/wtf/text/StringCommon.h 2019-11-20 04:31:20 UTC (rev 252682)
+++ trunk/Source/WTF/wtf/text/StringCommon.h 2019-11-20 05:12:14 UTC (rev 252683)
@@ -570,11 +570,12 @@
return WTF::find(haystack.characters16(), haystack.length(), needle[0], start);
}
+ if (start > haystack.length())
+ return notFound;
+
if (!needleLength)
return std::min(start, haystack.length());
- if (start > haystack.length())
- return notFound;
unsigned searchLength = haystack.length() - start;
if (needleLength > searchLength)
return notFound;