[Harmony Proxies] Proposal: Property fixing

classic Classic list List threaded Threaded
140 messages Options
1 ... 4567
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
On Fri, Jun 17, 2011 at 4:53 AM, David Bruant <[hidden email]> wrote:
Le 17/06/2011 02:38, Mark S. Miller a écrit :
Another useful line of question is: Why are these invariants in the spec? This is useful to discuss, but is a separate matter.
This is what I was asking, sorry for the confusion. [...] Why each of these 5 bullets points is in the spec?


Hi David, thanks for asking. I think of them as seven points, divided into the first four and the last three. The last two have no bullets on them in the text but are the two paragraphs following the bulleted list at the end of 8.6.2. Renumbering, so that we can refer back to them:

1) Universal property constraints:

  a) If a property is described as a data property and it may return different values over time, then either or both of the [[Writable]] and [[Configurable]] attributes must be true even if no mechanism to change the value is exposed via the other internal methods. 

  b) If a property is described as a data property and its [[Writable]] and [[Configurable]] are both false, then the SameValue (according to 9.12) must be returned for the [[Value]] attribute of the property on all calls to [[GetOwnProperty]]. 

  c) If the attributes other than [[Writable]] may change over time or if the property might disappear, then the [[Configurable]] attribute must be true. [The "disappear" clause here is also partially a universal object constraint, and so is relevant to the next list. Alternatively, we could consider non-existence to be a state of a property.]

  d) If the [[Writable]] attribute may change from false to true, then the [[Configurable]] attribute must be true. 


2) Universal object constraints:

  a) If the value of the host object‘s [[Extensible]] internal property has been observed by ECMAScript code to be false, then if a call to [[GetOwnProperty]] describes a property as non-existent all subsequent calls must also describe that property as non-existent. 

  b) The [[DefineOwnProperty]] internal method [...] must not permit the addition of a new property to a [...] object if the [[Extensible]] internal property of that [...] object has been observed by ECMAScript code to be false.  

  c) If the [[Extensible]] internal property of that [...] object has been observed by ECMAScript code to be false then it must not subsequently become true. 

    ["host" replaced with [...] above for the following reasons]

Although the ES5.1 text describes these only as constraints on non-native (i.e., "host") objects, I label these as "universal" above, since the detailed behavior that ES5.1 specifies for all native objects, both normal and abnormal, all imply these constraints. Put another way, if any object O on system X violates these constraints, then system X is not a conformant ES5.1 system. Either O is native, in which case it violates some specific specified native behavior spec, or O is non-native, in which case it directly violates the above text from 8.6.2.

 
What is the rational behind each of these?

One of JavaScript's great virtues is that it is a highly reflective language, leading programmers and framework builders to engage in a wide range of meta-programming patterns. One of JavaScript's great flaws is that its property state space is complex, leading such meta-programmers into a case explosion. Prior to ES5, there were so few guarantees of any stability properties over this state space, especially for non-native ("host") objects, that robust programming was almost impossible. 

For example, Caja on ES3, to uphold its own invariants, must handle non-native objects very carefully. Given the unspecified nature of native objects, we cannot both handle these objects and maintain our security, while depending only on specified behavior. To figure out how to handle them safely enough, we rely on lore and testing as well, relying on observed regularities not guaranteed by any spec. To diminish the cases we need to worry about, Caja pays substantial costs to ensure that untrusted code never gets a direct reference to any non-native object, not even "alert". This ice is too thin.

How does ES5.1 help make robust meta-programming practical? The following text from Chapter 4 of <http://erights.org/talks/thesis/> seems relevant:

In the human world, when you plan for yourself, you make assumptions about future situations in which your plan will unfold. Occasionally, someone else’s plan may interfere with yours, invalidating the assumptions on which your plan is based. To plan successfully, you need some sense of which assumptions are usually safe from such disruption. But you do not need to anticipate every possible contingency. If someone does something you did not expect, you will probably be better able to figure out how to cope at that time anyway. 
 
When programmers write programs, they express plans for machines to execute. To formulate such plans, programmers must also make assumptions. When separately formulated plans are composed, conflicting assumptions can cause the run-time situation to become inconsistent with a given plan’s assumptions, corrupting its continued execution. Such corrupted executions likely violate assumptions on which other programs depend, potentially spreading corruption throughout a system. To program successfully, programmers use abstraction and modularity mechanisms to limit (usually implicitly) which assumptions must be made, and to structure these assumptions so they are more likely to mesh without conflict. Beyond these assumptions, correct programs must handle all remaining relevant contingencies. The case analysis burden this requires must be kept reasonable, or robust programming becomes impractical. 
[emphasis added] 


In ES5.1, across normal native, abnormal native, and non-native, we reliably contain the property state space to <http://wiki.ecmascript.org/doku.php?id=es3.1:attribute_states>. Only the transition labels in this diagram are specific to normal native. If we erase only these transition labels, keeping the state labels and everything else, then this diagram is universal and directly reflects the "Universal property constraints" from 8.6.2. Open problem: What are the non-equivalences if any between this diagram and the universal property constraints? How are these constraints either stronger or weaker than the diagram? If there are substantial non-equivalences, then we should revisit this.

Once we erase the transition labels, then we can without further loss of info also erase the blue transition arc currently labeled "put" and keep only the two transitions currently labeled "defineProperty". Even this state space is distressingly large and hard to think about. But we've learned to deal with it. Let's not make it any worse. 


--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
Le 17/06/2011 18:45, Mark S. Miller a écrit :
 
What is the rational behind each of these?

One of JavaScript's great virtues is that it is a highly reflective language, leading programmers and framework builders to engage in a wide range of meta-programming patterns. One of JavaScript's great flaws is that its property state space is complex, leading such meta-programmers into a case explosion. Prior to ES5, there were so few guarantees of any stability properties over this state space, especially for non-native ("host") objects, that robust programming was almost impossible. 

