Ticket #233 (new defect)

Opened 3 years ago

Last modified 2 years ago

(Resolved) Are DontDelete and fixture not the same?

Reported by: lth Assigned to: lth
Type: defect Priority: trivial
Component: Spec Version: 4
Keywords: Cc: graydon,jeffdyer,dherman,brendan

Description (last modified by lth) (diff)

In the white paper I wrote that fixtures are DontDelete (and vice versa). Graydon wrote,

"DontDelete is not the same as fixed; you can have a DontDelete non-fixture.

Fixtures are early-bindable names, present in ribs. D!ontDeletes are consulted in the same (secondary, late, dynamic) name-lookup pass as the rest."

I don't know why this should be the case; for it to make sense there must be things that are DontDelete but not a fixture. Does anyone know such a beast?

Attachments

Change History

Changed 3 years ago by lth

  • description changed from In the white paper I wrote that fixtures are DontDelete (and vice versa). Graydon wrote, "DontDelete is not the same as fixed; you can have a DontDelete non-fixture. Fixtures are early-bindable names, present in ribs. DontDeletes are consulted in the same (secondary, late, dynamic) name-lookup pass as the rest." I don't know why this should be the case; for it to make sense there must be things that are DontDelete but not a fixture. Does anyone know such a beast? to In the white paper I wrote that fixtures are !DontDelete (and vice versa). Graydon wrote, "!DontDelete is not the same as fixed; you can have a !DontDelete non-fixture. Fixtures are early-bindable names, present in ribs. D!ontDeletes are consulted in the same (secondary, late, dynamic) name-lookup pass as the rest." I don't know why this should be the case; for it to make sense there must be things that are !DontDelete but not a fixture. Does anyone know such a beast?

Changed 3 years ago by brendan

ES1-3 specify DontDelete? for properties such as f.prototype given function f. Do we say that f.prototype is a fixture? Sure, why not -- if it can't be deleted, how could you detect that it was not a "fixed property"?

The two-pass lookup must be per object, not across the prototype chain, so there's no way to shadow a DontDelete? non-fixture on a nearer object with a fixture on a farther object.

So if there's no difference, let's not multiply beyond necessity here, and get that clean, close Occam Mach-VII with battery-powered tingler action shave ;-).

/be

Changed 3 years ago by brendan

If we have two-pass lookup, first for fixtures in ribs, then (if not found) for unfixed properties along the prototype chain, we could still map fixtures onto DontDelete? props *provided* all fixtures come before same-named non-fixtures along the prototype chain. Is that the case? It is for class instance vars and methods.

Graydon, if the RI makes an object with a fixture named (say) intrinsic::toString the prototype of another object of dynamic class (say Object) with a direct (own, expando) property toString, and you call the latter object o, what does o.intrinsic::toString mean?

/be

Changed 3 years ago by brendan

But of course, you can't shadow a fixture, so fixtures come before expandos (unless the proto chain link is mutable, a very bad idea!):

>> function f(){}
>> f.prototype = new Date
Wed, 31 Oct 2007 16:20:42 GMT+0000
>> o = new f
[object Object]
>> o.intrinsic::getFullYear = 42
[locn] builtins/Error.es:86:55-86.55
[stack] []
**ERROR** EvalError: uncaught exception: TypeError: setValue on read-only method property: [ns intrinsic]::getFullYear  (near builtins/Error.es:86:55-86.55)
>> o.intrinsic::getFullYear()
2007

This would be good to formalize as simply as possible in specs and overviews and the like ;-).

/be

Changed 3 years ago by lth

The two-pass lookup algorithm sounds like it might not be what we want. Consider this code:

   var x = { const toString: 37 }
   {
      use namespace intrinsic
      x.toString
   }

Suppose the new toString is DD, RO but on the dynamic property list. (It must be DD otherwise RO isn't worth anything.) Will the two-pass lookup algorithm find intrinsic::toString? If so, DD != fixture.

On the other hand, if "const" in a literal creates a fixture (which one can legitimately argue, I think, because clearly x above could be said to be an instance of the type {toString:*}) then the lookup x.toString is ambiguous.

Changed 3 years ago by brendan

My example did not demonstrate the issue, sorry. Here's the example:

class C { var x; } function f() {} f.prototype = new C

[object C]

o = new f

[object Object]

f.prototype.x = 42

42

o.x

42

o.x = 44

44

o.x

44

This shows a plain old deletable property shadowing a fixture. However, does the first pass lookup in the RI walk up the prototype chain from o, looking only for fixtures? If so, it should have found 42. It found 44, so something else is going on. We need to understand the two-pass lookup in the RI.

/be

Changed 3 years ago by brendan

Graydon pointed out the obvious missing step from the RI session:

f.prototype.x

44

(I like the red-green citing, so I'll not wrap with ...) here.

So once again, DD and fixtures seem to be indistinguishable. Graydon asks, however, if this non-shadowing is the right thing. I think so, and I recall us discussing this already, a while ago.

/be

Changed 3 years ago by graydon

For me, the key here is to prove to ourselves that every possible way of arriving at the existence of an object x with a dynamic, non-DD property y requires that:

  • x has no DD property y
  • x has no prototype with a DD property y

If we think we can preserve these conditions in every possible operation, I think we're ok. A crucial operation is assignment. Suppose we're doing x.y = foo:

  • If x has any local prop y, the assignment resolves to it and no prop is made.
  • If x has a proto p with local prop y, then:
    • if p.y is RO, we do nothing silently (old ES3 behavior)
    • if p.y is DD, we need to resolve to p.y and assign to it, not shadow. No new prop.
    • if p.y is non-DD, non-RO, it is dynamic as well. By assumption (hopefully) this means that it has no DD slots higher up on the chain that it's shadowing. So shadowing is ok here, we create a prop y on x, assigning to it.

Does this explanation reflect your shared understanding? If so, can we enumerate more ways of possibly creating props or injecting them into the prototype chain? Brendan and I discussed briefly and determined that it is strictly necessary for proto chains to be immutable once created, for this to work. Thus __proto__ = foo as in spidermonkey -- thankfully not part of spec behavior -- will have to be broken.

Changed 3 years ago by brendan

That matches my understanding, and yeah, o.__proto__ = p is right out.

The proof is inductive:

  • o.x = y creates a property named 'x' in o iff there is no RO or DD x in o.__proto__
  • The only way to set o.__proto__ is by function f(){}; f.prototype = p; o = new f.
  • Therefore if there is no RO or DD 'x' in o.__proto__, then by the above two steps there can be no 'x' in any object on o's prototype chain.

/be

Changed 3 years ago by graydon

Oh. I think I may have stumbled on the old reason for modeling DD and Fixed separately: the RI presently runs two passes of the entire multiname-resolution algorithm, first seeing only fixtures, and second seeing all props.

This means that the first pass of multiname resolution -- the one on fixtures alone -- always has a chance to produce a unique name, and if it succeeds the other props have no ability to influence multiname resolution.

Adding a dynamic non-DD property must not be able to cause multiname resolution to become ambiguous (and thereby fail) at runtime. This is a crucial behavior to preserve if we're to make optional early binding possible; early binding doesn't just resolve a name to a slot, it also resolves a multiname to a name in the first place. So "non-shadowing" isn't the only thing we need to prove. We'd also need to prove "non-interference in the multiname algorithm".

We might be able to do that if we phrase each step of multiname resolution as a probe for both DD and non-DD props, but one that treats any mixture of DD/non-DD as containing only its DD constituents. Then it would report ambiguity if there were multiple DD candidates, or no DD candidates and multiple non-DD candidates, but not if there were a unique DD candidate (and any number of non-DD).

A little weird, but perhaps semantically equivalent. What do others think?

Changed 2 years ago by lth

  • priority changed from major to trivial
  • summary changed from Are DontDelete and fixture not the same? to (Resolved) Are DontDelete and fixture not the same?

Fixture and DD are not the same (resolved in discussion between Jeff, Lars, Brendan). Dynamic properties have the attributes DD, DE, RO; fixture properties have only RO. Forthcoming details in the enumerability spec.

Note: See TracTickets for help on using tickets.