commit 8a6600bfda89f3a61f6309ed0f109728130d28d8
parent c4692f4209d521f00fac507337bfd7eba8d02227
Author: André Bargull <andre.bargull@gmail.com>
Date: Mon, 20 Oct 2025 12:27:43 +0000
Bug 1991402 - Part 12: Convert RegExp getters. r=jandem
Differential Revision: https://phabricator.services.mozilla.com/D266604
Diffstat:
9 files changed, 97 insertions(+), 96 deletions(-)
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
@@ -15,6 +15,7 @@
#include "frontend/FrontendContext.h" // AutoReportFrontendContext
#include "frontend/TokenStream.h"
#include "irregexp/RegExpAPI.h"
+#include "jit/InlinableNatives.h"
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_NEWREGEXP_FLAGGED
#include "js/PropertySpec.h"
#include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
@@ -964,15 +965,15 @@ bool js::regexp_unicodeSets(JSContext* cx, unsigned argc, JS::Value* vp) {
const JSPropertySpec js::regexp_properties[] = {
JS_SELF_HOSTED_GET("flags", "$RegExpFlagsGetter", 0),
- JS_PSG("hasIndices", regexp_hasIndices, 0),
- JS_PSG("global", regexp_global, 0),
- JS_PSG("ignoreCase", regexp_ignoreCase, 0),
- JS_PSG("multiline", regexp_multiline, 0),
- JS_PSG("dotAll", regexp_dotAll, 0),
+ JS_INLINABLE_PSG("hasIndices", regexp_hasIndices, 0, RegExpHasIndices),
+ JS_INLINABLE_PSG("global", regexp_global, 0, RegExpGlobal),
+ JS_INLINABLE_PSG("ignoreCase", regexp_ignoreCase, 0, RegExpIgnoreCase),
+ JS_INLINABLE_PSG("multiline", regexp_multiline, 0, RegExpMultiline),
+ JS_INLINABLE_PSG("dotAll", regexp_dotAll, 0, RegExpDotAll),
JS_PSG("source", regexp_source, 0),
- JS_PSG("sticky", regexp_sticky, 0),
- JS_PSG("unicode", regexp_unicode, 0),
- JS_PSG("unicodeSets", regexp_unicodeSets, 0),
+ JS_INLINABLE_PSG("sticky", regexp_sticky, 0, RegExpSticky),
+ JS_INLINABLE_PSG("unicode", regexp_unicode, 0, RegExpUnicode),
+ JS_INLINABLE_PSG("unicodeSets", regexp_unicodeSets, 0, RegExpUnicodeSets),
JS_PS_END,
};
diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h
@@ -164,7 +164,7 @@ extern const JSFunctionSpec regexp_static_methods[];
extern const JSPropertySpec regexp_properties[];
extern const JSFunctionSpec regexp_methods[];
-// Used in RegExpObject::isOriginalFlagGetter.
+// Used in OptimizeRegExpPrototypeFuse::checkInvariant.
[[nodiscard]] extern bool regexp_hasIndices(JSContext* cx, unsigned argc,
JS::Value* vp);
[[nodiscard]] extern bool regexp_global(JSContext* cx, unsigned argc,
diff --git a/js/src/jit-test/tests/regexp/flag-getters.js b/js/src/jit-test/tests/regexp/flag-getters.js
@@ -10,6 +10,16 @@ function testGlobal() {
}
for (let i = 0; i < 2; ++i) testGlobal();
+function testHasIndices() {
+ const xs = [/a/, /b/d];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.hasIndices, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testHasIndices();
+
function testIgnoreCase() {
const xs = [/a/, /b/i];
@@ -50,6 +60,16 @@ function testUnicode() {
}
for (let i = 0; i < 2; ++i) testUnicode();
+function testUnicodeSets() {
+ const xs = [/a/, /b/v];
+
+ for (let i = 0; i < 200; ++i) {
+ let x = xs[i & 1];
+ assertEq(x.unicodeSets, !!(i & 1));
+ }
+}
+for (let i = 0; i < 2; ++i) testUnicodeSets();
+
function testSticky() {
const xs = [/a/, /b/y];
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
@@ -465,7 +465,6 @@ AttachDecision GetPropIRGenerator::tryAttachStub() {
if (nameOrSymbol) {
TRY_ATTACH(tryAttachObjectLength(obj, objId, id));
- TRY_ATTACH(tryAttachRegExp(obj, objId, id));
TRY_ATTACH(tryAttachNative(obj, objId, id, receiverId));
TRY_ATTACH(tryAttachModuleNamespace(obj, objId, id));
TRY_ATTACH(tryAttachWindowProxy(obj, objId, id));
@@ -2461,49 +2460,6 @@ AttachDecision GetPropIRGenerator::tryAttachInlinableNativeGetter(
return nativeGen.tryAttachStub();
}
-AttachDecision GetPropIRGenerator::tryAttachRegExp(HandleObject obj,
- ObjOperandId objId,
- HandleId id) {
- if (!obj->is<RegExpObject>()) {
- return AttachDecision::NoAction;
- }
- auto* regExp = &obj->as<RegExpObject>();
-
- if (mode_ != ICState::Mode::Specialized) {
- return AttachDecision::NoAction;
- }
-
- // Receiver should be the object.
- if (isSuper()) {
- return AttachDecision::NoAction;
- }
-
- NativeObject* holder = nullptr;
- Maybe<PropertyInfo> prop;
- NativeGetPropKind kind =
- CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_);
- if (kind != NativeGetPropKind::NativeGetter) {
- return AttachDecision::NoAction;
- }
-
- auto& fun = holder->getGetter(*prop)->as<JSFunction>();
- JS::RegExpFlags flags = JS::RegExpFlag::NoFlags;
- if (!RegExpObject::isOriginalFlagGetter(fun.native(), &flags)) {
- return AttachDecision::NoAction;
- }
-
- maybeEmitIdGuard(id);
- // Emit all the normal guards for calling this native, but specialize
- // callNativeGetterResult.
- emitCallGetterResultGuards(regExp, holder, id, *prop, objId);
-
- writer.regExpFlagResult(objId, flags.value());
- writer.returnFromIC();
-
- trackAttached("GetProp.RegExpFlag");
- return AttachDecision::Attach;
-}
-
AttachDecision GetPropIRGenerator::tryAttachFunction(HandleObject obj,
ObjOperandId objId,
HandleId id) {
@@ -7745,6 +7701,38 @@ AttachDecision InlinableNativeIRGenerator::tryAttachHasClass(
return AttachDecision::Attach;
}
+AttachDecision InlinableNativeIRGenerator::tryAttachRegExpFlag(
+ JS::RegExpFlags flags) {
+ // Expecting no arguments.
+ if (args_.length() != 0) {
+ return AttachDecision::NoAction;
+ }
+
+ // Ensure |this| is a RegExpObject.
+ if (!thisval_.isObject() || !thisval_.toObject().is<RegExpObject>()) {
+ return AttachDecision::NoAction;
+ }
+
+ auto* regExp = &thisval_.toObject().as<RegExpObject>();
+
+ // Initialize the input operand.
+ Int32OperandId argcId = initializeInputOperand();
+
+ // Guard callee is the native RegExp getter function.
+ ObjOperandId calleeId = emitNativeCalleeGuard(argcId);
+
+ // Guard |this| is a RegExpObject.
+ ValOperandId thisValId = loadThis(calleeId);
+ ObjOperandId objId = writer.guardToObject(thisValId);
+ writer.guardShapeForClass(objId, regExp->shape());
+
+ writer.regExpFlagResult(objId, flags.value());
+ writer.returnFromIC();
+
+ trackAttached("RegExpFlag");
+ return AttachDecision::Attach;
+}
+
// Returns whether the .lastIndex property is a non-negative int32 value and is
// still writable.
static bool HasOptimizableLastIndexSlot(RegExpObject* regexp, JSContext* cx) {
@@ -13246,6 +13234,22 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
return tryAttachArrayIteratorPrototypeOptimizable();
// RegExp natives.
+ case InlinableNative::RegExpDotAll:
+ return tryAttachRegExpFlag(JS::RegExpFlag::DotAll);
+ case InlinableNative::RegExpGlobal:
+ return tryAttachRegExpFlag(JS::RegExpFlag::Global);
+ case InlinableNative::RegExpHasIndices:
+ return tryAttachRegExpFlag(JS::RegExpFlag::HasIndices);
+ case InlinableNative::RegExpIgnoreCase:
+ return tryAttachRegExpFlag(JS::RegExpFlag::IgnoreCase);
+ case InlinableNative::RegExpMultiline:
+ return tryAttachRegExpFlag(JS::RegExpFlag::Multiline);
+ case InlinableNative::RegExpSticky:
+ return tryAttachRegExpFlag(JS::RegExpFlag::Sticky);
+ case InlinableNative::RegExpUnicode:
+ return tryAttachRegExpFlag(JS::RegExpFlag::Unicode);
+ case InlinableNative::RegExpUnicodeSets:
+ return tryAttachRegExpFlag(JS::RegExpFlag::UnicodeSets);
case InlinableNative::IsRegExpObject:
return tryAttachHasClass(&RegExpObject::class_,
/* isPossiblyWrapped = */ false);
diff --git a/js/src/jit/CacheIRGenerator.h b/js/src/jit/CacheIRGenerator.h
@@ -31,7 +31,8 @@ class JSFunction;
namespace JS {
struct XrayJitInfo;
-}
+class RegExpFlags;
+} // namespace JS
namespace js {
@@ -176,8 +177,6 @@ class MOZ_RAII GetPropIRGenerator : public IRGenerator {
ValOperandId receiverId);
AttachDecision tryAttachObjectLength(HandleObject obj, ObjOperandId objId,
HandleId id);
- AttachDecision tryAttachRegExp(HandleObject obj, ObjOperandId objId,
- HandleId id);
AttachDecision tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId,
HandleId id);
AttachDecision tryAttachWindowProxy(HandleObject obj, ObjOperandId objId,
@@ -773,6 +772,7 @@ class MOZ_RAII InlinableNativeIRGenerator {
AttachDecision tryAttachGuardToSharedArrayBuffer();
AttachDecision tryAttachHasClass(const JSClass* clasp,
bool isPossiblyWrapped);
+ AttachDecision tryAttachRegExpFlag(JS::RegExpFlags flags);
AttachDecision tryAttachRegExpMatcherSearcher(InlinableNative native);
AttachDecision tryAttachRegExpSearcherLastLimit();
AttachDecision tryAttachRegExpHasCaptureGroups();
diff --git a/js/src/jit/InlinableNatives.cpp b/js/src/jit/InlinableNatives.cpp
@@ -325,6 +325,14 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::NumberParseInt:
case InlinableNative::NumberToString:
case InlinableNative::ReflectGetPrototypeOf:
+ case InlinableNative::RegExpDotAll:
+ case InlinableNative::RegExpGlobal:
+ case InlinableNative::RegExpHasIndices:
+ case InlinableNative::RegExpIgnoreCase:
+ case InlinableNative::RegExpMultiline:
+ case InlinableNative::RegExpSticky:
+ case InlinableNative::RegExpUnicode:
+ case InlinableNative::RegExpUnicodeSets:
case InlinableNative::SetConstructor:
case InlinableNative::SetHas:
case InlinableNative::SetDelete:
diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h
@@ -157,6 +157,14 @@
\
_(ReflectGetPrototypeOf) \
\
+ _(RegExpDotAll) \
+ _(RegExpGlobal) \
+ _(RegExpHasIndices) \
+ _(RegExpIgnoreCase) \
+ _(RegExpMultiline) \
+ _(RegExpSticky) \
+ _(RegExpUnicode) \
+ _(RegExpUnicodeSets) \
_(RegExpMatcher) \
_(RegExpSearcher) \
_(RegExpSearcherLastLimit) \
diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
@@ -117,44 +117,6 @@ RegExpShared* RegExpObject::getShared(JSContext* cx,
return createShared(cx, regexp);
}
-/* static */
-bool RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlags* mask) {
- if (native == regexp_hasIndices) {
- *mask = RegExpFlag::HasIndices;
- return true;
- }
- if (native == regexp_global) {
- *mask = RegExpFlag::Global;
- return true;
- }
- if (native == regexp_ignoreCase) {
- *mask = RegExpFlag::IgnoreCase;
- return true;
- }
- if (native == regexp_multiline) {
- *mask = RegExpFlag::Multiline;
- return true;
- }
- if (native == regexp_dotAll) {
- *mask = RegExpFlag::DotAll;
- return true;
- }
- if (native == regexp_sticky) {
- *mask = RegExpFlag::Sticky;
- return true;
- }
- if (native == regexp_unicode) {
- *mask = RegExpFlag::Unicode;
- return true;
- }
- if (native == regexp_unicodeSets) {
- *mask = RegExpFlag::UnicodeSets;
- return true;
- }
-
- return false;
-}
-
static const ClassSpec RegExpObjectClassSpec = {
GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
GenericCreatePrototype<RegExpObject>,
diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h
@@ -159,8 +159,6 @@ class RegExpObject : public NativeObject {
return flags.global() || flags.sticky();
}
- static bool isOriginalFlagGetter(JSNative native, JS::RegExpFlags* mask);
-
static RegExpShared* getShared(JSContext* cx, Handle<RegExpObject*> regexp);
bool hasShared() const { return !getFixedSlot(SHARED_SLOT).isUndefined(); }