[Harmony Proxies] Proposal: Property fixing

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

[Harmony Proxies] Proposal: Property fixing

seaneagan1
What:

Honor attempts to make individual proxy properties non-configurable.

Why:

For consistency since attempts to make *all* of a proxy's properties
non-configurable are honored via the "fix" trap.

Example:

var x = Proxy.create({
  defineProperty: function() {},
  getPropertyDescriptor: function() {
    return {
      value: "whatever I want",
      writable: true,
      configurable: true,
      enumerable: false
    };
  }
});
Object.defineProperty(x, "foo", {value: "bar", configurable: false,
writable: false, enumerable: true};

/* ... */

// logs "whatever I want", not "bar"
console.log(x.foo);
// non-writable, but doesn't throw
object.foo = "baz";
// non-configurable, but doesn't throw
Object.defineProperty(x, "foo", {configurable: true});

How:

Assume a "defineProperty" trap invocation due to
|Object.defineProperty(proxy, name, pd)| where |!pd.configurable|.  If
return value is...

  true - Henceforth bypass |proxy|'s handler for any traps with a
property name parameter when the property name would be |name|
  false - throw TypeError similarly to "fix" trap

Update the "fix" trap semantics such that when its return value is not
undefined but rather a property descriptor map, behavior is similar to
|Object.defineProperties| in that improperly redefining any properties
will cause a TypeError to be thrown.

Notes:

Can check |Object.getOwnPropertyDescriptor(proxy, name).configurable|
to determine if a given proxy property is fixed since it will always
be false in this case.


Thanks,
Sean Eagan
_______________________________________________
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

seaneagan1
Further clarification:

The traps that have a property name parameter are...

getOwnPropertyDescriptor
hasOwn
defineProperty
delete
getPropertyDescriptor
has
get
set

The internal methods that these traps are invoked from could delegate
to their standard object implementations if the property name they are
passed represents a fixed property, and perform proxy logic otherwise.

It would be optimal to ensure that all the other traps ...

getOwnPropertyNames
keys
getPropertyNames
enumerate
iterate

... always include any fixed properties, but the associated validation
logic would be inefficient, and a solution along the lines of just
tacking the fixed properties on to the front is probably too
inflexible, so it is probably leave those traps alone with respect to
property fixing.

Implementation considerations:

Implementations could store fixed properties in an own properties
table just as they would for regular objects.  This table would not
need to be created unless and until any properties are actually fixed.
 When a proxy's "fix" trap is invoked, if this table already exists,
it just "becomes" the object's own property table, and then the
properties in the "fix" trap return value can be added to this table
as if via |Object.defineProperties|.

Bug fixes:

> Object.defineProperty(x, "foo", {value: "bar", configurable: false, writable: false, enumerable: true};

... should have had a closing paren, and ...

> // non-writable, but doesn't throw
> object.foo = "baz";

... should have been ...

// non-writable, but "set" trap still called
x.foo = "baz";


Thanks,
Sean Eagan
_______________________________________________
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
Hi Sean,

So in this proposal, once an individual property has been fixed, any access/assignment to it can no longer be intercepted? I'm not sure whether that's always beneficial. We are then taking away some power of proxies to ensure more consistency, but maybe this additional power is still needed in some wrapping scenarios. Consider a hypothetical scenario in which a funky host object ignores the "configurable" attribute. Proxies won't be able to patch it up anymore. (I have no idea how likely such scenarios actually are though)

Also, as you point out, full consistency across all traps is difficult to guarantee, so does it make sense to enforce consistency for some traps but not for others? The overall behavior of the proxy may still be inconsistent.

I do see benefit in this proposal as an aid for proxy writers to develop more well-behaved proxies. But for that purpose it seems that this proposal can be fully written as a Javascript library layered on top of the existing API.

Cheers,
Tom

2011/5/5 Sean Eagan <[hidden email]>
Further clarification:

The traps that have a property name parameter are...

getOwnPropertyDescriptor
hasOwn
defineProperty
delete
getPropertyDescriptor
has
get
set

The internal methods that these traps are invoked from could delegate
to their standard object implementations if the property name they are
passed represents a fixed property, and perform proxy logic otherwise.

It would be optimal to ensure that all the other traps ...

getOwnPropertyNames
keys
getPropertyNames
enumerate
iterate

... always include any fixed properties, but the associated validation
logic would be inefficient, and a solution along the lines of just
tacking the fixed properties on to the front is probably too
inflexible, so it is probably leave those traps alone with respect to
property fixing.

Implementation considerations:

Implementations could store fixed properties in an own properties
table just as they would for regular objects.  This table would not
need to be created unless and until any properties are actually fixed.
 When a proxy's "fix" trap is invoked, if this table already exists,
it just "becomes" the object's own property table, and then the
properties in the "fix" trap return value can be added to this table
as if via |Object.defineProperties|.

Bug fixes:

> Object.defineProperty(x, "foo", {value: "bar", configurable: false, writable: false, enumerable: true};

... should have had a closing paren, and ...

> // non-writable, but doesn't throw
> object.foo = "baz";

... should have been ...

// non-writable, but "set" trap still called
x.foo = "baz";


Thanks,
Sean Eagan
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
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

seaneagan1
> So in this proposal, once an individual property has been fixed, any
> access/assignment to it can no longer be intercepted? I'm not sure whether
> that's always beneficial. We are then taking away some power of proxies to
> ensure more consistency, but maybe this additional power is still needed in
> some wrapping scenarios. Consider a hypothetical scenario in which a funky
> host object ignores the "configurable" attribute. Proxies won't be able to
> patch it up anymore. (I have no idea how likely such scenarios actually are
> though)

ES5 section 8.6.2 does mention some constraints around host object's
[[Configurable]] internal properties, but it doesn't explicitly forbid
them from ignoring it from what I can tell.

> Also, as you point out, full consistency across all traps is difficult to
> guarantee, so does it make sense to enforce consistency for some traps but
> not for others? The overall behavior of the proxy may still be inconsistent.

The only traps for which it would be difficult to enforce consistency
are proxy level traps not specific to individual properties, so it
might make sense to let these traps remain unconstrained anyways
unless and until the proxy as a whole is fixed via the "fix" trap.

> I do see benefit in this proposal as an aid for proxy writers to develop
> more well-behaved proxies. But for that purpose it seems that this proposal
> can be fully written as a Javascript library layered on top of the existing
> API.

But couldn't the same be said of the "fix" trap, frozen prototype, and
frozen typeof for proxies ?

Also, since proxies are not allowed to advertise their properties as
non-configurable, without something like this proposal proxy
transparency cannot be achieved.  With the current API, one could
implement a "Proxy.isProxy" function as follows...

Proxy.isProxy = function(objectOrProxy) {
  let unlikelyPropertyName = "_@_*_&_%_+_!_";
  Object.defineProperty(objectOrProxy, unlikelyPropertyName, {});
  return Object.getOwnPropertyDescriptor(objectOrProxy,
unlikelyPropertyName).configurable;
}


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

[Harmony Proxies] On property descriptor auto-completion in defineProperty/get{Own}PropertyDescriptor traps (derived from: [Harmony Proxies] Proposal: Property fixing)

David Bruant-4
In reply to this post by Tom Van Cutsem-3
Le 05/05/2011 20:38, Tom Van Cutsem a écrit :

> (...)
>
> Also, as you point out, full consistency across all traps is difficult
> to guarantee, so does it make sense to enforce consistency for some
> traps but not for others? The overall behavior of the proxy may still
> be inconsistent.
>
> I do see benefit in this proposal as an aid for proxy writers to
> develop more well-behaved proxies. But for that purpose it seems that
> this proposal can be fully written as a Javascript library layered on
> top of the existing API.
I think that these two points weigh in favor of not completing property
descriptor for the defineProperty/get{Own}PropertyDescriptor trap.
First, traps can ignore expected semantics of property attribute
("configurable", "enumerable" in particular), and by doing so, break
consistency with what we know of objects.
Then, completing property descriptors could be implemented as a library
on top of an existing non-completing proxy API. In order to help library
authors to implement such a thing, an
Object.prototype.toPropertyDescriptor method could be standardized
(following the definition at "ES5 8.10.5 ToPropertyDescriptor").

On the topic of defineProperty/get{Own}PropertyDescriptor traps, on the
open issue section of harmony:proxies is written: "Since a handler may
hold a reference to the property descriptor and change it later, should
the returned property descriptor object be copied?"
Last time I checked, FF4 implementation returns a copy (which is
consistent with ES5 ToPropertyDescriptor where a copy is performed to an
internal "Property Descriptor" representation). I think that this is a
good idea in order not the handler to be able to keep a reference to the
object. However, it has to be noted that it will prevent library writers
to use proxies as property descriptor (I am personally fine with this,
but it's worth noting).

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] On property descriptor auto-completion in defineProperty/get{Own}PropertyDescriptor traps (derived from: [Harmony Proxies] Proposal: Property fixing)

Allen Wirfs-Brock
I haven't been paying close attention to proxies for a while, so I may be missing something.  However, the requirement that a proxy only has configurable properties seems to severely limit their utility.  

Specify, it means that Proxies can not be used to implement/emulate ES5 Array instances (the length property is non-configurable) and  other built-ins that have non-configurable properties. I thought the ability to do so was one of the original goals of a Harmony Intercession mechanism.

I have also been talking to some DOM implementors who have similar concerns.  The current DOM and the WebIDL/ES DOM bindings apparently have objects with special behaviors where some properties are non-configurable.  How are we going to implement those if Proxies don't allow individual non-configurable properties.

I also have similar concerns about the fix trap: "A non-extensible, sealed or frozen object should somehow restrict the handler’s freedom in terms of what it can return from subsequent calls to ‘set’, ‘get’ etc. For example, if previous invocations of handler.get(p, “foo”) returned a non-undefined value (for some ‘handler’ of a proxy p), then future invocations of handler.get(p, “foo”) should return the same value when p is frozen."  While I can almost rationalize why this might be ok for frozen objects I don't think it flies even there.  I certainly don't see why preventing the addition of new properties or making all properties non-configurable should disable all trapping behavior.  For example, I might want to use a Proxy to log trace accesses to specific properties.  Why should freezing the object block this.

I gather that these constraints were applied  in order to guarantee  certainly invariants about object properties. While these invariants may be important for reasoning about programs it is less clear to me that they are essential for the low level operation of JavaScript implementations.  More importantly, I don't believe that guaranteeing these invariants  is important enough to for Proxies to be unusable for their primary use case of replacing/emulating built-in and host objects.  After all, while ES5 "requires" certain invariants of host objects it doesn't limit host object functionality in an attempt to make violations of those invariants impossible.  I'd be perfectly happy to take the same approach to Proxies.  There are certain invariants that we expect the implementation of a ES object to maintain.  An implementation that doesn't is buggy.  Whether that implementation is expressed in terms of a Proxies, or a "host object" foreign function interface, or the implementation of the core built-in objects is irrelevant.  They all needs equal power or they aren't interchangeable as implementation alternatives.

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] On property descriptor auto-completion in defineProperty/get{Own}PropertyDescriptor traps (derived from: [Harmony Proxies] Proposal: Property fixing)

David Bruant-4
Le 08/05/2011 22:58, Allen Wirfs-Brock a écrit :
> I also have similar concerns about the fix trap: "A non-extensible, sealed or frozen object should somehow restrict the handler’s freedom in terms of what it can return from subsequent calls to ‘set’, ‘get’ etc. For example, if previous invocations of handler.get(p, “foo”) returned a non-undefined value (for some ‘handler’ of a proxy p), then future invocations of handler.get(p, “foo”) should return the same value when p is frozen."  While I can almost rationalize why this might be ok for frozen objects I don't think it flies even there.  I certainly don't see why preventing the addition of new properties or making all properties non-configurable should disable all trapping behavior.  For example, I might want to use a Proxy to log trace accesses to specific properties.  Why should freezing the object block this.
We have discussed a similar point and one interesting solution came up
for the forwarding proxy case:
https://mail.mozilla.org/pipermail/es-discuss/2011-April/013573.html
It "saves" the behaviors for "get" and "set" traps (if these used to
call other traps, these traps will kept being called). It doesn't help
for other traps, though.


> (...)
> There are certain invariants that we expect the implementation of a ES object to maintain.  An implementation that doesn't is buggy.  Whether that implementation is expressed in terms of a Proxies, or a "host object" foreign function interface, or the implementation of the core built-in objects is irrelevant.  They all needs equal power or they aren't interchangeable as implementation alternatives.
I tend to agree... unless there would be security reason to state
otherwise. Proxies are user-inserted scripts and it may be a good enough
reason to diminish proxies power. I do not have an example in mind that
would justify this for now though.
Going in the direction you're describing would open the door for a
Object.getPrototypeOf trap (which I'm fine with).

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 seaneagan1
> I do see benefit in this proposal as an aid for proxy writers to develop
> more well-behaved proxies. But for that purpose it seems that this proposal
> can be fully written as a Javascript library layered on top of the existing
> API.

But couldn't the same be said of the "fix" trap, frozen prototype, and
frozen typeof for proxies ?

As for the fix trap: there needs to be some way for a proxy to react to freeze|seal|preventExtensions, even with your proposed extension.

As for frozen typeof: there have been proposals in the past of adding an additional argument to Proxy.create to configure the proxy's |typeof| value (still operating under the restrictions of ES5 11.4.3). If this configurability turns out to be necessary in certain scenarios, I'm sure it will be reconsidered.

As for frozen prototype: despite non-standard __proto__, the current consensus is to move away from mutable prototypes, not to add standardized features to support it.
 
Also, since proxies are not allowed to advertise their properties as
non-configurable, without something like this proposal proxy
transparency cannot be achieved.  With the current API, one could
implement a "Proxy.isProxy" function as follows...

Proxy.isProxy = function(objectOrProxy) {
 let unlikelyPropertyName = "_@_*_&_%_+_!_";
 Object.defineProperty(objectOrProxy, unlikelyPropertyName, {});
 return Object.getOwnPropertyDescriptor(objectOrProxy,
unlikelyPropertyName).configurable;
}

Good point. As recently pointed out by Allen, the design point of not allowing proxies to emulate non-configurable properties is still controversial. I have always felt it to be an odd restriction, but I don't have a good alternative that doesn't break |configurable|'s implied invariants. The sweet spot that we seem to be looking for is an API that:

a) allows non-configurable properties to be emulated,
b) while still allowing access to these properties to be intercepted (e.g. for logging/tracing),
c) and upholding the invariants of non-configurable properties.

The current API doesn't allow a, but upholds c (b is not an issue).
Your proposed extension would allow a, but not b, and still uphold c.
If one is willing to drop c, it's easy to get a and b by simply removing the check for configurable:true from the current API.

I can think of an API that satisfies all three by e.g. extending your proposal such that access to non-configurable properties still triggers the get trap, but ignores its result, making the proxy more like a chaperone in Racket <http://docs.racket-lang.org/reference/chaperones.html?q=chaperone#(tech._chaperone)> (chaperones may intercept operations, but are not allowed to influence the result of the operation).

I'm worried though that such additional magic, tucked away in the proxy implementation, can make for a very difficult-to-understand overall API.

Cheers,
Tom
 

Thanks,
Sean Eagan


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

Re: [Harmony Proxies] On property descriptor auto-completion in defineProperty/get{Own}PropertyDescriptor traps (derived from: [Harmony Proxies] Proposal: Property fixing)

Tom Van Cutsem-3
In reply to this post by David Bruant-4
Going in the direction you're describing would open the door for a
Object.getPrototypeOf trap (which I'm fine with).

David

How exactly would Allen's suggestion of relaxing the fix trap/configurable constraints open the door for supporting mutable prototypes via a getPrototypeOf trap? I must be missing something.

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

Allen Wirfs-Brock
In reply to this post by Tom Van Cutsem-3
On May 9, 2011, at 8:38 AM, Tom Van Cutsem wrote:

Good point. As recently pointed out by Allen, the design point of not allowing proxies to emulate non-configurable properties is still controversial. I have always felt it to be an odd restriction, but I don't have a good alternative that doesn't break |configurable|'s implied invariants. The sweet spot that we seem to be looking for is an API that:

a) allows non-configurable properties to be emulated,
b) while still allowing access to these properties to be intercepted (e.g. for logging/tracing),
c) and upholding the invariants of non-configurable properties.

The current API doesn't allow a, but upholds c (b is not an issue).
Your proposed extension would allow a, but not b, and still uphold c.
If one is willing to drop c, it's easy to get a and b by simply removing the check for configurable:true from the current API.


I contend we should drop c.  A handler that does not maintain the specified invariants is a buggy handler but, in general, we can't guarantee that programs do not contain bugs.  ECMAScript is a language that generally favors expressiveness over early error detection.  More generally, dynamic languages typically defer error checking as late as possible in order to avoid false positives.  They check what a program actually does rather than what it might do.  

Note that there are other explicit or implicit invariants about objects that are not guaranteed.  For example, the keys/enumerate traps should only return the names of properties whose [[enumberable]] attribute is false (at the time of the call) and keys should only return the names of "own" properties.  Yet we don't attempt to enforce that a handler correctly implements these invariants or disable proxy semantics upon the first occurrence of an action that might exercise buggy handler code.


I can think of an API that satisfies all three by e.g. extending your proposal such that access to non-configurable properties still triggers the get trap, but ignores its result, making the proxy more like a chaperone in Racket <http://docs.racket-lang.org/reference/chaperones.html?q=chaperone#(tech._chaperone)> (chaperones may intercept operations, but are not allowed to influence the result of the operation).

I'm worried though that such additional magic, tucked away in the proxy implementation, can make for a very difficult-to-understand overall API.

