Closed
Bug 552432
Opened 15 years ago
Closed 14 years ago
Determine whether to shadow a property based on slotful *or* has JSPropertyOp setter (given writable if data property)
Categories
(Core :: JavaScript Engine, defect, P2)
Tracking
()
RESOLVED
FIXED
mozilla2.0b10
Tracking | Status | |
---|---|---|
blocking2.0 | --- | final+ |
People
(Reporter: jorendorff, Assigned: brendan)
References
Details
(Whiteboard: fixed-in-tracemonkey)
Attachments
(1 file, 7 obsolete files)
(deleted),
patch
|
jorendorff
:
review+
|
Details | Diff | Splinter Review |
They should behave like data properties.
(function (y) {
arguments.y = 2;
var x = Object.create(arguments);
x[0] = 3;
assertEq(x[0], 3);
assertEq(y, 2);
})(1);
Reporter | ||
Comment 1•15 years ago
|
||
I put the test in ecma_5, since it uses Object.create. But you could create this object in ES3 too, and the behavior should be the same.
Assignee: general → jorendorff
Attachment #432591 -
Flags: review?(jim)
Reporter | ||
Comment 2•15 years ago
|
||
Comment on attachment 432591 [details] [diff] [review]
v1
Withdrawing this. We want properties of the arguments object to remain slotless.
The problem is that slotlessness and accessor-property-ness are represented by the same bit, JSPROP_SHARED.
Here we want an ES5 "data property" without a slot.
Attachment #432591 -
Flags: review?(jim)
Assignee | ||
Comment 3•15 years ago
|
||
(In reply to comment #2)
> (From update of attachment 432591 [details] [diff] [review])
> Withdrawing this. We want properties of the arguments object to remain
> slotless.
>
> The problem is that slotlessness and accessor-property-ness are represented by
> the same bit, JSPROP_SHARED.
>
> Here we want an ES5 "data property" without a slot.
Which would be indistinguishable from ES5 'accessor properties' under the hood, which appear to be 'data properties' to the ES5 and de-facto meta-object query API. So JSPROP_NOSLOT (use 0x80, shared with API-only input flag JSPROP_INDEX and function-flag-not-sprop-attr JSFUN_LAMBDA)?
/be
Assignee | ||
Comment 4•15 years ago
|
||
Morphing this. The goal should be to remove the possibility of having both a slot and getter/setter (whether JSPropertyOp, or JSObject* representation flagged by JSPROP_[GS]ETTER). Then we can union slot with rawGetter or rawSetter (bug 558451 will make JSEmptyScope encapsulate a clasp unioned with the other raw*etter, but in either order there's room to overlay {clasp,slot} with {getter,setter}).
Prototype properties which cannot be shadowed become ES5-conformant in the case of accessor properties. Data properties cannot be unshadowable -- must always be shadowable -- and by definition have a slot (do not have JSPROP_NOSLOT). Built-in JSProperyOp getter/setter pairs act like data properties without slots (we even use a fixed reserved slot under the hood), but therefore should be shadowable. No more JSPROP_SHARED, we can rename and use the same bit. API change, but it's the right thing, and it is time.
Jason, can you sanity-check my words here? And, are you able to work on this or should someone cc'ed feel free to take it?
/be
blocking2.0: --- → ?
Summary: arguments properties should not be JSPROP_SHARED → JSPROP_SHARED should be JSPROP_NOSLOT, and imputed for any getter/setter
Updated•15 years ago
|
blocking2.0: ? → final+
Reporter | ||
Comment 5•15 years ago
|
||
Making a property JSPROP_SHARED has three effects:
1. It makes it slotless.
2. It changes the behavior of assigning to the property via an object
that inherits it from a prototype. In pseudocode:
A = {};
JS_DefineProperty(cx, A, "prop", ..., JSPROP_SHARED);
B = Object.create(A);
B.prop = 0; // N.B. calls the setter instead of shadowing
3. In combination with PERMANENT, the property mostly passes for
an own property.
Comment 4 proposes making behavior #1 depend on whether the property has the default getter/setter or not. I am very much in favor.
Comment 4 further proposes making behavior #2 depend on JSPROP_GETTER and JSPROP_SETTER rather than JSPROP_SHARED. The problem there is that xpc_qsDefineQuickStubs defines properties with JSPropertyOp accessors and JSPROP_SHARED. For those properties behavior #2 is crucial, and we don't want JSPROP_GETTER/SETTER.
Of course we can't dispose of JSPROP_SHARED until we do something about behavior #3 as well. We'll get there.
Assignee | ||
Comment 6•15 years ago
|
||
Indeed, the exact test for (2) is (1):
/* Don't clone a prototype property that doesn't have a slot. */
if (!sprop->hasSlot()) {
in js_SetPropertyHelper, to decide not to shadow.
(3) requires care currently to avoid deoptimizing due to js_GetMutableScope, but with scope elimination it should be no problem.
My hope for JSPROP_GETTER | JSPROP_SETTER sufficing in lieu of JSPROP_NOSLOT or JSPROP_ACCESSOR forgot all about native non-stub JSPropertyOp getters and setters, but ok: we need a bit. We have a bit. We can rename it (+1 on ACCESSOR instead of NOSLOT).
/be
Assignee | ||
Comment 7•15 years ago
|
||
(In reply to comment #6)
> JSPROP_ACCESSOR forgot all about native non-stub JSPropertyOp getters and
> setters, but ok: we need a bit. We have a bit. We can rename it (+1 on ACCESSOR
> instead of NOSLOT).
Or as Jason just suggested on IRC, give up on unioning slot with raw{G,S}etter and test for SPROP_INVALID_SLOT instead of JSPROP_ACCESSOR. Would be nice to free an attr bit, and (pending bug 558451 patch in full) alignment rounding may not free any space in JSScopeProperty if we did union slot with raw{G,S}etter anyway.
/be
Reporter | ||
Updated•15 years ago
|
Assignee: jorendorff → general
Reporter | ||
Comment 8•15 years ago
|
||
Morphing once again to focus on behavior #1 (of the three items listed in
comment 5).
=== New bug description ===
A property should have a slot iff it has a non-default getter.
After that: the properties of the arguments object should not be JSPROP_SHARED (this is the change in patch v1). These two changes together will fix the test case in comment 0 without making those properties slotful.
Summary: JSPROP_SHARED should be JSPROP_NOSLOT, and imputed for any getter/setter → Determine property slotfulness based on getter, not (attrs & JSPROP_SHARED)
Reporter | ||
Comment 9•15 years ago
|
||
> A property should have a slot iff it has a non-default getter.
Oops. That's negated. "iff it has the default getter" is correct.
Rationale for depending on getter, not setter: I think if you use the default getter with a non-default setter, you want a slot. You might be using the setter to validate the assigned value or coerce it to the desired type, or to issue notifications or invalidate caches when it changes; but in any case you want the slot. Otherwise getting the property would always get JSVAL_VOID, which is awfully weird.
Also note that in this case:
var y = Object.create({}, {p: {set: print}});
we do NOT want a slot, even though we implement this as rawGetter == NULL.
Assignee | ||
Comment 10•15 years ago
|
||
(In reply to comment #9)
> Rationale for depending on getter, not setter: I think if you use the default
> getter with a non-default setter, you want a slot. You might be using the
> setter to validate the assigned value or coerce it to the desired type, or to
> issue notifications or invalidate caches when it changes; but in any case you
> want the slot. Otherwise getting the property would always get JSVAL_VOID,
> which is awfully weird.
Yeah -- plus, watchpoints wrap using a cloned property with a JSPropertyOp setter and whatever slot and getter are in the watched prop.
/be
Assignee | ||
Comment 11•15 years ago
|
||
Who will own this nicely focused bug?
/be
Updated•14 years ago
|
Attachment #432591 -
Attachment is obsolete: true
Reporter | ||
Comment 13•14 years ago
|
||
From bug 575997 comment 1:
> I don't think RegExp and E4X need the shared-permanent hack, though: the
> properties in question just need to be slotless and non-configurable. When bug
> 552432 is fixed, these properties should no longer be JSPROP_SHARED.
We should fix that in this bug, and add tests requiring the affected properties to act like data properties. For example:
var x = Object.create(RegExp);
x.$1 = 0;
assertEq(x.hasOwnProperty("$1"), true); // fails
assertEq(x.$1, 0); // fails
Assignee | ||
Updated•14 years ago
|
Assignee: jorendorff → brendan
Assignee | ||
Comment 14•14 years ago
|
||
(In reply to comment #13)
> For example:
>
> var x = Object.create(RegExp);
> x.$1 = 0;
> assertEq(x.hasOwnProperty("$1"), true); // fails
> assertEq(x.$1, 0); // fails
Not the example we want: RegExp.$1 is readonly (de-facto mess here). Comment 1 has a good test.
/be
Assignee | ||
Comment 15•14 years ago
|
||
Should be safe. "What can go wrong?" - Homer S.
/be
Attachment #497407 -
Flags: review?(jorendorff)
Assignee | ||
Comment 16•14 years ago
|
||
This fixes an ancient bug:
js> o = {}
({})
js> p = {q:1}
({q:1})
js> o.__proto__ = p
({q:1})
js> o.__proto__ = null
null
js> o.__proto__
null
js> o.__proto__ = p
({q:1})
js> o
({__proto__:{q:1}})
That's correct as far as __proto__ being magic goes (Rhino agrees). Without this patch, here's what happens:
js> o = {}
({})
js> p = {q:1}
({q:1})
js> o.__proto__ = p
({q:1})
js> o.__proto__ = null
null
js> o.__proto__ = p
({q:1})
js> o
null
js> o.q
js> o.__proto__
({q:1})
The recreated o.__proto__ is a plain old data property.
Jason, can you poke holes in this? Is it really this simple? It makes properties without slots but with JSPropertyOp-typed setters act a lot more like data properties, but shadowing a property with a shortid inherits that shortid, the HAS_SHORTID flag, and the prototype property's getter and setter.
/be
Attachment #497407 -
Attachment is obsolete: true
Attachment #497441 -
Flags: review?(jorendorff)
Attachment #497407 -
Flags: review?(jorendorff)
Assignee | ||
Comment 17•14 years ago
|
||
Comment 1's testcase is wrong:
(function (y) {
arguments.y = 2;
var x = Object.create(arguments);
x[0] = 3;
assertEq(x[0], 3);
assertEq(y, 2); // 2 should be 1 here
})(1);
There are no named properties of arguments objects reflecting same-named formals, only indexed props.
/be
Assignee | ||
Comment 18•14 years ago
|
||
Attachment #497441 -
Attachment is obsolete: true
Attachment #497443 -
Flags: review?(jorendorff)
Attachment #497441 -
Flags: review?(jorendorff)
Assignee | ||
Comment 19•14 years ago
|
||
The comment 16 with-patch results were slightly off, in that o.__proto__ became enumerable once assigned after being nulled. With this new patch:
js> o = {}
({})
js> p = {q:1}
({q:1})
js> o.__proto__ = p
({q:1})
js> o.q
1
js> delete o.__proto__
false
js> o.__proto__
({q:1})
js> o.q
1
js> o.__proto__ = null
null
js> o.q
js> o.__proto__ = p
({q:1})
js> o.q
1
js> o
({})
Note that Shape::shadowable (better name than canShadow, wrong verb given that |this| is the prototype property's shape) does not test JSPROP_SHARED at all if !shape->hasSlot(). We want to get rid of JSPROP_SHARED, and it will take several steps, so we don't want to add new tests of this attribute.
(Getting rid of shortid is a different bargain, not materially altered here.)
Here, the only issue is whether a property-setting operation is trying to shadow a proto-property that has no slot and a JSPropertyOp-type setter. If so, then the property will be shadowed -- and what's more, if the proto-property has a shortid then the related attrs, shortid and HAS_SHORTID flag, getter, and setter will be copied down. If no shortid, then the set creates an enumerable data property.
/be
Attachment #497443 -
Attachment is obsolete: true
Attachment #497458 -
Flags: review?(jorendorff)
Attachment #497443 -
Flags: review?(jorendorff)
Assignee | ||
Comment 20•14 years ago
|
||
This passes the suite as far as I can tell.
/be
Attachment #497458 -
Attachment is obsolete: true
Attachment #497467 -
Flags: review?(jorendorff)
Attachment #497458 -
Flags: review?(jorendorff)
Assignee | ||
Updated•14 years ago
|
Status: NEW → ASSIGNED
Priority: -- → P2
Summary: Determine property slotfulness based on getter, not (attrs & JSPROP_SHARED) → Determine whether to shadow a property based on slotful *or* has JSPropertyOp setter (given writable if data property)
Target Milestone: --- → mozilla2.0
Reporter | ||
Comment 21•14 years ago
|
||
This patch changes the behavior of assignment to an inherited property that has JSPROP_SHARED but not JSPROP_SETTER, is that right? Quick stub properties are like that.
Assignee | ||
Comment 22•14 years ago
|
||
(In reply to comment #21)
> This patch changes the behavior of assignment to an inherited property that has
> JSPROP_SHARED but not JSPROP_SETTER, is that right? Quick stub properties are
> like that.
It does not change the behavior if the proto-property has a shortid -- the shadow inherits attrs, shortid, getter and setter. Do quickstubs have shortids?
/be
Reporter | ||
Comment 23•14 years ago
|
||
(In reply to comment #22)
> (In reply to comment #21)
> > This patch changes the behavior of assignment to an inherited property that has
> > JSPROP_SHARED but not JSPROP_SETTER, is that right? Quick stub properties are
> > like that.
>
> It does not change the behavior if the proto-property has a shortid -- the
> shadow inherits attrs, shortid, getter and setter. Do quickstubs have shortids?
No, they don't.
Reporter | ||
Comment 24•14 years ago
|
||
However, note that even if they did, this would cause such properties to be cloned onto each instance on which they were ever set... which would be a weird change in behavior.
Assignee | ||
Comment 25•14 years ago
|
||
Attachment #497467 -
Attachment is obsolete: true
Attachment #497598 -
Flags: review?(jorendorff)
Attachment #497467 -
Flags: review?(jorendorff)
Assignee | ||
Comment 26•14 years ago
|
||
(In reply to comment #24)
> However, note that even if they did, this would cause such properties to be
> cloned onto each instance on which they were ever set... which would be a weird
> change in behavior.
How is it weird?
It could be too expensive, but that is not clear without data.
/be
Reporter | ||
Comment 27•14 years ago
|
||
Weird is subjective, but here's why I feel this way. These properties have acted like accessor properties w.r.t. assignment for a long time. I think the ultimate target is to make these fully accessor properties--make Object.getOwnPropertyDescriptor return the getter and setter, the whole nine yards. So it seems weird to add an aspect of data property behavior now.
You can observe the difference directly with .hasOwnProperty or Object.getOwnPropertyDescriptor, and you would notice it if you modify the original property on the prototype (objects with clones of the property would not see the change) or if you use a watchpoint.
Assignee | ||
Comment 28•14 years ago
|
||
(In reply to comment #27)
> Weird is subjective, but here's why I feel this way. These properties have
> acted like accessor properties w.r.t. assignment for a long time. I think the
> ultimate target is to make these fully accessor properties--make
> Object.getOwnPropertyDescriptor return the getter and setter, the whole nine
> yards. So it seems weird to add an aspect of data property behavior now.
I hear you, but doesn't this argue for Waldo's JSPROP_ACCESSOR patch, which would be more future-proof? What else can we do?
My understanding of WebIDL and ES5 is that you'd have to use Object.defineProperty to override these proto-based quickstubbed DOM accessors. Can you guys please check me here, since I may be missing something.
> You can observe the difference directly with .hasOwnProperty or
> Object.getOwnPropertyDescriptor, and you would notice it if you modify the
> original property on the prototype (objects with clones of the property would
> not see the change) or if you use a watchpoint.
Yes, for quickstubs it would be observable and it's not future-proof. But short of Waldo's JSPROP_ACCESSOR, I'm not sure what else to do.
For properties such as arguments[0] accessed via a delegating object (as in the testcase based on comment 0), do we have such a future-hostility problem? ES5 models indexed properties of arguments using internal getters and setters but these are not observable. And aren't they shadowable by assignment? If not, this bug needs a new testcase!
/be
Assignee | ||
Comment 29•14 years ago
|
||
Attachment #497598 -
Attachment is obsolete: true
Attachment #497707 -
Flags: review?(jorendorff)
Attachment #497598 -
Flags: review?(jorendorff)
Reporter | ||
Comment 30•14 years ago
|
||
This approach is much better.
I was just thinking: the narrowest way to fix the particular test case at hand would be to make shadowable() return true if the property's setter is exactly ArgSetter.
Assignee | ||
Comment 31•14 years ago
|
||
(In reply to comment #30)
> This approach is much better.
>
> I was just thinking: the narrowest way to fix the particular test case at hand
> would be to make shadowable() return true if the property's setter is exactly
> ArgSetter.
I'd rather burn a bit (can take Waldo's suggestion of using 0x08, which is free, but using JSPROP_INDEX only on the engine's inside seems nicer if we need a real API-facing JSPROP_ new attribute). I suspect we have other cases like arguments elements. I'm just too tired to think of them atm.
/be
Assignee | ||
Comment 32•14 years ago
|
||
Writable shared permanent properties having a JSPropertyOp setter that possibly should be per-instance ("own") data properties, excluding properties of objects that can't be referenced (Call, e.g.):
Array length
arguments elements (fixed by this bug's patch)
strict arguments callee and caller
RegExp lastIndex
RegExp.input, RegExp.multiline statics
Anyone reading, please double-check me if you can.
Most of these can be fixed here using SHADOWABLE.
/be
Reporter | ||
Comment 33•14 years ago
|
||
I can't finish this tonight. Fading. The main thing I still want to look at is whether PolyIC also needs changes.
Reporter | ||
Comment 34•14 years ago
|
||
Comment on attachment 497707 [details] [diff] [review]
more narrowly targeted fix
>+ return hasSlot() || (!hasSetterValue() && (attrs & JSPROP_SHADOWABLE));
This could be
return hasSlot() || (attrs & JSPROP_SHADOWABLE);
Right?
r=me either way.
jstracer doesn't need an update for this since the change is before SetPropHit. However my SetPropHit-ectomy will need a slight adjustment, since in that world the tracer has to distinguish adding assignments from non-adding before the instruction executes.
PolyIC actually disables in these cases (assigning to any shared property with a setter), which seems like it might be slow. Has anyone looked at that?
Attachment #497707 -
Flags: review?(jorendorff) → review+
Assignee | ||
Comment 35•14 years ago
|
||
http://hg.mozilla.org/tracemonkey/rev/df69f4793470
> However my SetPropHit-ectomy will need a slight adjustment, since in that world
> the tracer has to distinguish adding assignments from non-adding before the
> instruction executes.
I just landed, if you rebase the SetPropHit-ectomy I will review it by tomorrow noon. Thanks.
> PolyIC actually disables in these cases (assigning to any shared property with
> a setter), which seems like it might be slow. Has anyone looked at that?
Separate bug, cc'ing bhackett and dvander who may know of one on file, or file, or something (comment about WONTFIX nature of JITting these cases, pending more real-world data?).
/be
Whiteboard: fixed-in-tracemonkey
Assignee | ||
Comment 36•14 years ago
|
||
(In reply to comment #34)
> Comment on attachment 497707 [details] [diff] [review]
> more narrowly targeted fix
>
> >+ return hasSlot() || (!hasSetterValue() && (attrs & JSPROP_SHADOWABLE));
>
> This could be
> return hasSlot() || (attrs & JSPROP_SHADOWABLE);
>
> Right?
Right -- I was playing it too safe. JSPROP_SHADOWABLE is internal-only but it could be useful with function-based accessors as well as with PropertyOp-based properties (as in the patch).
/be
Comment 37•14 years ago
|
||
Status: ASSIGNED → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
Assignee | ||
Comment 38•14 years ago
|
||
(In reply to comment #34)
> jstracer doesn't need an update for this since the change is before SetPropHit.
> However my SetPropHit-ectomy will need a slight adjustment, since in that world
> the tracer has to distinguish adding assignments from non-adding before the
> instruction executes.
Jason, following up here: did your SetPropHit-ectomy make the slight adjustment?
/be
Component: JavaScript Engine → jemalloc
Target Milestone: mozilla2.0 → flash10
Updated•14 years ago
|
Component: jemalloc → JavaScript Engine
Updated•14 years ago
|
Target Milestone: flash10 → mozilla2.0b10
Assignee | ||
Comment 39•14 years ago
|
||
Bugzilla broken? I sweat I didn't touch either of Component or TM.
/be
You need to log in
before you can comment on or make changes to this bug.
Description
•