[Harmony Proxies] Proposal: Property fixing

Tom Van Cutsem tomvc.be at gmail.com
Thu May 12 05:40:39 PDT 2011


I thought it might be productive to try and list the relevant "invariants"
that proxies may or may not violate. Here is an initial list, which I do not
claim is complete. If people think it's useful I can also record them on the
wiki.

invariants that are enforced:

- proxies can't take on the identity of other objects (i.e. '===' can't be
intercepted)

- immutable [[Prototype]]

- unconfigurable instanceof (Proxy.create(handler, f.prototype) implies
proxy instanceof f, function proxies are instanceof Function)

- unconfigurable typeof (typeof objectproxy === "object", typeof
functionproxy === "function")

- unconfigurable [[Class]] (Object or Function, respectively)

- once preventExtensions, seal, freeze is called on a proxy, its properties
are fixed, so the invariants associated with these operations are maintained
(e.g. can't add new properties, can't delete existing properties, …)


controversial invariant:

- proxies can't emulate non-configurable properties. If they would, proxies
could still update attributes of non-configurable properties.


invariants that can be broken:

- general inconsistencies between traps

  - e.g. has('foo') returning true while getPropertyDescriptor('foo')
returns undefined

  - e.g. has('foo') returning true while getPropertyNames() doesn't contain
it

  - e.g. get('foo') returning 42 while getOwnPropertyDescriptor('foo').value
returns 24 (with no assignment operations happening in between)

- traps that return values of the wrong type, e.g. getOwnPropertyNames not
returning an array, getOwnPropertyDescriptor not returning a valid property
descriptor

- inheritance: traps are free to ignore the proxy's prototype when it comes
to property lookup

- duplicate property names in the property listing traps (enumerate,
get{Own}PropertyNames)

- the keys/enumerate traps should only return enumerable property names

- the keys/getOwnPropertyNames traps should only return "own" property names

- the result of getOwnPropertyNames should be a proper subset of the result
of getPropertyNames (same for enumerate/keys)

- ... (I did not try to be exhaustive)

Because the ES5 spec is very implicit about most of these invariants, any
distinction between which invariants to uphold and which not will be
necessarily vague. However, I can discern some logic in the current
distinction:

Most of the enforced properties have to do with classification: instanceof,
typeof and === are operators used to classify objects, and classifications
typically come with some implied invariants (I'm aware of the fact that
instanceof tests can be non-monotonic in JS).

For {freeze|seal|preventExtensions}, one can make the case that defensive
programming is one of their main use cases. Allowing proxies to gratuitously
break them feels like taking away a lot of the usefulness of these
primitives. The use case is not always defensive programming, e.g. frozen
objects facilitate caching without cache invalidation.


W.r.t. non-configurable properties: at this point I am convinced that Sean's
API is better than the current design of outright rejecting non-configurable
properties. Surely there will be cases where proxies will need to emulate
non-configurable properties. Also, the fact that the default forwarding
handler can't straightforwardly delegate getOwnPropertyDescriptor calls to
its target (since it has to change the property's configurability) is a bad
smell.


Building on an earlier idea proposed by David ("inheritance-safe proxies"),
a compromise could be as follows:

- allow proxies to emulate/intercept non-configurable properties without
checking

- introduce an "ESObject" abstraction such that if h is a user-defined proxy
handler, ESObject(h) creates a "safe" proxy handler that checks conformance
of the handler w.r.t. the above ES5 Object semantics. This can be useful for
catching bugs, or preventing misbehavior, depending on your POV.


Whether or not ESObject should or could be fully defined in either the
engine or in Javascript is an orthogonal issue.


Cheers,

Tom


2011/5/11 David Bruant <david.bruant at labri.fr>

> Le 11/05/2011 08:41, Allen Wirfs-Brock a écrit :
>
>  I think we are dancing around one of the key differences between static
>> languages and dynamic languages.  Static languages make guarantees about a
>> set of potentially complex invariants  (for example, subtype conformance).
>> They can do this because the necessary work to detect violations of those
>> invariants is performed ahead of time before the program is allowed to
>> execute.  Dynamic languages do most invariant validation as the program runs
>> and hence generally restrict themselves to guaranteeing simple invariants
>> (for example, memory safety) that can be cheaply performed many times as the
>> program runs.  Dynamic languages generally avoid expense checking of complex
>> invariants and instead assume that any critical violation of complex
>> invariants will ultimately manifest  themselves as violations of the simple
>> invariants that are checked.
>>
>> A related difference is that a static language generally rejects programs
>> when it proves the set of all possible program inputs produces some states
>> that violate the language's invariants.  The program is rejected, even if
>> the input states that produce the invariant violations will never occur in
>> practice.  This is a conservative (or pessimistic) approach -- if a program
>> might fail, we assume it will fail.  Dynamic languages generally only reject
>> programs (at runtime) when the actual data values used by the program
>> violates the language's invariants.  This is a permissive (or optimistic)
>> approach -- if a program might work, we give it the benefit of the doubt and
>> let it run up to the point it begins to misbehave.
>>
>> The configurability restrictions on Proxies seems to be trying to apply a
>> static language perspective to the very dynamic ES language.  They are based
>> upon a complex invariant (what can/cannot be assumed after  observing the
>> state of a configurable attribute).  Because, there is at best difficult to
>> guarantee that user written proxy handlers will correctly enforce the
>> invariants associated with of configurable:false it is forbidden for a proxy
>> to set configurable to that state.  It is pessimistic, it says that because
>> somebody might write a buggy proxy setting configurable we won't let them
>> write any proxy that sets configurable.  An alternative that has been
>> proposed is to try to dynamically enforce the configurable invariants.  But
>> that is an example, of moving expensive (and probably highly redundant)
>>  complex invariants checks into runtime.  While it would catch buggy
>> programs, but has the potential of imposing a significant runtime
>> performance penalty on valid programs.
>>  The normal dynamic language approach to this sort of problem is to be
>> optimistic about the validity of the program while continuing to guarantee
>> memory safety, and depending upon conventional testing procedure to detect
>> more complex error conditions.
>>
> I understand the rationale that leads to the difference you describe in
> static/dynamic languages design. I understand it and I think these are good
> reasons. However, I can't help asking you some sort of proof. Has some
> research been done in the area?
> Are there dynamic languages that tried to enforce invariants at run-time?
> What lessons did they learn from that experience?
> Was the cost on valid program big enough to question these checks?
> Are there examples of dynamic languages interpreter with static analysis
> that were able to diminish this cost? Diminishing the cost to make the
> program "as fast in the long term"? (I quote, because I know that "as fast"
> and "in the long term" are vague notions)
>
> David
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110512/1c5d07c0/attachment.html>


More information about the es-discuss mailing list