and would also properly result in a higher overhead (and hence less useful) facility.

I think our first priority should be the expressiveness of Proxies.  They need to be able to deal with all the use cases we have discussed including providing an faithful and efficient implementation of the existing built-ins and DOM. After we have that we can try to find situations where we can use the API to enforce various object invariants but even there we need to be selective. After all, a major use case for Proxies is to implement objects that in some way or another violate something about the normal invariants for native ES objects.  

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] On property descriptor auto-completion in defineProperty/get{Own}PropertyDescriptor traps (derived from: [Harmony Proxies] Proposal: Property fixing)

David Bruant-4
In reply to this post by Tom Van Cutsem-3
Le 09/05/2011 17:50, Tom Van Cutsem a écrit :
Going in the direction you're describing would open the door for a
Object.getPrototypeOf trap (which I'm fine with).

David

How exactly would Allen's suggestion of relaxing the fix trap/configurable constraints open the door for supporting mutable prototypes via a getPrototypeOf trap? I must be missing something.
Allen's last paragraph was more general than relaxing the fix trap/configurable constraints (at least that's how I understand it).

Allen wrote:
I don't believe that guaranteeing these invariants  is important enough to for Proxies to be unusable for their primary use case of replacing/emulating built-in and host objects.
One could imagine a buggy Object.getPrototypeOf on an host object (consecutive calls returning different objects, for instance). Will a proxy for which Object.getPrototypeOf is not trappable be able to replace/emulate all cases?
I admit that it is a bit hard to imagine, but in all rigour, there is no reason to think the prototype to be bug-free in all implementations (those which implement __proto__ in first line).
Actually, a writable __proto__ could be implemented (I don't discuss now if it's a good thing) with an Object.getPrototypeOf trap. Couldn't be without.

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

seaneagan1
In reply to this post by Tom Van Cutsem-3
On Mon, May 9, 2011 at 10:38 AM, Tom Van Cutsem <[hidden email]> wrote:

>> > I do see benefit in this proposal as an aid for proxy writers to develop
>> > more well-behaved proxies. But for that purpose it seems that this
>> > proposal
>> > can be fully written as a Javascript library layered on top of the
>> > existing
>> > API.
>>
>> But couldn't the same be said of the "fix" trap, frozen prototype, and
>> frozen typeof for proxies ?
>
> As for the fix trap: there needs to be some way for a proxy to react to
> freeze|seal|preventExtensions, even with your proposed extension.
> As for frozen typeof: there have been proposals in the past of adding an
> additional argument to Proxy.create to configure the proxy's |typeof| value
> (still operating under the restrictions of ES5 11.4.3). If this
> configurability turns out to be necessary in certain scenarios, I'm sure it
> will be reconsidered.
> As for frozen prototype: despite non-standard __proto__, the current
> consensus is to move away from mutable prototypes, not to add standardized
> features to support it.

I was not implying that these invariants should not be enforced, but
rather that if they are enforced, then non-configurable properties
should be enforced as well, as not doing so seems arbitrary and thus
confusing to users of the API.  As Allen mentioned, there are other
important invariants that are not currently enforced, and I would make
the same argument there, that either they should be enforced as well,
or that nothing should be enforced.

