summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormeatloaf <ktulip2@protonmail.com>2021-02-03 20:53:17 -0600
committermeatloaf <ktulip2@protonmail.com>2021-02-04 12:59:54 -0600
commit24c40b583687e6f67ba57e6c8927515ceb5399ce (patch)
tree8b2a41127db4e432e31609f354ed62b56a5057e0
parentc5ad76a2875ca5c06c5bbff7b2f2e3ff7b3599c3 (diff)
downloadUXP-24c40b583687e6f67ba57e6c8927515ceb5399ce.tar
UXP-24c40b583687e6f67ba57e6c8927515ceb5399ce.tar.gz
UXP-24c40b583687e6f67ba57e6c8927515ceb5399ce.tar.lz
UXP-24c40b583687e6f67ba57e6c8927515ceb5399ce.tar.xz
UXP-24c40b583687e6f67ba57e6c8927515ceb5399ce.zip
Issue #1726 - Implement String.replaceAll()
This also implements IsRegExp, as this demands it. Ported from https://bugzilla.mozilla.org/show_bug.cgi?id=1540021
-rw-r--r--js/src/builtin/RegExp.js21
-rw-r--r--js/src/builtin/String.js109
-rw-r--r--js/src/jit/InlinableNatives.h1
-rw-r--r--js/src/jit/IonBuilder.h1
-rw-r--r--js/src/jit/MCallOptimize.cpp40
-rw-r--r--js/src/jsstr.cpp261
-rw-r--r--js/src/jsstr.h4
-rw-r--r--js/src/vm/SelfHosting.cpp66
8 files changed, 478 insertions, 25 deletions
diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js
index 91212066c..7218fc0e8 100644
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -1166,3 +1166,24 @@ function RegExpStringIteratorNext() {
result.value = match;
return result;
}
+
+// String.prototype.matchAll proposal.
+// ES2020 draft rev e97c95d064750fb949b6778584702dd658cf5624
+// 7.2.8 IsRegExp ( argument )
+function IsRegExp(argument) {
+ // Step 1.
+ if (!IsObject(argument)) {
+ return false;
+ }
+
+ // Step 2.
+ var matcher = argument[std_match];
+
+ // Step 3.
+ if (matcher !== undefined) {
+ return !!matcher;
+ }
+
+ // Steps 4-5.
+ return IsPossiblyWrappedRegExpObject(argument);
+}
diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js
index d07ec6127..c70f8558b 100644
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -224,6 +224,115 @@ function String_generic_replace(thisValue, searchValue, replaceValue) {
return callFunction(String_replace, thisValue, searchValue, replaceValue);
}
+function String_replaceAll(searchValue, replaceValue) {
+
+ // Step 1.
+ RequireObjectCoercible(this);
+
+ // Step 2.
+ if (searchValue !== undefined && searchValue !== null) {
+ // Steps 2.a-b.
+ if (IsRegExp(searchValue)) {
+ // Step 2.b.i.
+ var flags = searchValue.flags;
+
+ // Step 2.b.ii.
+ if (flags === undefined || flags === null) {
+ ThrowTypeError(JSMSG_FLAGS_UNDEFINED_OR_NULL);
+ }
+
+ // Step 2.b.iii.
+ if (!callFunction(std_String_includes, ToString(flags), "g")) {
+ ThrowTypeError(JSMSG_REQUIRES_GLOBAL_REGEXP, "replaceAll");
+ }
+ }
+
+ // Step 2.c.
+ var replacer = GetMethod(searchValue, std_replace);
+
+ // Step 2.b.
+ if (replacer !== undefined) {
+ return callContentFunction(replacer, searchValue, this, replaceValue);
+ }
+ }
+
+ // Step 3.
+ var string = ToString(this);
+
+ // Step 4.
+ var searchString = ToString(searchValue);
+
+ // Steps 5-6.
+ if (!IsCallable(replaceValue)) {
+ // Steps 7-16.
+ return StringReplaceAllString(string, searchString, ToString(replaceValue));
+ }
+
+ // Step 7.
+ var searchLength = searchString.length;
+
+ // Step 8.
+ var advanceBy = std_Math_max(1, searchLength);
+
+ // Step 9 (not needed in this implementation).
+
+ // Step 12.
+ var endOfLastMatch = 0;
+
+ // Step 13.
+ var result = "";
+
+ // Steps 10-11, 14.
+ var position = 0;
+ while (true) {
+ // Steps 10-11.
+ //
+ // StringIndexOf doesn't clamp the |position| argument to the input
+ // string length, i.e. |StringIndexOf("abc", "", 4)| returns -1,
+ // whereas |"abc".indexOf("", 4)| returns 3. That means we need to
+ // exit the loop when |nextPosition| is smaller than |position| and
+ // not just when |nextPosition| is -1.
+ var nextPosition = callFunction(std_String_indexOf, string, searchString, position);
+ if (nextPosition < position) {
+ break;
+ }
+ position = nextPosition;
+
+ // Step 14.a.
+ var replacement = ToString(callContentFunction(replaceValue, undefined, searchString,
+ position, string));
+
+ // Step 14.b (not applicable).
+
+ // Step 14.c.
+ var stringSlice = Substring(string, endOfLastMatch, position - endOfLastMatch);
+
+ // Step 14.d.
+ result += stringSlice + replacement;
+
+ // Step 14.e.
+ endOfLastMatch = position + searchLength;
+
+ // Step 11.b.
+ position += advanceBy;
+ }
+
+ // Step 15.
+ if (endOfLastMatch < string.length) {
+ // Step 15.a.
+ result += Substring(string, endOfLastMatch, string.length - endOfLastMatch);
+ }
+
+ // Step 16.
+ return result;
+}
+
+function String_generic_replace(thisValue, searchValue, replaceValue) {
+ if (thisValue === undefined)
+ ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.replace');
+ return callFunction(String_replace, thisValue, searchValue, replaceValue);
+}
+
function StringProtoHasNoSearch() {
var ObjectProto = GetBuiltinPrototype("Object");
var StringProto = GetBuiltinPrototype("String");
diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h
index e4fdf7ee2..01b5f7852 100644
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -66,6 +66,7 @@
_(RegExpSearcher) \
_(RegExpTester) \
_(IsRegExpObject) \
+ _(IntrinsicIsPossiblyWrappedRegExpObject)\
_(RegExpPrototypeOptimizable) \
_(RegExpInstanceOptimizable) \
_(GetFirstDollarIndex) \
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index a262f8166..402fbbf1a 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -867,6 +867,7 @@ class IonBuilder
InliningStatus inlineRegExpSearcher(CallInfo& callInfo);
InliningStatus inlineRegExpTester(CallInfo& callInfo);
InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
+ InliningStatus inlineIsPossiblyWrappedRegExpObject(CallInfo& callInfo);
InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo);
InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo);
InliningStatus inlineGetFirstDollarIndex(CallInfo& callInfo);
diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp
index 182fa2fd5..23e894ffb 100644
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -185,6 +185,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineRegExpTester(callInfo);
case InlinableNative::IsRegExpObject:
return inlineIsRegExpObject(callInfo);
+ case InlinableNative::IntrinsicIsPossiblyWrappedRegExpObject:
+ return inlineIsPossiblyWrappedRegExpObject(callInfo);
case InlinableNative::RegExpPrototypeOptimizable:
return inlineRegExpPrototypeOptimizable(callInfo);
case InlinableNative::RegExpInstanceOptimizable:
@@ -1902,6 +1904,44 @@ IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
callInfo.setImplicitlyUsedUnchecked();
return InliningStatus_Inlined;
}
+IonBuilder::InliningStatus
+IonBuilder::inlineIsPossiblyWrappedRegExpObject(CallInfo& callInfo)
+{
+ MOZ_ASSERT(!callInfo.constructing());
+ MOZ_ASSERT(callInfo.argc() == 1);
+
+ if (callInfo.getArg(0)->type() != MIRType::Object)
+ return InliningStatus_NotInlined;
+ if (getInlineReturnType() != MIRType::Boolean)
+ return InliningStatus_NotInlined;
+
+ MDefinition* arg = callInfo.getArg(0);
+ if (arg->type() != MIRType::Object) {
+ return InliningStatus_NotInlined;
+ }
+
+ TemporaryTypeSet* types = arg->resultTypeSet();
+ if (!types) {
+ return InliningStatus_NotInlined;
+ }
+
+ // Don't inline if the argument might be a wrapper.
+ if (types->forAllClasses(constraints(), IsProxyClass) !=
+ TemporaryTypeSet::ForAllResult::ALL_FALSE) {
+ return InliningStatus_NotInlined;
+ }
+
+ if (const Class* clasp = types->getKnownClass(constraints())) {
+ pushConstant(BooleanValue(clasp == &RegExpObject::class_));
+ } else {
+ MHasClass* hasClass = MHasClass::New(alloc(), arg, &RegExpObject::class_);
+ current->add(hasClass);
+ current->push(hasClass);
+ }
+
+ callInfo.setImplicitlyUsedUnchecked();
+ return InliningStatus_Inlined;
+}
IonBuilder::InliningStatus
IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index 7a0861998..6eb11b1cc 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2111,9 +2111,13 @@ AppendDollarReplacement(StringBuffer& newReplaceChars, size_t firstDollarIndex,
const CharT* repChars, size_t repLength)
{
MOZ_ASSERT(firstDollarIndex < repLength);
+ MOZ_ASSERT(matchStart <= matchLimit);
+ MOZ_ASSERT(matchLimit <= text->length());
/* Move the pre-dollar chunk in bulk. */
- newReplaceChars.infallibleAppend(repChars, firstDollarIndex);
+ if (!newReplaceChars.append(repChars, firstDollarIndex)) {
+ return false;
+ }
/* Move the rest char-by-char, interpreting dollars as we encounter them. */
const CharT* repLimit = repChars + repLength;
@@ -2329,6 +2333,200 @@ js::str_flat_replace_string(JSContext *cx, HandleString string, HandleString pat
return str;
}
+// https://tc39.es/proposal-string-replaceall/#sec-string.prototype.replaceall
+// Steps 7-16 when functionalReplace is false and searchString is not empty.
+//
+// The steps are quite different, for performance. Loops in steps 11 and 14
+// are fused. GetSubstitution is optimized away when possible.
+template <typename StrChar, typename RepChar>
+static JSString* ReplaceAll(JSContext* cx, JSLinearString* str,
+ JSLinearString* pat,
+ JSLinearString* rep) {
+ // Step 7.
+ const size_t stringLength = str->length();
+ const size_t searchLength = pat->length();
+ const size_t replaceLength = rep->length();
+
+ MOZ_ASSERT(stringLength > 0);
+ MOZ_ASSERT(searchLength > 0);
+ MOZ_ASSERT(stringLength >= searchLength);
+
+ // Step 8 (advanceBy is equal to searchLength when searchLength > 0).
+
+ // Step 9 (not needed in this implementation).
+
+ // Step 10.
+ // Find the first match.
+ int32_t position = StringMatch(str, pat, 0);
+
+ // Nothing to replace, so return early.
+ if (position < 0) {
+ return str;
+ }
+
+ // Step 11 (moved below).
+
+ // Step 12.
+ uint32_t endOfLastMatch = 0;
+
+ // Step 13.
+ StringBuffer result(cx);
+ if (std::is_same<StrChar, char16_t>::value ||
+ std::is_same<RepChar, char16_t>::value) {
+ if (!result.ensureTwoByteChars()) {
+ return nullptr;
+ }
+ }
+
+ {
+ AutoCheckCannotGC nogc;
+ const StrChar* strChars = str->chars<StrChar>(nogc);
+ const RepChar* repChars = rep->chars<RepChar>(nogc);
+
+ uint32_t dollarIndex = FindDollarIndex(repChars, replaceLength);
+
+ // If it's true, we are sure that the result's length is, at least, the same
+ // length as |str->length()|.
+ if (replaceLength >= searchLength) {
+ if (!result.reserve(stringLength)) {
+ return nullptr;
+ }
+ }
+
+ do {
+ // Step 14.c.
+ // Append the substring before the current match.
+ if (!result.append(strChars + endOfLastMatch,
+ position - endOfLastMatch)) {
+ return nullptr;
+ }
+
+ // Steps 14.a-b and 14.d.
+ // Append the replacement.
+ if (dollarIndex != UINT32_MAX) {
+ size_t matchLimit = position + searchLength;
+ if (!AppendDollarReplacement(result, dollarIndex, position, matchLimit,
+ str, repChars, replaceLength)) {
+ return nullptr;
+ }
+ } else {
+ if (!result.append(repChars, replaceLength)) {
+ return nullptr;
+ }
+ }
+
+ // Step 14.e.
+ endOfLastMatch = position + searchLength;
+
+ // Step 11.
+ // Find the next match.
+ position = StringMatch(str, pat, endOfLastMatch);
+ } while (position >= 0);
+
+ // Step 15.
+ // Append the substring after the last match.
+ if (!result.append(strChars + endOfLastMatch,
+ stringLength - endOfLastMatch)) {
+ return nullptr;
+ }
+ }
+
+ // Step 16.
+ return result.finishString();
+}
+
+// https://tc39.es/proposal-string-replaceall/#sec-string.prototype.replaceall
+// Steps 7-16 when functionalReplace is false and searchString is the empty
+// string.
+//
+// The steps are quite different, for performance. Loops in steps 11 and 14
+// are fused. GetSubstitution is optimized away when possible.
+template <typename StrChar, typename RepChar>
+static JSString* ReplaceAllInterleave(JSContext* cx, JSLinearString* str,
+ JSLinearString* rep) {
+ // Step 7.
+ const size_t stringLength = str->length();
+ const size_t replaceLength = rep->length();
+
+ // Step 8 (advanceBy is 1 when searchString is the empty string).
+
+ // Steps 9-12 (trivial when searchString is the empty string).
+
+ // Step 13.
+ StringBuffer result(cx);
+ if (std::is_same<StrChar, char16_t>::value ||
+ std::is_same<RepChar, char16_t>::value) {
+ if (!result.ensureTwoByteChars()) {
+ return nullptr;
+ }
+ }
+
+ {
+ AutoCheckCannotGC nogc;
+ const StrChar* strChars = str->chars<StrChar>(nogc);
+ const RepChar* repChars = rep->chars<RepChar>(nogc);
+
+ uint32_t dollarIndex = FindDollarIndex(repChars, replaceLength);
+
+ if (dollarIndex != UINT32_MAX) {
+ if (!result.reserve(stringLength)) {
+ return nullptr;
+ }
+ } else {
+ // Compute the exact result length when no substitutions take place.
+ CheckedInt<uint32_t> strLength(stringLength);
+ CheckedInt<uint32_t> repLength(replaceLength);
+ CheckedInt<uint32_t> length = strLength + (strLength + 1) * repLength;
+ if (!length.isValid()) {
+ ReportAllocationOverflow(cx);
+ return nullptr;
+ }
+
+ if (!result.reserve(length.value())) {
+ return nullptr;
+ }
+ }
+
+ auto appendReplacement = [&](size_t match) {
+ if (dollarIndex != UINT32_MAX) {
+ return AppendDollarReplacement(result, dollarIndex, match, match,
+ str, repChars, replaceLength);
+ }
+ return result.append(repChars, replaceLength);
+ };
+
+ for (size_t index = 0; index < stringLength; index++) {
+ // Steps 11, 14.a-b and 14.d.
+ // The empty string matches before each character.
+ if (!appendReplacement(index)) {
+ return nullptr;
+ }
+
+ // Step 14.c.
+ if (!result.append(strChars[index])) {
+ return nullptr;
+ }
+ }
+
+ // Steps 11, 14.a-b and 14.d.
+ // The empty string also matches at the end of the string.
+ if (!appendReplacement(stringLength)) {
+ return nullptr;
+ }
+
+ // Step 15 (not applicable when searchString is the empty string).
+ }
+
+ // Step 16.
+ return result.finishString();
+}
+
+// String.prototype.replaceAll (Stage 3 proposal)
+// https://tc39.es/proposal-string-replaceall/
+//
+// String.prototype.replaceAll ( searchValue, replaceValue )
+//
+// Steps 7-16 when functionalReplace is false.o
JSString*
js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
HandleString replacement)
@@ -2371,6 +2569,62 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt
return BuildFlatReplacement(cx, string, repl, match, patternLength);
}
+JSString*
+js::str_replaceAll_string_raw(JSContext* cx, HandleString string, HandleString pattern,
+ HandleString replacement)
+{
+ const size_t stringLength = string->length();
+ const size_t searchLength = pattern->length();
+
+ // Directly return when we're guaranteed to find no match.
+ if (searchLength > stringLength) {
+ return string;
+ }
+
+ RootedLinearString str(cx, string->ensureLinear(cx));
+ if (!str) {
+ return nullptr;
+ }
+
+ RootedLinearString repl(cx, replacement->ensureLinear(cx));
+ if (!repl) {
+ return nullptr;
+ }
+
+ RootedLinearString search(cx, pattern->ensureLinear(cx));
+ if (!search) {
+ return nullptr;
+ }
+
+ // The pattern is empty, so we interleave the replacement string in-between
+ // each character.
+ if (searchLength == 0) {
+ if (str->hasTwoByteChars()) {
+ if (repl->hasTwoByteChars()) {
+ return ReplaceAllInterleave<char16_t, char16_t>(cx, str, repl);
+ }
+ return ReplaceAllInterleave<char16_t, Latin1Char>(cx, str, repl);
+ }
+ if (repl->hasTwoByteChars()) {
+ return ReplaceAllInterleave<Latin1Char, char16_t>(cx, str, repl);
+ }
+ return ReplaceAllInterleave<Latin1Char, Latin1Char>(cx, str, repl);
+ }
+
+ MOZ_ASSERT(stringLength > 0);
+
+ if (str->hasTwoByteChars()) {
+ if (repl->hasTwoByteChars()) {
+ return ReplaceAll<char16_t, char16_t>(cx, str, search, repl);
+ }
+ return ReplaceAll<char16_t, Latin1Char>(cx, str, search, repl);
+ }
+ if (repl->hasTwoByteChars()) {
+ return ReplaceAll<Latin1Char, char16_t>(cx, str, search, repl);
+ }
+ return ReplaceAll<Latin1Char, Latin1Char>(cx, str, search, repl);
+}
+
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
static JSObject*
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
@@ -2572,9 +2826,9 @@ static const JSFunctionSpec string_methods[] = {
JS_FN("endsWith", str_endsWith, 1,0),
JS_FN("trim", str_trim, 0,0),
JS_FN("trimLeft", str_trimStart, 0,0),
- JS_FN("trimStart", str_trimStart, 0,0),
+ JS_FN("trimStart", str_trimStart, 0,0),
JS_FN("trimRight", str_trimEnd, 0,0),
- JS_FN("trimEnd", str_trimEnd, 0,0),
+ JS_FN("trimEnd", str_trimEnd, 0,0),
JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,0),
JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,0),
JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
@@ -2586,6 +2840,7 @@ static const JSFunctionSpec string_methods[] = {
JS_SELF_HOSTED_FN("matchAll", "String_matchAll", 1,0),
JS_SELF_HOSTED_FN("search", "String_search", 1,0),
JS_SELF_HOSTED_FN("replace", "String_replace", 2,0),
+ JS_SELF_HOSTED_FN("replaceAll", "String_replaceAll", 2,0),
JS_SELF_HOSTED_FN("split", "String_split", 2,0),
JS_SELF_HOSTED_FN("substr", "String_substr", 2,0),
diff --git a/js/src/jsstr.h b/js/src/jsstr.h
index 42891900f..0e31276a8 100644
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -476,6 +476,10 @@ JSString*
str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
HandleString replacement);
+JSString*
+str_replaceAll_string_raw(JSContext* cx, HandleString string, HandleString pattern,
+ HandleString replacement);
+
extern bool
StringConstructor(JSContext* cx, unsigned argc, Value* vp);
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 9c20bbe9d..38785a822 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -191,6 +191,28 @@ intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
template<typename T>
static bool
+intrinsic_IsPossiblyWrappedBuiltin(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 1);
+
+ bool isTypeT = false;
+ if (args[0].isObject()) {
+ JSObject* obj = CheckedUnwrap(&args[0].toObject());
+ if (!obj) {
+ JS_ReportErrorASCII(cx, "Permission denied to access object");
+ return false;
+ }
+
+ isTypeT = obj->is<T>();
+ }
+
+ args.rval().setBoolean(isTypeT);
+ return true;
+}
+
+template<typename T>
+static bool
intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
@@ -1173,27 +1195,6 @@ intrinsic_IsFloat32TypedArray(JSContext* cx, unsigned argc, Value* vp)
}
static bool
-intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- MOZ_ASSERT(args.length() == 1);
-
- bool isTypedArray = false;
- if (args[0].isObject()) {
- JSObject* obj = CheckedUnwrap(&args[0].toObject());
- if (!obj) {
- JS_ReportErrorASCII(cx, "Permission denied to access object");
- return false;
- }
-
- isTypedArray = obj->is<TypedArrayObject>();
- }
-
- args.rval().setBoolean(isTypedArray);
- return true;
-}
-
-static bool
intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
@@ -1756,6 +1757,24 @@ intrinsic_StringReplaceString(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+static bool
+intrinsic_StringReplaceAllString(JSContext * cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 3);
+
+ RootedString string(cx, args[0].toString());
+ RootedString pattern(cx, args[1].toString());
+ RootedString replacement(cx, args[2].toString());
+ JSString* result = str_replaceAll_string_raw(cx, string, pattern, replacement);
+ if (!result) {
+ return false;
+ }
+
+ args.rval().setString(result);
+ return true;
+}
+
bool
js::intrinsic_StringSplitString(JSContext* cx, unsigned argc, Value* vp)
{
@@ -2401,7 +2420,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("IsTypedArray",
intrinsic_IsInstanceOfBuiltin<TypedArrayObject>, 1,0,
IntrinsicIsTypedArray),
- JS_INLINABLE_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0,
+ JS_INLINABLE_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedBuiltin<TypedArrayObject>,1,0,
IntrinsicIsPossiblyWrappedTypedArray),
JS_FN("TypedArrayBuffer", intrinsic_TypedArrayBuffer, 1,0),
@@ -2513,6 +2532,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("IsRegExpObject",
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
IsRegExpObject),
+ JS_INLINABLE_FN("IsPossiblyWrappedRegExpObject", intrinsic_IsPossiblyWrappedBuiltin<RegExpObject>,1,0,
+ IntrinsicIsPossiblyWrappedRegExpObject),
JS_FN("CallRegExpMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0,
@@ -2536,6 +2557,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("FlatStringSearch", FlatStringSearch, 2,0),
JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0,
IntrinsicStringReplaceString),
+ JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0),
JS_INLINABLE_FN("StringSplitString", intrinsic_StringSplitString, 2, 0,
IntrinsicStringSplitString),
JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0),