For example, Caja on ES3, to uphold its own invariants, must handle non-native objects very carefully. (...). To diminish the cases we need to worry about, Caja pays substantial costs to ensure that untrusted code never gets a direct reference to any non-native object, not even "alert". This ice is too thin.
It may be a naive question, but why should untrusted code be prevented from getting a reference to a non-native object? Do you have a concrete example of threat this protects from?
... well... if untrusted code has access to "alert", it can do "while(1) alert();" and prevent trusted code from running. But all other non-native objects?

How did each of the 4 "Universal property constraints" made Caja's work easier?


For NodeLists proxy emulation, the "length" property cannot be changed manually (as far as I know), so I'd say that its writable has to be false (tell me if I'm wrong to think that). The "length" value can however change due to DOM tree transformations. For this reason, configurable has to be true as per Universal property constraints 1) a). However you cannot really configure it in the sense that you can't neither delete it nor re-configure it. "configurable" does not really mean that the property is configurable (not quote) on host objects.

I'm just puzzled by the wording I think. "configurable:true" on normal native objects means "this property is configurable (delete + Object.defineProperty with any property descriptor)".
However, for abnormal+non-native objects, the semantics of "configurable" is constrainted to the Universal property constraints, but does not give any insight on what you can do with the property. Exact semantics (what you can do with the property) is left at the discretion of the object implementor.
The confusing part for me is probably the "-able" suffix which sounds like giving me an information on what I can do with the property (like "enumerable" do), but this is actually not the case at all for abnormal+non-native objects. It's not that big of a deal for abnormal native objects since I can read the spec to know what to expect from these.


On a last note, array.length already do respect all Universal property constraints since writable is true. Or am I missing something?

Thanks for your explanations,

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
In reply to this post by Tom Van Cutsem-3
Le 17/06/2011 16:51, Tom Van Cutsem a écrit :
2011/6/17 David Bruant <[hidden email]>
Le 17/06/2011 08:31, Brendan Eich a écrit :
On Jun 16, 2011, at 10:12 PM, Mark S. Miller wrote:
I agree. Sounds like a proxy. Glad we worked out the configurability issues.

Assuming Tom's fixed properties proposal (the latest) is approvied -- right?
I think that there are still pending issues.
One is figuring out why the ES5 invariants on abnormal+non-native object properties configurability are in the spec. If we assume they're legitimate, then we're pretty much done (not 100%, see below)
As far as I'm concerned, I haven't read arguments enforcing a belief that these invariants on abnormal+non-native objects really have a value and why all implementations should follow them (Once again, they are in the spec, so they should be respected by implementors. But should they be in the spec? What is the added value of these 5 invariants? Why no other invariant?)

