[Harmony proxies] Non-configurable properties: fixed properties VS trap all with invariants enforcement

Mark S. Miller erights at google.com
Sun Jul 3 12:32:29 PDT 2011


On Sun, Jul 3, 2011 at 11:07 AM, David Bruant <david.bruant at labri.fr> wrote:

> Le 03/07/2011 14:59, Tom Van Cutsem a écrit :
> > As a follow-up to the fixed properties proposal:
> >
> > I created a prototype implementation of fixed properties on top of the
> > existing Proxy API. The idea is to wrap an existing proxy handler in a
> > FixedHandler. This FixedHandler does the bookkeeping and checking to
> > ensure that the wrapped handler doesn't misreport non-configurable
> > properties.
> >
> > Full source code available here:
> > <
> http://code.google.com/p/es-lab/source/browse/trunk/src/proxies/FixedHandler.js
> >
> Just to make sure I do not misunderstand your work:
> - Your code relies on current Proxy implementation behavior (FF4&5)
> which doesn't do any check on the "configurable" attribute for
> defineProperty and get{Own}PropertyDecsriptor traps
>

I didn't know that. Is there a bug filed on this yet or a test case that
demonstrates this bug?



> - The idea is that eventually, by default, any handler would become your
> wrapped FixedHandler() handler (functionnally, of course; exact
> implementation would be left at the discretion of implementors)
>
> In your current FixedHandler implementation, all traps of the wrapped
> handler are called for configurable properties. For non-configurable
> properties, if already present in this.fixedProps, the "delete",
> "hasOwn", "has", "get" and "set" traps aren't called while all others
> are (first instruction of all other traps).
> This might appear to be an inconsistent behavior to the user. An easy
> fix could be to change a little bit these traps:
> -----
>  'delete': function(name) {
>    var ret = this.targetHandler['delete'](name);
>    // allows to throw with personalized message error rather than
> default decided by the engine.
>
>    if (name in this.fixedProps) {
>      throw new TypeError(
>        "property "+name+" is non-configurable and can't be deleted");
>    }
>
>    return ret;
>  },
>
>  hasOwn: function(name) {
>    var ret = this.targetHandler.hasOwn ?
>             this.targetHandler.hasOwn(name) : // call the trap in all
> cases if present
>             TrapDefaults.hasOwn.call(this.targetHandler, name);
>    return name in this.fixedProps || ret; // always true if
> non-configurable
>  },
>
>  has: function(name) {
>    var ret = this.targetHandler.has ?
>             this.targetHandler.has(name) : // call the trap in all
> cases if present
>             TrapDefaults.has.call(this.targetHandler, name);
>    return name in this.fixedProps || ret; // always true if
> non-configurable
>  },
>
>  get: function(rcvr, name) {
>    var ret = this.targetHandler.get ?
>             this.targetHandler.get(rcvr, name) :
>             TrapDefaults.get.call(this.targetHandler, rcvr, name);
>
>    var desc = Object.getOwnPropertyDescriptor(this.fixedProps, name);
>    if (desc !== undefined) {
>      if ('value' in desc) {
>        return desc.value;
>      } else {
>        if (desc.get === undefined) { return undefined; }
>        return desc.get.call(rcvr);
>      }
>    }
>
>    return ret;
>  },
>
>  set: function(rcvr, name, val) {
>    var ret = this.targetHandler.set ?
>             this.targetHandler.set(rcvr, name, val) :
>             TrapDefaults.set.call(this.targetHandler, rcvr, name, val);
>
>    var desc = Object.getOwnPropertyDescriptor(this.fixedProps, name);
>    if (desc !== undefined) {
>      if ('writable' in desc) { // fixed data property
>        if (desc.writable) {
>          desc.value = val;
>          this.defineProperty(name, desc);
>          return true;
>        } else {
>          return false;
>        }
>      } else { // fixed accessor property
>        if (desc.set) {
>          desc.set.call(rcvr, val);
>          return true;
>        } else {
>          return false;
>        }
>      }
>    }
>    // simulate missing derived trap fall-back behavior
>    return ret;
>  },
> -----
> This way, all traps are called, regardless of configurability and the
> invariant-complying result is returned in all cases.
> When it comes to return value, my implementation may surprise the
> programmer as what he return from his (target)handler doesn't match what
> is returned in the end. An alternative could be to test if "ret" matches
> expectations (with harmony:egal, not === of course) and throw if it's
> not the case.
>
> Unless testing expectation matching, there is barely any overhead in
> calling the traps (besides the trap themselves, but that's a cost the
> programmer is ready to pay for configurable properties already) when
> comparing with your implementation.
> And it has the advantages of not leaking things the way I've showed it
> at the beginning of this thread for the membrane use case.
>
> Thanks for the follow-up and the implementation.
>
> David
>



-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110703/425ea4ba/attachment.html>


More information about the es-discuss mailing list