mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 22:38:41 +09:00
Issue #1726 - Implement String.replaceAll()
This also implements IsRegExp, as this demands it. Ported from https://bugzilla.mozilla.org/show_bug.cgi?id=1540021
This commit is contained in:
parent
02233308b1
commit
00a689060f
8 changed files with 478 additions and 25 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
_(RegExpSearcher) \
|
||||
_(RegExpTester) \
|
||||
_(IsRegExpObject) \
|
||||
_(IntrinsicIsPossiblyWrappedRegExpObject)\
|
||||
_(RegExpPrototypeOptimizable) \
|
||||
_(RegExpInstanceOptimizable) \
|
||||
_(GetFirstDollarIndex) \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
261
js/src/jsstr.cpp
261
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),
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,28 @@ intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -1172,27 +1194,6 @@ intrinsic_IsFloat32TypedArray(JSContext* cx, unsigned argc, Value* vp)
|
|||
return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Float32);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue