Closed Bug 693811 Opened 13 years ago Closed 13 years ago

"Assertion failure: shouldCacheProtoShape(cx, proto, &shouldCache) && shouldCache" in ListBase::nativeGet

Categories

(Core :: XPConnect, defect)

x86_64
macOS
defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla13
Tracking Status
firefox10 - ---

People

(Reporter: jruderman, Assigned: peterv)

References

Details

(Keywords: assertion, regression, testcase)

Attachments

(4 files, 3 obsolete files)

Assertion failure: shouldCacheProtoShape(cx, proto, &shouldCache) && shouldCache, at js/src/xpconnect/src/dombindings.cpp:888
Attached file stack trace (deleted) —
Over to peterv for investigation.
Assignee: nobody → peterv
Shape doesn't cover the values of properties, so the cached shape didn't change. The assert looks wrong, but really the underlying assumptions are wrong. We rely on a shape change to detect changes in values.
Can we somehow guard on TI information instead of guarding on shape here?  Does TI even have the information we need?
What is the information being guarded on?  Changes in values can trigger changes in types (e.g. changing function-valued properties), but those changes can't really be guarded on in the same manner as shapes.  When a type changes it can trigger jitcode recompilation, and could potentially trigger e.g. invalidation of certain caches also.  We don't do the latter anywhere; I haven't seen places it is worth the hassle, and additionally TI isn't always on.
The basic setup here is that we have a proxy whose prototype has a property named 'item' which is a JS function that's backed by a fastnative that goes and calls into Gecko code.

Since this is a proxy there are no ICs going on in the jit, so when someone does .item() on it we have to walk up the proto chain looking for the property, then return the value it has.  This is somewhat slow.

So what we'd like to do is to guard on the case when the prototype is "correct" in the sense that its .item is the function we expect.  And in that case, we can just directly return that function without having to do a full prototype chain walk.

The current code actually guards on all the prototype's methods being what we expect (which is the common case).  But it only guards on the shape, which of course does not capture the actual value of the method-valued properties...
I thought shapes did include the values of properties of type function. Did that change?
Why is it faster for the optimization to check the prototype than for the normal codepath to walk up one level in the prototype chain?
(In reply to Jesse Ruderman from comment #7)
> I thought shapes did include the values of properties of type function. Did
> that change?

This is sometimes true --- shape determines the particular function for branded objects and method shapes, neither of which I think will apply here.  Also, the object shrinking stuff going on in the JM branch removes both of these constraints.

I think for now the code should check the slot value as well, the shape check ensures it is in a particular slot which should be fast to access.  The fastest solution will be to teach the JIT about the behavior of the proxy, so that it can generate code which will be invalidated should the prototype's 'item' property change.  That is some way off, though.
> Why is it faster for the optimization to check the prototype than for the normal codepath
> to walk up one level in the prototype chain?

A single shape check on the proto followed by the return of a cached value from a slot is faster than JS_GetProperty with all its machinery.

Brian, you're basically suggesting that the assert at the beginning of ListBase<LC>::nativeGet be loosened to just assert the shape and the JS_IsNativeFunction() call change from an assert to a check, right?
Attached patch Hacky fix (obsolete) (deleted) — Splinter Review
This seems to fix it by recording a property set with a setProperty op on the prototype.
Comment on attachment 567184 [details] [diff] [review]
Hacky fix

Will this work if someone changes out the proto from under you for some object that has the right things to start and then changes one of them?
[Triage Comment]
Does this bug occur in the wild?
(In reply to Alex Keybl [:akeybl] from comment #13)
> [Triage Comment]
> Does this bug occur in the wild?

I don't yet have enough information to determine whether this should be tracked for FF10. What's the user effect here?

If we're concerned about this bug, let's try to land a low risk fix on m-c and then consider for beta.
Attached patch v1 (obsolete) (deleted) — Splinter Review
Attachment #567184 - Attachment is obsolete: true
Attachment #587435 - Flags: review?(mrbkap)
(In reply to Peter Van der Beken [:peterv] from comment #15)
> Created attachment 587435 [details] [diff] [review]
> v1

peterv - can you explain what the user effect is of this bug?
Attachment #587435 - Flags: review?(mrbkap) → review+
(In reply to Alex Keybl [:akeybl] from comment #16)
> peterv - can you explain what the user effect is of this bug?

If a page attempts to override a function on a list by setting it on the prototype, attempts to call that function through pre-existing instances of that list type will call the original function instead of the new one.
Comment on attachment 587435 [details] [diff] [review]
v1

Actually, testing locally seems to show that this disables the proto cache altogether (since sProtoProperties doesn't know about the class getter).

Also, this only fixes this bug for a single instance of a nodelist.
Attachment #587435 - Flags: review+ → review-
(In reply to Blake Kaplan (:mrbkap) from comment #18)
> Actually, testing locally seems to show that this disables the proto cache
> altogether (since sProtoProperties doesn't know about the class getter).

Yeah, fixed by checking that the setter is |sProtoProperties[n].setter ? sProtoProperties[n].setter : InvalidateProtoShape|.

> Also, this only fixes this bug for a single instance of a nodelist.

I think it does actually, we reset the flag on the proto when we find that all the properties are the right ones. It doesn't matter how many instances we have, either a property isn't the right one and we'll just keep skipping the cache or they all are and we'll use the cache. Right?

There is a problem with multiple prototypes though (like HTMLOptionsCollection->HTMLCollection), the shape check covers both, but we only check the flag on the derived prototype (HTMLOptionsCollection).
(In reply to Blake Kaplan (:mrbkap) from comment #17)
> (In reply to Alex Keybl [:akeybl] from comment #16)
> > peterv - can you explain what the user effect is of this bug?
> 
> If a page attempts to override a function on a list by setting it on the
> prototype, attempts to call that function through pre-existing instances of
> that list type will call the original function instead of the new one.

I appreciate the technical explanation, but this doesn't help me understand whether this should be tracked for 10. What is this a regression from? Was FF9 affected? What would the /user/ impact be?
> Was FF9 affected?

No.  This is a bug in code that was added for Firefox 10.

The obvious user impact would be some sites possibly breaking, if they modify such prototypes.  I don't think there's security impact or crash potential (outside debug builds, of course).
(In reply to Peter Van der Beken [:peterv] from comment #19)
> I think it does actually, we reset the flag on the proto when we find that
> all the properties are the right ones. It doesn't matter how many instances
> we have, either a property isn't the right one and we'll just keep skipping
> the cache or they all are and we'll use the cache. Right?

Oops. You're right. I didn't realize that we wouldn't re-enable the cache until all of the properties had been restored.

> There is a problem with multiple prototypes though (like
> HTMLOptionsCollection->HTMLCollection), the shape check covers both, but we
> only check the flag on the derived prototype (HTMLOptionsCollection).

It seems like shouldCacheProtoShape can check to see if any of the great-prototypes have the flag set?
(In reply to Boris Zbarsky (:bz) from comment #21)
> The obvious user impact would be some sites possibly breaking, if they
> modify such prototypes.  I don't think there's security impact or crash
> potential (outside debug builds, of course).

Given where we are in the cycle, the fact that this hasn't yet been addressed on m-c, and that there's no known website regressions caused by this, we won't track for Firefox 10.
Attached patch v2 (obsolete) (deleted) — Splinter Review
Boris, I'd appreciate it if you could also review the big comment to make sure it's clear enough.
Attachment #587435 - Attachment is obsolete: true
Attachment #589275 - Flags: review?(mrbkap)
Attachment #589275 - Flags: feedback?(bzbarsky)
Comment on attachment 589275 [details] [diff] [review]
v2

I think the comment is fine.

I assume that the Shape* can't be deallocated and reallocated out from under us?

Should the classname be "Object" perhaps?

Is the setProperty hook called when someone defines/redefines a property on the object via the metaobject protocol?  Or do we need to hook at least addProperty too?  For that matter, what about delProperty? In either case, would be good to add tests for this. 

r=me modulo those
Attachment #589275 - Flags: feedback?(bzbarsky) → feedback+
Comment on attachment 589275 [details] [diff] [review]
v2

Review of attachment 589275 [details] [diff] [review]:
-----------------------------------------------------------------

bz was right about the need to observe addProperty since Object.defineProperty won't call the setter but will change the value of a property. delProperty isn't needed since deleting a property changes the shape.

::: js/xpconnect/src/dombindings.cpp
@@ +435,5 @@
> +InvalidateProtoShape(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
> +{
> +    if (JSID_IS_STRING(id)) {
> +        js::SetReservedSlot(obj, 0, PrivateUint32Value(CHECK_CACHE));
> +    }

Nit: I think we avoid braces for single-line if statements now.
Attachment #589275 - Flags: review?(mrbkap) → review+
Attached patch v2.1 (deleted) — Splinter Review
With the addProperty hook.
Attachment #589275 - Attachment is obsolete: true
Attachment #589968 - Flags: review+
Attached patch v2.1 additional patch (deleted) — Splinter Review
This fixes the Dromaeo failures, we need to make sure the setter is called for our prototype objects, not for a DOM proxy object.
Attachment #593096 - Flags: review?(mrbkap)
Comment on attachment 593096 [details] [diff] [review]
v2.1 additional patch

Rookie reviewer mistake on my part. I should have seen this coming.
Attachment #593096 - Flags: review?(mrbkap) → review+
https://hg.mozilla.org/mozilla-central/rev/06ab89fe65ab
Status: NEW → RESOLVED
Closed: 13 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla13
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: