Hi,
I will refer to Dean's proposal as "notification proxies" (where
traps essentially become notification callbacks), and will continue
to use "direct proxies" for the current design where the trap can
return a result (which is then verified).
These notification proxies remind me a lot of how one must implement
membranes with direct proxies. The general idea here is that the
proxy must use a "shadow" target as the proxy target, and the handler
must refer to the "real" wrapped target. Traps that have to do with
invariants (e.g. freeze/isFrozen, or querying a non-configurable
property descriptor) require the proxy to "synchronize" the state of
the real and shadow targets, because the proxy will verify the trap
result against the shadow target.
For such membranes, let's consider how direct proxies and
notification proxies trap an operation:
Direct proxies:
1) the proxy calls a trap on the handler
2) if the operation involves strong invariants on the real target,
the handler must synchronize real and shadow target
3) the trap returns a result
4) if the proxy detects that the operation involves strong
invariants, the trap result is verified against the shadow target
Notification proxies:
1) the proxy calls a trap on the handler (as a notification)
2) the handler must synchronize real and shadow target (regardless of
whether invariants are involved)
3) the trap returns no result
4) the proxy performs the intercepted operation on the shadow and
returns the result
I agree that the big benefit of notification proxies is that they get
rid of all the complex validation logic.
However, some reservations:
- if traps become mere notifications, perhaps their names should
change to reflect this, e.g. "notifyGetOwnPropertyNames" instead of
"getOwnPropertyNames". This is to alert handler writers that the
return value of these traps will be ignored.
- I think we do lose some expressiveness in the case of pure virtual
object abstractions that don't pretend to uphold any invariants.
With notification proxies, the handler must always (even in the case
of configurable properties) define concrete properties on the target.
Any virtual object abstraction is thus forced to maintain a "shadow"
which must eventually be represented as a plain Javascript object.
In other words: *all* virtual object abstractions, whether they
pretend to be frozen or not, have to make use of the "shadow target"
technique, with the burden of synchronizing the shadow upon each
operation. That burden currently doesn't exist for non-frozen virtual
object abstractions (I'm using "frozen" vs "non-frozen" here as a
shorthand for "has non-configurable/non-extensible invariants" vs
"has no such invariants").
- Regarding the overhead of the getOwnPropertyNames trap having to
create a defensive copy of the trap result:
With notification proxies, upon trapping "notifyGetOwnPropertyNames":
1) the trap must define all properties it wants to return on the
target. If there are N properties, there is an O(N) physical storage
cost involved.
2) the proxy applies the built-in Object.getOwnPropertyNames to the
target. Assuming the target is a normal object, the primitive
allocates a fresh array and returns the N properties.
In the current design:
1) the trap returns an array of property names (requiring O(N)
physical storage cost)
2) the proxy copies and verifies this array
I think the storage costs are largely the same. However, with
notification proxies, if the properties were "virtual", those
properties do linger as "concrete" properties on the target. Yes, the
handler can delete them later, but when is later? Should the handler
schedule clean-up actions using setTimeout(0)? This somehow does not
feel right.
I like the simplicity of notification proxies, but we should think
carefully what operations we turn into notifications only.
More generally, notification proxies are indeed
"even-more-direct-proxies". They make the "wrapping" use case
(logging, profiling, contract checking, etc.) simpler, at the expense
of "virtual objects" (remote objects, test mock-ups), which are
forced to always "concretize" the virtual object's properties on a
real Javascript object.
Cheers,
Tom
2012/11/25 Mark S. Miller <[email protected]
<mailto:[email protected]>>
+1. I think this is a really effective extension of the direct proxy
approach, and I don't know why we didn't see it earlier. It's weird
that this preserves all the flexibility of fully virtual configurable
properties even though it insists that even these be made into real
properties on the target. The trick is that placing a configurable
property on the target doesn't commit the handler to anything, since
the handler can remove or change this property freely as of the next
trap.
Apologies again for not yet having the time to do more than skim the
thread at this point. But IIRC someone already suggested a similar
change to some of the imperative traps -- perhaps freeze, seal, and
preventExtensions. The handler would not perform these operations on
the target, only to have the proxy check it. Rather, if the trap
indicates that the operation should succeed, the proxy then simply
performs the operation -- or else throws. I wonder if this philosophy
could be extended to some of the other imperative operations as well?
What expressiveness does this even-more-direct proxy approach lose?
AFAICT, not much. At the same time, it should result in a *much*
simpler implementation and much greater confidence that invariants of
the non-proxy sublanguage are preserved by the introduction of
proxies. In fact, I think it's much stronger on invariant
preservation
that the current direct proxies, while being much simpler.
On Sat, Nov 24, 2012 at 6:49 PM, Dean Tribble <[email protected]
<mailto:[email protected]>> wrote:
> I am looking forward to proxies in JavaScript, and had a
thought on the
> issues below. You could extend the the "direct proxy" approach
for this.
>
> When the Proxy receives getOwnPropertyNames, it
> 1) notifies the handler that property names are being requested
> 2) the handler adds/removes any properties (configurable or
otherwise
> subject to the normal constraints) on the target
> 3) upon return, the proxy invokes getOwnPropertyNames directly
on the target
> (e..g, invoking the normal system primitive)
>
> This approach appears to have consistent behavior for
configurability and
> extensibility. For example, the trap operation above could add
configurable
> properties to an extensible target, and remove them later. It
could add
> non-configurable properties, but they are permanent once added,
etc. Thus
> there's no loss of generality. In addition to optionally
setting up
> properties on the target, the handler trap above would need to
indicate to
> the proxy (via exception or boolean result) that the
getOwnPropertyNames
> operation should proceed ahead or fail.
>
> This extension of the "direct proxy" approach applies to all query
> operations, eliminates the copying and validation overhead
discussed below,
> simplifies the implementation, retains full backwards
compatibility, and
> enables most if not all the expressiveness we might expect for
proxies.
>
> Dean
_______________________________________________
es-discuss mailing list
[email protected] <mailto:[email protected]>
https://mail.mozilla.org/listinfo/es-discuss