Notification proxies (Was: possible excessive proxy invariants for Object.keys/etc??)
dtribble at gmail.com
Wed Nov 28 15:13:56 PST 2012
On Wed, Nov 28, 2012 at 1:09 PM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:
> 2012/11/26 Dean Tribble <dtribble at gmail.com>
>> I agree. My usual expectation for proxies is to support remote and
>> persistent objects. While supporting other scenarios is great, usually
>> that's incidental. Is there a broader list of aspirations for proxies? or
>> is this just a "all else being equal it would be good if we can do this"?
> Let's talk about aspirations for proxies. It will help us set priorities.
> First, some terminology (originating from CLOS, the "mother of all MOPs"
As a general point, I encourage you to look for other inspiration than CLOS
MOP for doign proxies (whose mother was really InterlispD). Meta-level
access deeply impacts security,maintainability,
reliability, understandability, etc. The tighter and more structured you
can make your meta-level access, the easier it will be to to implement,
use, and maintain (e.g., both coroutines and downward functions are more
understandable, easier to implement, easier to secure, etc. than general
continuations and call-cc).
> CLOS method combinations allow a composer to distinguish between "before",
> "after" and "around"-style composition:
> - "before"-style wrapping gives you only the ability to get notified
> before an operation happens. You can abort, but not change, the result of
> the operation. This is what notification-proxies offer.
You *can* change the result of the operation. You do so by modifying the
state before the operation proceeds, of course. You could also extend the
notification support to notify after so you could clenup (avoiding a
> - "after"-style wrapping allows you to get notified of an operation
> after-the-fact. Depending on the API, the "after"-wrapper may or may not
> get to see the outcome of the operation, and may or may not change the
> final outcome passed on to clients.
> - "around"-style wrapping is the most general and allows the composer to
> decide if and when to forward, and what result to return. It subsumes
> before/after wrapping. This is what direct proxies currently provide.
It does not subsume before/after wrapping, because it loses the integrity
of before/after (e.g., the wrapper can lie and cheat, where the before and
after cannot). That may be worth it, but it is substantially different.
Another variant is the "differential" version: the "differential" trap is
like a notification, but it can also return virtual additions (or an
iterator of additions). The proxy then invokes the primitive on the
target, and appends (with de-dupping, etc.) the virtual additions. This
allows the simple case to just use hte target, but also allows all of
Allen's additional cases.
> As far as I can tell, virtual object abstractions like remote/persistent
> objects require "around"-style wrapping, because there's otherwise no
> meaningful target to automatically forward to.
I thought the target in that case is an internal object to represent or
reify the meta-state of the remote or persistent object. I think that still
makes sense in both the persistent object and remote object cases.
> Here's a list of use cases that I frequently have in mind when thinking
> about proxies, categorized according to whether the use case requires
> before/after/around wrapping:
> Virtual objects, hence "around"-style:
> - self-hosting "exotic" objects such as Date, Array (i.e. self-host an
> ES5/ES6 environment)
> - self-hosting DOM/WebIDL objects such as NodeList
I should note that I'm not advocating a notification-only style for all
your proxy needs; having get operations able to generate virtual results
makes lots of sense. I primary suggest it for operations that are currently
implemented by the system (i.e., user code cannot normally intervene) and
that might be relied on for security-relevant behavior. wrapping return
results of user operations in a proxy makes perfect sense to me.
> Around-style wrapping (need to be able to change the result of an
> - membranes
> - higher-order contracts
> Before-style wrapping:
> - revocable references
You can validate arguments, the state of the destination object (e.g., if
you were implementing a state machine), logging
> What else?
There is the pattern derived from the meter pattern in KeyKOS: the handler
is only invoked on exception (e.g., like a page fault). For example, a
primitive stream gets read operations against. Normally they proceed as a
primitive against an implementation-provided buffer so that "next" is
really darned fast. When the buffer is exhausted, instead of throwing an
error to the caller, the error is thrown to the handler (called a keeper)
which goes through some user-defined effort to refill the buffer, then the
read is retried. This allows most data transfer to such a stream to use
fast, batch-oriented primitives, while supporting an arbitrary source of
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss