tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Ajs/src/jit-test/tests/cacheir/instanceof-primitive-lazy-prototype.js | 22++++++++++++++++++++++
Mjs/src/jit/CacheIR.cpp | 23++++++++++++++++++-----
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;