> As recently pointed out by Allen, the design point of not
> allowing proxies to emulate non-configurable properties is still
> controversial. I have always felt it to be an odd restriction, but I don't
> have a good alternative that doesn't break |configurable|'s implied
> invariants. The sweet spot that we seem to be looking for is an API that:
> a) allows non-configurable properties to be emulated,
> b) while still allowing access to these properties to be intercepted (e.g.
> for logging/tracing),
> c) and upholding the invariants of non-configurable properties.
> The current API doesn't allow a, but upholds c (b is not an issue).

It only upholds c due to the fact that it does not allow
non-configurable properties in the first place.

> I can think of an API that satisfies all three by e.g. extending your
> proposal such that access to non-configurable properties still triggers the
> get trap, but ignores its result, making the proxy more like a chaperone in
> Racket
> <http://docs.racket-lang.org/reference/chaperones.html?q=chaperone#(tech._chaperone)>
> (chaperones may intercept operations, but are not allowed to influence the
> result of the operation).
> I'm worried though that such additional magic, tucked away in the proxy
> implementation, can make for a very difficult-to-understand overall API.

I think the way to get b is actually through AOP (Aspect Oriented
Programming) support.  I would envision this as an AOP API that is
tightly integrated with the intercession API, having the intercession
API's "traps" serve as the "join points" "around" which the AOP API's
advice could be executed.

With regard to property fixing, if there really are use cases that it
restricts (although I don't see any), it could be made optional.  To
support this, this proposal would merely no longer throw due to a
falsey "defineProperty" trap invocation return value.

Thanks,
Sean Eagan
_______________________________________________
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

seaneagan1
In reply to this post by Allen Wirfs-Brock
On Mon, May 9, 2011 at 1:14 PM, Allen Wirfs-Brock <[hidden email]> wrote:

>> The sweet spot that we seem to be looking for is an API that:
>> a) allows non-configurable properties to be emulated,
>> b) while still allowing access to these properties to be intercepted (e.g.
>> for logging/tracing),
>> c) and upholding the invariants of non-configurable properties.
>> The current API doesn't allow a, but upholds c (b is not an issue).
>> Your proposed extension would allow a, but not b, and still uphold c.
>> If one is willing to drop c, it's easy to get a and b by simply removing the
>> check for configurable:true from the current API.
>
> I contend we should drop c.  A handler that does not maintain the specified
> invariants is a buggy handler but, in general, we can't guarantee that
> programs do not contain bugs.

But we can guarantee that programs do not contain *certain types* of
bugs, as is done with ES5 strict mode for example, and as long as this
does not preclude any reasonable use cases, then why not?

> ECMAScript is a language that generally
> favors expressiveness over early error detection.

I thought early error detection was one of the goals of ES Harmony ?

> More generally, dynamic
> languages typically defer error checking as late as possible in order to
> avoid false positives.  They check what a program actually does rather than
> what it might do.

