commit 1aadb8502c27eef95c92f3c08ccb1f98544c2090
parent 48f61ddc988698e572a33f6379cdf5ad4a22c69f
Author: Jan de Mooij <jdemooij@mozilla.com>
Date: Tue, 21 Oct 2025 11:53:58 +0000
Bug 1994205 part 2 - Resolve .prototype property in InstanceOfIRGenerator if needed. r=iain
This fixes a performance cliff when the LHS is a primitive. In that case
the fallback code will not resolve the lazy `RHS.prototype` property but our
CacheIR code currently requires a prototype object for `LoadInstanceOfObjectResult`.
We could emit different CacheIR ops for this case involving a primitive LHS with a
not-yet-resolved `RHS.prototype`, but that seemed more complicated.
Differential Revision: https://phabricator.services.mozilla.com/D269253
Diffstat:
2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/js/src/jit-test/tests/cacheir/instanceof-primitive-lazy-prototype.js b/js/src/jit-test/tests/cacheir/instanceof-primitive-lazy-prototype.js
@@ -0,0 +1,22 @@
+function Foo1() {}
+
+function test1() {
+ var arr = [1, "a", null, undefined];
+ for (var i = 0; i < 20; i++) {
+ var val = arr[i % arr.length];
+ assertEq(val instanceof Foo1, false);
+ }
+}
+test1();
+
+function Foo2() {}
+function Foo3() {}
+
+function test2() {
+ for (var i = 0; i < 20; i++) {
+ var val = i < 17 ? 1 : new Foo2();
+ assertEq(val instanceof Foo2, i >= 17);
+ assertEq(val instanceof Foo3, false);
+ }
+}
+test2();
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
@@ -5710,16 +5710,15 @@ AttachDecision InstanceOfIRGenerator::tryAttachFunction() {
// hasInstance hook will not change without the need to guard on the actual
// property value.
PropertyResult hasInstanceProp;
- NativeObject* hasInstanceHolder = nullptr;
+ Rooted<NativeObject*> hasInstanceHolder(cx_);
jsid hasInstanceID = PropertyKey::Symbol(cx_->wellKnownSymbols().hasInstance);
- if (!LookupPropertyPure(cx_, fun, hasInstanceID, &hasInstanceHolder,
+ if (!LookupPropertyPure(cx_, fun, hasInstanceID, hasInstanceHolder.address(),
&hasInstanceProp) ||
!hasInstanceProp.isNativeProperty()) {
return AttachDecision::NoAction;
}
- JSObject& funProto = cx_->global()->getPrototype(JSProto_Function);
- if (hasInstanceHolder != &funProto) {
+ if (hasInstanceHolder != &cx_->global()->getPrototype(JSProto_Function)) {
return AttachDecision::NoAction;
}
@@ -5734,7 +5733,21 @@ AttachDecision InstanceOfIRGenerator::tryAttachFunction() {
// Look up the function's .prototype property.
Maybe<PropertyInfo> prototypeProp = fun->lookupPure(cx_->names().prototype);
if (prototypeProp.isNothing()) {
- return AttachDecision::NoAction;
+ if (!fun->needsPrototypeProperty()) {
+ return AttachDecision::NoAction;
+ }
+ // The function does not have a (lazily resolved) .prototype property yet.
+ // If the LHS is a primitive, the fallback code in OrdinaryHasInstance will
+ // return before resolving this property. Our CacheIR implementation expects
+ // a .prototype property so we resolve it now.
+ bool hasProp;
+ if (!HasProperty(cx_, fun, cx_->names().prototype, &hasProp)) {
+ cx_->clearPendingException();
+ return AttachDecision::NoAction;
+ }
+ MOZ_ASSERT(hasProp);
+ prototypeProp = fun->lookupPure(cx_->names().prototype);
+ MOZ_ASSERT(prototypeProp);
}
if (!prototypeProp->isDataProperty()) {
return AttachDecision::NoAction;