For the moment, apparently, two cases are problematic:
- A forwarding proxy cannot fully forward, because it cannot honestly tell whether a property in the target object is configurable or not (it will always pretend it's configurable). Since the forwarding proxy pattern is probably the most important, this seems to be a major issue.

I agree there doesn't seem to be a generic solution. However, strawman:fixed_properties does make it possible for forwarding proxies to deal with certain cases:
- if target has a non-configurable accessor property, the proxy can create a fixed non-configurable accessor whose get/set attributes invoke handler.get/set, as shown on the strawman page.
Actually, for this very case, you could take the getter and setter of the target property and use them for the fixed proxy property (since they won't change on the target). It would solve the asymetry that comes from calling handler.get/set when the non-configurable proxy property is an accessor and not calling these traps when it's a data property.

- if target has a non-writable, non-configurable data property (typically a property just acting as a constant), the proxy can create its own fixed copy of that property. IINM, non-writable non-configurable properties can't change, so the copies cannot grow out of sync.
No issue with constants, indeed.

- if target has a writable, non-configurable data property, the proxy could create a fixed copy. In its defineProperty trap, it has to make sure to forward the defineProperty operation to the target, in case its writable property is switched from true to false. The one thing the proxy cannot intercept are changes made to the original property on its |target|. I.e. the proxy may still advertise its fixed property as {writable:true, configurable:false} while the target's property may have been changed to {writable:false,configurable:false} by some other code.
Symetrically to the forwarding of defineProperty, maybe that getOwnPropertyDescriptor could be forwarded too (requires to call the getOwnPropertyDescriptor trap for non-configurable properties) and the return value could be used to redefine writable on the proxy fixed property if necessary.
-----
var t = Object.create(null, {a:{value:1, configurable:false, writable:true}});
var p = ForwardingProxy(t);
// p has a fixed property 'a'.
Object.getOwnPropertyDescriptor(p, 'a');
// {value:1, configurable:false, writable:true, enumerable:false}

Object.defineProperty(t, 'a', {value:1, configurable:false, writable:false});

Object.getOwnPropertyDescriptor(p, 'a');
// Currently unchanged as you said: {value:1, configurable:false, writable:true, enumerable:false}
// because the trap is not called.
// My suggestion is that the getOwnPropertyDescriptor trap should be called
// In that case, it would forward to the target and return the correct target property descriptor
// {value:1, configurable:false, writable:false, enumerable:false}
// Since the pd cannot be returned right away without risking to violate some invariants,
// the engine should check whether the returned pd can replace the current pd
// without violating an invariant.
// In that case, changing writable from false to true of a non-configurable property is legitimate.
// The engine changes the fixed property descriptor with the returned one (reject if the wanted
// change violates an invariant) and actually return to the program this invariant.
-----


Also, trying to create a non-configurable property on the target will not be possible (unless using custom property descriptor attribute? :-s), because with the fixed property proposal, such a property get stuck at the proxy level. No trap is called, no forwarding.

My latest addition to the proposal would allow proxy handlers to still intercept defineProperty, even on fixed properties, so they could propagate changes to fixed properties to their target.
That's what I was missing. Propagating getOwnPropertyDescriptor is the symetric idea.



 
- Issue with a proxy saying that an inherited property is a non-configurable property (getPropertyDescriptor trap). Basically, getPropertyDescriptor may say that a property is configurable while getPrototypeOf+getOwnPropertyDescriptor(on a native object which allows non-configurable property) may say that the property is not-configurable. They would both talk about the same property and be inconsistent.

Yes, this is a thorny issue. We could drop the getPropertyDescriptor operation & trap. What we lose by that is that proxies would be unable to fully emulate inherited properties. That is: while a proxy's get/set traps may still emulate "foo" as an inherited property (i.e. proxy.foo returns a value, but Object.getOwnPropertyDescriptor(proxy,'foo') returns undefined), the illusion would be broken once the proxy's inheritance chain is inspected (i.e. code may find out that there's actually no "foo" property on the proxy's prototype chain). Actually, one could argue that even without getPropertyDescriptor proxies already cannot emulate prototype inheritance, since they can't virtualize Object.getPrototypeOf.
For the getPropertyDescriptor trap, if the returned descriptor has "configurable:false", the engine could climb the prototype chain to check that "configurable:false" is actually legitimate (only configurability). May be costly?



To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
- All other property-specific traps have the default derived trap behavior (all the remaining property-specific traps are derived).

Does that sounds right?

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
Le 17/06/2011 21:41, David Bruant a écrit :

To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
...wait a minute. There is no invariant imposing this. There is an invariant asking to a property that might /disappear/ to show "configurable:true" but it does not prevent from manually deleting a property with "configurable:false", does it?
I agree that this is the expectation we have from a native object, but I am not sure this is enforced by the spec on abnormal native or non-native (host) objects.

Regardless of the very delete trap issue, the more I think about it, the more I realize that we're slightly moving to a position where we put invariant enforcement code: the getOwnPropertyDescriptor trap imposes "configurable:true", unless it has been noticed that this very property has been noticed (or set) with "configurable:false" before, in which case, check against previously returned/set descriptor, etc.
Would it cost that much more to just call traps and put the invariant enforcement code at the relevant places instead of the bicephal property fixing proposal?

Also, I think that there might be missing invariants like:
- if for a property, [[Configurable]] has been set to false or observed as false, it must stay like so (otherwise, all other invariants relying on "configurable:false" are just pointless)
- The first observed value of [[Prototype]] of an object must remain throughout the program lifetime
- etc.

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2


On Fri, Jun 17, 2011 at 1:50 PM, David Bruant <[hidden email]> wrote:
Le 17/06/2011 21:41, David Bruant a écrit :

To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
...wait a minute. There is no invariant imposing this. There is an invariant asking to a property that might /disappear/ to show "configurable:true" but it does not prevent from manually deleting a property with "configurable:false", does it?

If the delete succeeds, then the property disappears, so yes, this would be prohibited.

 
I agree that this is the expectation we have from a native object, but I am not sure this is enforced by the spec on abnormal native or non-native (host) objects.

I'm not sure that the delete operation must throw, though I hope it must. But it cannot result in a non-configurable property disappearing.

 

Regardless of the very delete trap issue, the more I think about it, the more I realize that we're slightly moving to a position where we put invariant enforcement code: the getOwnPropertyDescriptor trap imposes "configurable:true", unless it has been noticed that this very property has been noticed (or set) with "configurable:false" before, in which case, check against previously returned/set descriptor, etc.
Would it cost that much more to just call traps and put the invariant enforcement code at the relevant places instead of the bicephal property fixing proposal?

I think the loophole idea suggests this as well.


 

Also, I think that there might be missing invariants like:
- if for a property, [[Configurable]] has been set to false or observed as false, it must stay like so (otherwise, all other invariants relying on "configurable:false" are just pointless)

Oops. You're right, this is missing. It is a spec bug and needs to be added to the errata.


 
- The first observed value of [[Prototype]] of an object must remain throughout the program lifetime

For ES5.1, we explicitly do not prohibit changes to the [[Prototype]] on extensible objects. Neither do we provide any way to change it, but implementations that allow such changes do not thereby violate ES5.1. I believe we should prohibit this in ES-next, but I cannot say we yet have consensus on that.

We do prohibit changes to [[Prototype]] on frozen objects. I think we prohibit such changes on all non-extensible objects, but no longer remember for sure.

 
- etc.

David



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Allen Wirfs-Brock
In reply to this post by seaneagan1
Mark,
I don't want to argue about the specific invarients you listed.  They generally make sense.  However I don't believe that they are as firmly embedded in the fabric of ES as your believe.

Prior, to ES5 attribute value were not reified and there were no way for ES code to specify attribute values for properties.  I believe (I'm where where I can't check) that [[DefineOwnProperty]] didn't exist prior to ES5.  Argurably Host objects were not required to even have property attributes.  They simply needed to provide [[PUT/Get]] behaviors and there were minimal constraints on what they implemented. Some widely used host objest (DOM NodeList) have behaviors that are tough to match to the normal interpretation of the attributes.

In ES5 we added [[DefineOwnProperty]] as an internal method and incorporated into it the attribute transtion rules you quoted.  However as an internal method it can have other "native" implementations that implement other rules.  I don't believe there is any contrary spec. language.  We did put in the host object invarients but I don't believe we actually talked about expanding there applicability to all objects.

I'm not saying it is necessarily a bad idea.  I just don't think it is implicit in ES5.

Allen





_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
On Fri, Jun 17, 2011 at 4:52 PM, Allen Wirfs-Brock <[hidden email]> wrote:
Mark,
I don't want to argue about the specific invarients you listed.  They generally make sense.  However I don't believe that they are as firmly embedded in the fabric of ES as your believe.

Prior, to ES5 [...bunch of stuff about ES3...]

Hi Allen,

It sounds like you think I'm making a point about ES that spans at least ES3 and ES5. I'm not. I'm making a point only about ES5. ES3 was a mess and we did an extraordinary job, starting under the ES3.1 banner and continuing under the ES5 banner, of cleaning up that mess. So if we wish to speak historically, my point spans ES3.1 to ES5.1.

 

In ES5 we added [[DefineOwnProperty]] as an internal method and incorporated into it the attribute transtion rules you quoted.  However as an internal method it can have other "native" implementations that implement other rules.  I don't believe there is any contrary spec. language.

ES5.1 states the specification of all normal and abnormal native objects. I didn't feel we needed spec language to state the invariants that all these specific native specifications upheld, since they did uphold them.

 
 We did put in the host object invarients but I don't believe we actually talked about expanding there applicability to all objects.

I'm not saying it is necessarily a bad idea.  I just don't think it is implicit in ES5.

It is because I was careful to ensure, starting with our ES3.1 conversations, that all these specific native specs upheld these invariants. I certainly admit that what these invariants were, and why they were important, are all more clear to me now than they were then. I was discovering the invariants that mattered by intuition while we were doing the ES3.1 design. 

We actually did better than I expected. In July 2009 when we wrote "Support a statically verifiable, object-capability secure subset." into the Harmony Goals at <http://wiki.ecmascript.org/doku.php?id=harmony:harmony#goals>, we had already achieved this goal in ES5 and I didn't know it. I had been groping towards this goal during the ES5 design hoping to make incremental progress, but I didn't know we had crossed the finish line until December 2009 when I wrote the first SES prototype at <http://code.google.com/p/es-lab/source/detail?r=3&path=/trunk/src/ses/initSES.js>. Current development has moved to <http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/>. 

(David, the following should also serve as a partial answer to the question of your's that I postponed)

As an example where I'm already exploiting these invariants: At <http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#614> you will find


/**
   * Read the current value of base[name], and freeze that property as
   * a data property to ensure that all further reads of that same
   * property from that base produce the same value.
   *
   * <p>The algorithms in startSES traverse the graph of primordials
   * multiple times. These algorithms rely on all these traversals
   * seeing the same graph. By freezing these as data properties the
   * first time they are read, we ensure that all traversals see the
   * same graph.
   *
   * <p>The frozen property should preserve the enumerability of the
   * original property.
   */
  function read(base, name) {
    var desc = Object.getOwnPropertyDescriptor(base, name);
    if (desc && 'value' in desc && !desc.writable && !desc.configurable) {
      return desc.value;
    }

    var result = base[name];
    try {
      Object.defineProperty(base, name, {
        value: result, writable: false, configurable: false
      });
    } catch (ex) {
      cantNeuter.push({base: base, name: name, err: ex});
    }
    return result;
  }





--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
[again with better formatting of the code. The previous one also looked good when I sent it. If this one arrives looking crappy, please just follow the link to the repository site. Email mangling of html befuddles me.]


On Fri, Jun 17, 2011 at 6:00 PM, Mark S. Miller <[hidden email]> wrote:
On Fri, Jun 17, 2011 at 4:52 PM, Allen Wirfs-Brock <[hidden email]> wrote:
Mark,
I don't want to argue about the specific invarients you listed.  They generally make sense.  However I don't believe that they are as firmly embedded in the fabric of ES as your believe.

Prior, to ES5 [...bunch of stuff about ES3...]

Hi Allen,

It sounds like you think I'm making a point about ES that spans at least ES3 and ES5. I'm not. I'm making a point only about ES5. ES3 was a mess and we did an extraordinary job, starting under the ES3.1 banner and continuing under the ES5 banner, of cleaning up that mess. So if we wish to speak historically, my point spans ES3.1 to ES5.1.

 

In ES5 we added [[DefineOwnProperty]] as an internal method and incorporated into it the attribute transtion rules you quoted.  However as an internal method it can have other "native" implementations that implement other rules.  I don't believe there is any contrary spec. language.

ES5.1 states the specification of all normal and abnormal native objects. I didn't feel we needed spec language to state the invariants that all these specific native specifications upheld, since they did uphold them.

 
 We did put in the host object invarients but I don't believe we actually talked about expanding there applicability to all objects.

I'm not saying it is necessarily a bad idea.  I just don't think it is implicit in ES5.

It is because I was careful to ensure, starting with our ES3.1 conversations, that all these specific native specs upheld these invariants. I certainly admit that what these invariants were, and why they were important, are all more clear to me now than they were then. I was discovering the invariants that mattered by intuition while we were doing the ES3.1 design. 

We actually did better than I expected. In July 2009 when we wrote "Support a statically verifiable, object-capability secure subset." into the Harmony Goals at <http://wiki.ecmascript.org/doku.php?id=harmony:harmony#goals>, we had already achieved this goal in ES5 and I didn't know it. I had been groping towards this goal during the ES5 design hoping to make incremental progress, but I didn't know we had crossed the finish line until December 2009 when I wrote the first SES prototype at <http://code.google.com/p/es-lab/source/detail?r=3&path=/trunk/src/ses/initSES.js>. Current development has moved to <http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/>. 

(David, the following should also serve as a partial answer to the question of your's that I postponed)

As an example where I'm already exploiting these invariants: At <http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#614> you will find


/**
   * Read the current value of base[name], and freeze that property as
   * a data property to ensure that all further reads of that same
   * property from that base produce the same value.
   *
   * <p>The algorithms in startSES traverse the graph of primordials
   * multiple times. These algorithms rely on all these traversals
   * seeing the same graph. By freezing these as data properties the
   * first time they are read, we ensure that all traversals see the
   * same graph.
   *
   * <p>The frozen property should preserve the enumerability of the
   * original property.
   */
  function read(base, name) {
    var desc = Object.getOwnPropertyDescriptor(base, name);
    if (desc && 'value' in desc && !desc.writable && !desc.configurable) {
      return desc.value;
    }
    var result = base[name];
    try {
      Object.defineProperty(base, name, {
        value: result, writable: false, configurable: false
      });
    } catch (ex) {
      cantNeuter.push({base: base, name: name, err: ex});
    }
    return result;
  }





--
    Cheers,
    --MarkM



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
On Fri, Jun 17, 2011 at 6:03 PM, Mark S. Miller <[hidden email]> wrote:
[again with better formatting of the code. The previous one also looked good when I sent it. If this one arrives looking crappy, please just follow the link to the repository site. Email mangling of html befuddles me.]


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
In reply to this post by Allen Wirfs-Brock
An even better example where Tom and I used these invariants, even if only for an illustrative example, is <http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_eventual_reference_proxy>. The old text at the bottom of this old code says: 

    "Note: localFarReferenceMaker should work even if obj is a host object." 

Were these invariants relaxed, abstractions such as this would either be incorrect or be much less precise and therefore less useful.


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
Le 18/06/2011 05:35, Mark S. Miller a écrit :
An even better example where Tom and I used these invariants, even if only for an illustrative example, is <http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_eventual_reference_proxy>. The old text at the bottom of this old code says: 

    "Note: localFarReferenceMaker should work even if obj is a host object."
A couple of things on this code:
* var pds = Object.getProperties(obj);
=> strawman:extended_object_api suggests the name "Object.getPropertyDescriptors"

* var nonConfigurableOwnProperties =
      Object.freeze(nonConfigurableProperties.filter(function(name) { return !!({}).getOwnPropertyDescriptor.call(obj, name); });
=> I think the filter should be ({}).hasOwnProperty.call(obj, name); (no need for !! since hasOwnProperty already returns a boolean)

* isEnumerableOwn
=> This is not a trap (anymore?)

* In "enumerate" and "keys" traps, you do not take "enumerable" into account, is it on purpose?

* This abstraction does not take into account whether an property of obj has later been set to "configurable:false". (But I don't know purpose of an eventual reference, so it might be on purpose)

* There are no defineProperty, getPropertyNames or getOwnPropertyDescriptor traps (which are fundamental traps). Is it on purpose?

Were these invariants relaxed, abstractions such as this would either be incorrect or be much less precise and therefore less useful.
What is the purpose of this abstration? How is it used?

On a slightly unrelated note, you use setTimeout(f,0) several times. I'd like to point out that Malte Ulb very recently showed (http://jsperf.com/postmessage) that setTimeout(f,0) is not very good performance-wise and that posting a message to oneself (window.postmessage) is far more efficient.
This is neither part of ECMAScript, nor supported by all browsers (should be by IE8, but the test doesn't work for a reason I have tried to find and given up on), but in relevant contexts, i'll stop using setTimeout(f,0).

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
Hi David, yes, that code is just a stale illustrative example. This abstraction itself is not being used, and the code predates many changes to the proxy API that render it invalid. And even then, it was sloppy in ways you point out, e.g, regarding enumerability.

However, it is a good example of the kinds of abstraction that one could write using the modern proxy API, *if* we don't weaken the invariants it relies on. And again, the "read" function I pointed to is a production example which also relies on these invariants.


On Sat, Jun 18, 2011 at 3:01 PM, David Bruant <[hidden email]> wrote:
Le 18/06/2011 05:35, Mark S. Miller a écrit :
An even better example where Tom and I used these invariants, even if only for an illustrative example, is <http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_eventual_reference_proxy>. The old text at the bottom of this old code says: 

    "Note: localFarReferenceMaker should work even if obj is a host object."
A couple of things on this code:
* var pds = Object.getProperties(obj);
=> strawman:extended_object_api suggests the name "Object.getPropertyDescriptors"

* var nonConfigurableOwnProperties =
      Object.freeze(nonConfigurableProperties.filter(function(name) { return !!({}).getOwnPropertyDescriptor.call(obj, name); });
=> I think the filter should be ({}).hasOwnProperty.call(obj, name); (no need for !! since hasOwnProperty already returns a boolean)

* isEnumerableOwn
=> This is not a trap (anymore?)

* In "enumerate" and "keys" traps, you do not take "enumerable" into account, is it on purpose?

* This abstraction does not take into account whether an property of obj has later been set to "configurable:false". (But I don't know purpose of an eventual reference, so it might be on purpose)

* There are no defineProperty, getPropertyNames or getOwnPropertyDescriptor traps (which are fundamental traps). Is it on purpose?


Were these invariants relaxed, abstractions such as this would either be incorrect or be much less precise and therefore less useful.
What is the purpose of this abstration? How is it used?

On a slightly unrelated note, you use setTimeout(f,0) several times. I'd like to point out that Malte Ulb very recently showed (http://jsperf.com/postmessage) that setTimeout(f,0) is not very good performance-wise and that posting a message to oneself (window.postmessage) is far more efficient.
This is neither part of ECMAScript, nor supported by all browsers (should be by IE8, but the test doesn't work for a reason I have tried to find and given up on), but in relevant contexts, i'll stop using setTimeout(f,0).

David



--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
Le 19/06/2011 00:30, Mark S. Miller a écrit :
> Hi David, yes, that code is just a stale illustrative example. This
> abstraction itself is not being used, and the code predates many
> changes to the proxy API that render it invalid. And even then, it was
> sloppy in ways you point out, e.g, regarding enumerability.
Sorry if i was a little harsh on my e-mail. It wasn't my intention.
The critiques on the code were just meant to keep examples consistent to
not puzzle newcomers to the wiki.

> However, it is a good example of the kinds of abstraction that one
> could write using the modern proxy API, *if* we don't weaken the
> invariants it relies on. And again, the "read" function I pointed to
> is a production example which also relies on these invariants.
But the very question I am asking is: do we need this kind of
abstractions? the "read" function you showed uses some invariants in
production for SES. So these invariants should be kept (unless
equivalent robustness can be guaranteed with weaker invariants).
However, do all invariants have such use? I am sorry to ask this
question this way, but this is all very new to me and I'm blind when it
comes to justifying that such or such invariant is required to build
such or such things which is used (or should be or may be) in such and
such context.

By the way, I am a bit puzzled by the read function. The comment on top
says "The frozen property should preserve the enumerability of the
original property.". However, l.636-638, the call to defineProperty
doesn't set "enumerable" (implicitly set to 'false'). Shouldn't it be
enumerable:desc.enumerable, or am I missing something?

David
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
In reply to this post by Mark S. Miller-2
Le 17/06/2011 23:21, Mark S. Miller a écrit :
On Fri, Jun 17, 2011 at 1:50 PM, David Bruant <[hidden email]> wrote:
Le 17/06/2011 21:41, David Bruant a écrit :

To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
...wait a minute. There is no invariant imposing this. There is an invariant asking to a property that might /disappear/ to show "configurable:true" but it does not prevent from manually deleting a property with "configurable:false", does it?

If the delete succeeds, then the property disappears, so yes, this would be prohibited.
Ok. "Disappear" seemed to imply "not manually" to me, but I'm not a native English speaker.
 
I agree that this is the expectation we have from a native object, but I am not sure this is enforced by the spec on abnormal native or non-native (host) objects.

I'm not sure that the delete operation must throw, though I hope it must. But it cannot result in a non-configurable property disappearing.
Actually, the spec is not very precise on this point.
ES5.1 11.4.1 the delete operator "In addition, if a delete operator occurs within strict mode code and the property to be deleted has the attribute { [[Configurable]]: false }, a TypeError exception is thrown."
An exception is thrown, however, nothing is said on the destiny of the property. Has it been deleted anyways? One can assume not, but the spec leaves it implicit.


- The first observed value of [[Prototype]] of an object must remain throughout the program lifetime

For ES5.1, we explicitly do not prohibit changes to the [[Prototype]] on extensible objects. Neither do we provide any way to change it, but implementations that allow such changes do not thereby violate ES5.1. I believe we should prohibit this in ES-next, but I cannot say we yet have consensus on that.

We do prohibit changes to [[Prototype]] on frozen objects. I think we prohibit such changes on all non-extensible objects, but no longer remember for sure.
The spec words are "if [[Extensible]] is false the value of the [[Class]] and [[Prototype]] internal properties of the object may not be modified."..."Implementation specific extensions that modify [[Class]], [[Prototype]] or [[Extensible]] must not violate the invariants defined in the preceding paragraph."
Based on that a decision has to be made between:
- changes to [[Prototype]] even on extensible objects should be prohibited
- getPrototypeOf should be a proxy trap.

And I am also going to ask the same question as usual: is this invariant on extensible objects and prototype actually used (in SES for instance)?

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Tom Van Cutsem-3
In reply to this post by David Bruant-4
2011/6/17 David Bruant <[hidden email]>
Symetrically to the forwarding of defineProperty, maybe that getOwnPropertyDescriptor could be forwarded too (requires to call the getOwnPropertyDescriptor trap for non-configurable properties) and the return value could be used to redefine writable on the proxy fixed property if necessary.

Yep, trapping getOwnPropertyDescriptor on fixed properties seems to close the loop.

However, I find it strange that the return value of getOwnPropertyDescriptor would actually implicitly side-effect the fixed property of the proxy. I naturally think of defineProperty as a side-effecting trap, not so for getOwnPropertyDescriptor. Perhaps it should only check the return value for consistency with the fixed descriptor, without side-effects.

One can still achieve 'synchrony' between a target's descriptor and the proxy's fixed descriptor by explicitly setting the property in the getOwnPropertyDescriptor trap (at least then the side-effect is explicit):

// assume 'name' already refers to a fixed property within 'proxy'
function getOwnPropertyDescriptor(name, proxy) {
  var desc = Object.getOwnPropertyDescriptor(this.target, name);
  if (desc && !desc.configurable) {
    // desc is non-configurable, redefine it on the proxy to keep it in sync
    Object.defineProperty(proxy, name, desc); // can only change writable:true into writable:false
  }
  return desc; // proxy will test whether 'desc' is consistent with the fixed descriptor stored in 'proxy'
}
 
- Issue with a proxy saying that an inherited property is a non-configurable property (getPropertyDescriptor trap). Basically, getPropertyDescriptor may say that a property is configurable while getPrototypeOf+getOwnPropertyDescriptor(on a native object which allows non-configurable property) may say that the property is not-configurable. They would both talk about the same property and be inconsistent.

Yes, this is a thorny issue. We could drop the getPropertyDescriptor operation & trap. What we lose by that is that proxies would be unable to fully emulate inherited properties. That is: while a proxy's get/set traps may still emulate "foo" as an inherited property (i.e. proxy.foo returns a value, but Object.getOwnPropertyDescriptor(proxy,'foo') returns undefined), the illusion would be broken once the proxy's inheritance chain is inspected (i.e. code may find out that there's actually no "foo" property on the proxy's prototype chain). Actually, one could argue that even without getPropertyDescriptor proxies already cannot emulate prototype inheritance, since they can't virtualize Object.getPrototypeOf.
For the getPropertyDescriptor trap, if the returned descriptor has "configurable:false", the engine could climb the prototype chain to check that "configurable:false" is actually legitimate (only configurability). May be costly?

Ugh :-/
I'm not keen on mandating such prototype-climbing checks.

If given the choice, I would prefer taking away the complexity introduced by |getPropertyDescriptor| by removing the proposed built-in + the trap, rather than mandating more consistency checks.
 
To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
- All other property-specific traps have the default derived trap behavior (all the remaining property-specific traps are derived).

Does that sounds right?

Yes, sounds right to me.

Cheers,
Tom

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Tom Van Cutsem-3
In reply to this post by David Bruant-4
2011/6/19 David Bruant <[hidden email]>
Le 19/06/2011 00:30, Mark S. Miller a écrit :
> Hi David, yes, that code is just a stale illustrative example. This
> abstraction itself is not being used, and the code predates many
> changes to the proxy API that render it invalid. And even then, it was
> sloppy in ways you point out, e.g, regarding enumerability.
Sorry if i was a little harsh on my e-mail. It wasn't my intention.
The critiques on the code were just meant to keep examples consistent to
not puzzle newcomers to the wiki.

Thanks for pointing out these flaws. I updated the example so that the API is at least used consistently on the wiki page.

Cheers,
Tom

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
In reply to this post by David Bruant-4
On Sat, Jun 18, 2011 at 4:44 PM, David Bruant <[hidden email]> wrote:
Le 19/06/2011 00:30, Mark S. Miller a écrit :
> Hi David, yes, that code is just a stale illustrative example. This
> abstraction itself is not being used, and the code predates many
> changes to the proxy API that render it invalid. And even then, it was
> sloppy in ways you point out, e.g, regarding enumerability.
Sorry if i was a little harsh on my e-mail. It wasn't my intention.
The critiques on the code were just meant to keep examples consistent to
not puzzle newcomers to the wiki.

Hi David, I didn't take it as harsh, just explaining context. Critiques are always appreciated. Tom, thanks for fixing the examples.

 

> However, it is a good example of the kinds of abstraction that one
> could write using the modern proxy API, *if* we don't weaken the
> invariants it relies on. And again, the "read" function I pointed to
> is a production example which also relies on these invariants.
But the very question I am asking is: do we need this kind of
abstractions? the "read" function you showed uses some invariants in
production for SES. So these invariants should be kept (unless
equivalent robustness can be guaranteed with weaker invariants).
However, do all invariants have such use? I am sorry to ask this
question this way, but this is all very new to me and I'm blind when it
comes to justifying that such or such invariant is required to build
such or such things which is used (or should be or may be) in such and
such context.

Hi David,

The question you are asking is important I would like to take the time to give it a serious answer. Unfortunately, I do not currently have that time and a too-quick answer to this question, especially on es-discuss, will do harm than good. I will get back to this, but not in the next two weeks. (This isn't the only crucial es-discuss discussion I need to postpone. I'm already feeling guilty about not engaging with the "can we do without classes?" threads, but can't get to those soon either.)

That said, the short beginning of an answer is that these issues do not come up primarily in the SES implementation, they come up primarily in anticipated SES use, which will include code along the lines of the flawed example Tom fixed. Safely handling objects from potential adversaries is *hard*. The more useful invariants there are that such code might find useful, that adversaries cannot violate, the easier this burden becomes. 

There are other ways to approach the question of what such invariants should be. E and Joe-E took a very different tack and ended up with a different (and IMO better) mixture of what-is-easy and what-is-hard. Given the legacy compatibility constraints shaping ES3.1, I think we ended up with about as good a mix as could be expected. There are several things I wish we had decided differently, but...

 
By the way, I am a bit puzzled by the read function. The comment on top
says "The frozen property should preserve the enumerability of the
original property.". However, l.636-638, the call to defineProperty
doesn't set "enumerable" (implicitly set to 'false'). Shouldn't it be
enumerable:desc.enumerable, or am I missing something?

In an Object.defineProperty call to change an existing property, missing attributes default to their current setting.


--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Mark S. Miller-2
In reply to this post by David Bruant-4


On Sun, Jun 19, 2011 at 4:36 AM, David Bruant <[hidden email]> wrote:
Le 17/06/2011 23:21, Mark S. Miller a écrit :
On Fri, Jun 17, 2011 at 1:50 PM, David Bruant <[hidden email]> wrote:
Le 17/06/2011 21:41, David Bruant a écrit :

To summurize fixed propeties property-specific trap behaviors, one could say that:
- defineProperty trap is called (and its return value is meaningful as explained on the current strawman)
(if approved) - getOwnPropertyDescriptor trap is called (and its return value is meaningful the same way)
- delete rejects
...wait a minute. There is no invariant imposing this. There is an invariant asking to a property that might /disappear/ to show "configurable:true" but it does not prevent from manually deleting a property with "configurable:false", does it?

If the delete succeeds, then the property disappears, so yes, this would be prohibited.
Ok. "Disappear" seemed to imply "not manually" to me, but I'm not a native English speaker.

 
I agree that this is the expectation we have from a native object, but I am not sure this is enforced by the spec on abnormal native or non-native (host) objects.

I'm not sure that the delete operation must throw, though I hope it must. But it cannot result in a non-configurable property disappearing.
Actually, the spec is not very precise on this point.
ES5.1 11.4.1 the delete operator "In addition, if a delete operator occurs within strict mode code and the property to be deleted has the attribute { [[Configurable]]: false }, a TypeError exception is thrown."
An exception is thrown, however, nothing is said on the destiny of the property. Has it been deleted anyways? One can assume not, but the spec leaves it implicit.

If the spec nowhere states this explicitly, I would consider it a spec bug to be added to the errata. The intent is clearly that the property not disappear in this circumstance.

 

- The first observed value of [[Prototype]] of an object must remain throughout the program lifetime

For ES5.1, we explicitly do not prohibit changes to the [[Prototype]] on extensible objects. Neither do we provide any way to change it, but implementations that allow such changes do not thereby violate ES5.1. I believe we should prohibit this in ES-next, but I cannot say we yet have consensus on that.

We do prohibit changes to [[Prototype]] on frozen objects. I think we prohibit such changes on all non-extensible objects, but no longer remember for sure.
The spec words are "if [[Extensible]] is false the value of the [[Class]] and [[Prototype]] internal properties of the object may not be modified."..."Implementation specific extensions that modify [[Class]], [[Prototype]] or [[Extensible]] must not violate the invariants defined in the preceding paragraph."
Based on that a decision has to be made between:
a) changes to [[Prototype]] even on extensible objects should be prohibited
b) getPrototypeOf should be a proxy trap.
[labeled your bullets above so we can refer back to them]

While I do not agree with "has to be made", I do agree with "should be made". Without changing either, the spec makes a consistent even if somewhat silly stance:

x) It nowhere provides any means for modifying a [[Prototype]], so implementations in which [[Prototype]] cannot be modified are conforming.
y) It prohibits changes to [[Prototype]] only on non-extensible objects, so implementations which allow such changes to extensible objects by unspecified means are conforming.

Choosing #a implies loss of #y. Choosing #b implies loss of #x.

I think we should chose #a and lose #y. If we can't agree on that, I think I prefer the status quo to choosing #b and losing #x, as it still leaves open the possibility that we could choose #a in the future.
 

And I am also going to ask the same question as usual: is this invariant on extensible objects and prototype actually used (in SES for instance)?

In answering this question, I think we may have a bug. I think I may be counting on [[Prototype]] not changing in general, rather than only on non-extensible objects. I need to get back to you on that. Thanks for raising it!

--
    Cheers,
    --MarkM

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

David Bruant-4
In reply to this post by Tom Van Cutsem-3
Le 19/06/2011 17:43, Tom Van Cutsem a écrit :
2011/6/17 David Bruant <[hidden email]>
Symetrically to the forwarding of defineProperty, maybe that getOwnPropertyDescriptor could be forwarded too (requires to call the getOwnPropertyDescriptor trap for non-configurable properties) and the return value could be used to redefine writable on the proxy fixed property if necessary.

Yep, trapping getOwnPropertyDescriptor on fixed properties seems to close the loop.

However, I find it strange that the return value of getOwnPropertyDescriptor would actually implicitly side-effect the fixed property of the proxy. I naturally think of defineProperty as a side-effecting trap, not so for getOwnPropertyDescriptor. Perhaps it should only check the return value for consistency with the fixed descriptor, without side-effects.
It won't be enough to ensure invariants.
----
var ep = EvilProxy();
Object.defineProperty(ep, 'a', {value:1, configurable:false, writable:true}); // works as expected
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:true}
// This is the stored fixed property descriptor
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:false} is legitimate
// because it doesn't break any invariant. No side effect-here, stored descriptor remains the same
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:true}
// This is coherent with what is stored in the fixed property record
----
My point is that the second Object.getOwnPropertyDescriptor call is legitimate, but if we do not store that writable changed from "true" to "false", then it can be later observed as "true" which violates the ES5.1 invariant:
"If the [[Writable]] attribute may change from false to true, then the [[Configurable]] attribute must be true."
Consequently, for this very case (changing writable from false to true for non-configurable properties), getOwnPropertyDescriptor needs to have a side-effect. That's probably the only one.

To sum up, we need to trap getOwnPropertyDescriptor for writable consistency in the forwarding use case. However, if we trap and do not have a side effect, an EvilProxy could break an invariant.


Also, as it turns out, there is actually no need to store the entire property descriptor for fixed properties to enforce invariants. There is a need to store:
1) Whether the property has been observed as non-configurable and if it's the case:
1.1) Whether writable has been observed to false and if it's the case
1.1.1) latest observed/provided value (no need to keep the value until configurable and writable or both false since it can be changed anytime. First universal property invariant)
1.2) latest observed/provided get/set
1.3) latest observed/provided enumerable (there are no invariants on this one, so I'm not sure)

I think that the notion of "latest observed" is the one requiring getOwnPropertyDescriptor to have side-effects (even though it may sound counter intuitive).

David

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: [Harmony Proxies] Proposal: Property fixing

Tom Van Cutsem-3
2011/6/19 David Bruant <[hidden email]>
Le 19/06/2011 17:43, Tom Van Cutsem a écrit :
2011/6/17 David Bruant <[hidden email]>
Symetrically to the forwarding of defineProperty, maybe that getOwnPropertyDescriptor could be forwarded too (requires to call the getOwnPropertyDescriptor trap for non-configurable properties) and the return value could be used to redefine writable on the proxy fixed property if necessary.

Yep, trapping getOwnPropertyDescriptor on fixed properties seems to close the loop.

However, I find it strange that the return value of getOwnPropertyDescriptor would actually implicitly side-effect the fixed property of the proxy. I naturally think of defineProperty as a side-effecting trap, not so for getOwnPropertyDescriptor. Perhaps it should only check the return value for consistency with the fixed descriptor, without side-effects.
It won't be enough to ensure invariants.
----
var ep = EvilProxy();
Object.defineProperty(ep, 'a', {value:1, configurable:false, writable:true}); // works as expected
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:true}
// This is the stored fixed property descriptor
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:false} is legitimate
// because it doesn't break any invariant. No side effect-here, stored descriptor remains the same
Object.getOwnPropertyDescriptor(ep, 'a'); // {value:1, configurable:false, writable:true}
// This is coherent with what is stored in the fixed property record
----
My point is that the second Object.getOwnPropertyDescriptor call is legitimate, but if we do not store that writable changed from "true" to "false", then it can be later observed as "true" which violates the ES5.1 invariant:
"If the [[Writable]] attribute may change from false to true, then the [[Configurable]] attribute must be true."
Consequently, for this very case (changing writable from false to true for non-configurable properties), getOwnPropertyDescriptor needs to have a side-effect. That's probably the only one.