I don't believe we have the option to check what a program actually
does in this case.  How would we validate that a proxy is upholding
the invariants of non-configurable properties throughout each
property's lifetime?

> Note that there are other explicit or implicit invariants about objects that
> are not guaranteed.  For example, the keys/enumerate traps should only
> return the names of properties whose [[enumberable]] attribute is false (at
> the time of the call) and keys should only return the names of "own"
> properties.  Yet we don't attempt to enforce that a handler correctly
> implements these invariants or disable proxy semantics upon the first
> occurrence of an action that might exercise buggy handler code.

I think while a proxy property is still configurable, semantics
related to particular values of property descriptor attributes such as
"enumerable" do not need to be enforced, because the value of those
properties can still be changed at any time, including within the
execution of another trap, such as "keys" or "enumerate".  However,
once a property is non-configurable, these values can change at any
time.

>> I can think of an API that satisfies all three by e.g. extending your
>> proposal such that access to non-configurable properties still triggers the
>> get trap, but ignores its result, making the proxy more like a chaperone in
>> Racket
>> <http://docs.racket-lang.org/reference/chaperones.html?q=chaperone#(tech._chaperone)>
>> (chaperones may intercept operations, but are not allowed to influence the
>> result of the operation).
>> I'm worried though that such additional magic, tucked away in the proxy
>> implementation, can make for a very difficult-to-understand overall API.
>
> and would also properly result in a higher overhead (and hence less useful)
> facility.

As I commented before, I think AOP support is what is actually needed
here, and thus no such magic or overhead is needed for property
fixing.

> I think our first priority should be the expressiveness of Proxies.  They
> need to be able to deal with all the use cases we have discussed including
> providing an faithful and efficient implementation of the existing built-ins
> and DOM.

I believe this proposal for property fixing would get us the
non-configurable properties necessary to handle some of the use cases
you are referring to.

> After we have that we can try to find situations where we can use
> the API to enforce various object invariants but even there we need to be
> selective. After all, a major use case for Proxies is to implement objects
> that in some way or another violate something about the normal invariants
> for native ES objects.

I think we should take a balanced approach as we have been to this
point.  Being able to depend on a decent set of invariants I believe
will be welcomed by both proxy providers and proxy consumers.

Thanks,
Sean Eagan
_______________________________________________
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

On May 10, 2011, at 11:18 AM, Sean Eagan wrote:

> On Mon, May 9, 2011 at 10:38 AM, Tom Van Cutsem <[hidden email]> wrote:
>>>> I do see benefit in this proposal as an aid for proxy writers to develop
>>>> more well-behaved proxies. But for that purpose it seems that this
>>>> proposal
>>>> can be fully written as a Javascript library layered on top of the
>>>> existing
>>>> API.
>>>
>>> But couldn't the same be said of the "fix" trap, frozen prototype, and
>>> frozen typeof for proxies ?
>>
>> As for the fix trap: there needs to be some way for a proxy to react to
>> freeze|seal|preventExtensions, even with your proposed extension.
>> As for frozen typeof: there have been proposals in the past of adding an
>> additional argument to Proxy.create to configure the proxy's |typeof| value
>> (still operating under the restrictions of ES5 11.4.3). If this
>> configurability turns out to be necessary in certain scenarios, I'm sure it
>> will be reconsidered.
>> As for frozen prototype: despite non-standard __proto__, the current
>> consensus is to move away from mutable prototypes, not to add standardized
>> features to support it.
>
> I was not implying that these invariants should not be enforced, but
> rather that if they are enforced, then non-configurable properties
> should be enforced as well, as not doing so seems arbitrary and thus
> confusing to users of the API.  As Allen mentioned, there are other
> important invariants that are not currently enforced, and I would make
> the same argument there, that either they should be enforced as well,
> or that nothing should be enforced.

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.

 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