To sum up, we need to trap getOwnPropertyDescriptor for writable consistency in the forwarding use case. However, if we trap and do not have a side effect, an EvilProxy could break an invariant.


Also, as it turns out, there is actually no need to store the entire property descriptor for fixed properties to enforce invariants. There is a need to store:
1) Whether the property has been observed as non-configurable and if it's the case:
1.1) Whether writable has been observed to false and if it's the case
1.1.1) latest observed/provided value (no need to keep the value until configurable and writable or both false since it can be changed anytime. First universal property invariant)
1.2) latest observed/provided get/set
1.3) latest observed/provided enumerable (there are no invariants on this one, so I'm not sure)

I think that the notion of "latest observed" is the one requiring getOwnPropertyDescriptor to have side-effects (even though it may sound counter intuitive).

Yes, you're right. Thanks for the clarifying example. I gave a first shot at trying to define the semantics of [[GetOwnProperty]] for Proxies with support for fixed properties:

[[GetOwnProperty]] (P)
1. Let handler be the value of the [[Handler]] internal property of O.
2. Let getOwnProperty be the result of calling the [[Get]] internal method of handler with argument “getOwnPropertyDescriptor”.
3. If getOwnProperty is undefined, throw a TypeError exception.
4. If IsCallable(getOwnProperty) is false, throw a TypeError exception.
5. Let trapResult be the result of calling the [[Call]] internal method of getOwnProperty providing handler as the this value and P as the first argument.
6. Let fixedProperty be the result of calling Object.[[GetOwnProperty]]( P )
7. If trapResult is undefined
  a. If fixedProperty is undefined, return undefined
  b. Otherwise, fixedProperty is defined, so throw a TypeError
8. Let desc be ToCompletePropertyDescriptor(trapResult)
9. If fixedProperty is not undefined, or desc.[[Configurable]] is false
  a. call Object.[[DefineOwnProperty]](P, desc, true)
10. Return desc

Here, Object.[[GetOwnProperty]] and Object.[[DefineOwnProperty]] refer to the algorithms for Object values
from ES5 sections 8.12.1 and 8.12.9 respectively.
Line 7.b. is necessary to guard against a handler reporting a previously fixed property as undefined.

It's true that the proxy does not necessarily need to store the entire fixed property descriptor. However, in terms of specifying the semantics, reusing the existing built-in semantics for Objects seems easier to grasp.

Cheers,
Tom

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
1 ... 4567