David Bruant-4
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
[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
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 <[hidden email]>
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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
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
@David: One area of related work is that of Kiczales' metaobject protocols and more generally, "open implementations". Kiczales used the terminology of "rules of behavior" rather than "invariants". All of this work originated in dynamic languages (various flavors of Lisp in particular).

As for the conflicting issues of run-time enforcement and performance, in the MOP literature, a trick that is often applied is to use partial evaluation where possible. The use of partial evaluation makes a distinction between "load-time" and "run-time", allowing expensive conformance checks to be moved to "load-time". While this works great for structural invariants (e.g. ensuring that an inheritance graph is loop-free), it probably is no help at all for enforcing behavioral invariants (such as those associated with non-configurable properties).

(BTW, passing a function proxy's prototype as an argument to Proxy.createFunction ahead of time, and testing at proxy creation time whether it inherits from Function.prototype is one example of this technique. If one would opt for a getPrototypeOf trap, the check would need to be performed time and time again, at "run-time", so to speak).

Cheers,
Tom

2011/5/11 David Bruant <[hidden email]>
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
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
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 12/05/2011 14:40, Tom Van Cutsem a écrit :
> controversial invariant:
>
> - proxies can't emulate non-configurable properties. If they would,
> proxies could still update attributes of non-configurable properties.
>
Currently, proxies can emulate configurable properties (they are forced
to, but that's not my point). Still, a proxy can ignore
Object.defineProperty calls in the case of a re-configuration and also
ignore deletions, giving the impression that the property is
non-configurable even though Object.getOwnPropertyDescriptor would say
that configurable is set to "true".
Proxies being able to lie about configurability is the exact mirror of
proxies being able to lie about non-configurability. However, one is
allowed, the other isn't.

I need to think more about the rest of your message.
Good work on the invariant enumeration. It may be incomplete as you
point out, but that's a good start anyway.

Among the enforced invariants, maybe that "property names being strings"
could be added if that is enforced. If it is, it requries to cast all
property names arguments to strings before trap calls. However,
enforcing this invariant may require to do the same on the array
returned by get{Own}PropertyNames/enumerate/keys. (maybe that it should
rather be in the controversial part :-) )

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

Cameron McCormack-4
In reply to this post by Tom Van Cutsem-3
Tom Van Cutsem:
> invariants that are enforced:

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

Web IDL requires [[Class]] to take on values other than these two, for
example it should be "HTMLDivElement" for an HTMLDivElement object.  I
have a feeling that the web requires this, as opposed to just having a
custom toString function.

http://google.com/codesearch?q=Object.prototype.toString.call+lang:javascript
brings up some libraries that use Object.prototype.toString on DOM
objects.

Is it palatable to have proxies control [[Class]] or is there another
way we can help proxies over this hurdle?

--
Cameron McCormack ≝ http://mcc.id.au/
_______________________________________________
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 Thu, May 12, 2011 at 4:20 PM, Cameron McCormack <[hidden email]> wrote:
Tom Van Cutsem:
> invariants that are enforced:

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

Web IDL requires [[Class]] to take on values other than these two, for
example it should be "HTMLDivElement" for an HTMLDivElement object.  I
have a feeling that the web requires this, as opposed to just having a
custom toString function.

http://google.com/codesearch?q=Object.prototype.toString.call+lang:javascript
brings up some libraries that use Object.prototype.toString on DOM
objects.

Is it palatable to have proxies control [[Class]] or is there another
way we can help proxies over this hurdle?

Tom and I actually proposed this at an earlier EcmaScript meeting, for precisely this reason. Discussion revealed difficulties that led to increasing complexity, due to the use of [[Class]] as an internal nominal type test within the specification machinery. For example, all the Date methods implicitly test that their this.[[Class]] is "Date":

    15.9.5:
    > a TypeError exception is thrown if the this value is not an 
    > object for which the value of the [[Class]] internal property is "Date"

Do we want a proxy to be able to pass this test? If so, do we want a proxy to be able to provide the internal properties that the Date methods access? At the time at least, the proposal for [[Class]] control went down in flames as such questions accumulated. In light of Allen's recent thinking about meta-objects and reflection, we can certainly revisit this, but please not at the May meeting ;).


--
Cameron McCormack ≝ http://mcc.id.au/
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



--
    Cheers,
    --MarkM

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