Converting an existing object to a proxy

# Cormac Flanagan (14 years ago)

[documenting/expanding some ideas briefly discussed at today's meeting]

The current proxy proposal has a method to create a new proxy:

var proxy = Proxy.create(handler, proto);

We could extend this proposal to allow an existing object to be converted to a proxy, via:

var proxy = Proxy.createFrom(object, handler, proto);

Here, the return value 'proxy' is the same address as the argument 'object'. The original object thus becomes a proxy. Any state of the original object is discarded.

This extension appears to support additional applications, such as registering an observer on an existing object. The target object would first be cloned, then the target object would be converted into a proxy that dispatches to the cloned object, but which also notifies observers about accesses/updates to the (now proxified) object.

There are a number of open issues relating to security etc: In particular, what objects can be proxified in this way - perhaps not frozen object, or objects with non-configurable properties or with unique names.

A design goal is that for any object that could be proxified, we can replace it with a proxy in a way that is semantically transparent.

# Mark S. Miller (14 years ago)

On Thu, May 26, 2011 at 5:04 PM, Cormac Flanagan <cormac at cs.ucsc.edu> wrote:

[documenting/expanding some ideas briefly discussed at today's meeting]

The current proxy proposal has a method to create a new proxy:

var proxy = Proxy.create(handler, proto);

We could extend this proposal to allow an existing object to be converted to a proxy, via:

var proxy = Proxy.createFrom(object, handler, proto);

Here, the return value 'proxy' is the same address as the argument 'object'. The original object thus becomes a proxy. Any state of the original object is discarded.

This extension appears to support additional applications, such as registering an observer on an existing object. The target object would first be cloned, then the target object would be converted into a proxy that dispatches to the cloned object, but which also notifies observers about accesses/updates to the (now proxified) object.

There are a number of open issues relating to security etc: In particular, what objects can be proxified in this way - perhaps not frozen object, or objects with non-configurable properties or with unique names.

In today's meeting, I made two suggestions along these lines:

  • Given the current proxy semantics, we should allow this only if the object-to-be-proxified is extensible and has no non-configurable own properties.
  • We have on occasion discussed modifying the proxy proposal so that individual properties could be fixed rather than just the proxy as a whole. (Note: I am not in favor of such a change, but it could be done soundly.) Given that this change to proxies were done, then we should allow proxification only if the object-to-be-proxified is extensible, period.

In both cases, as you state, one effect of the operation is to remove all configurable own properties from the object. In both cases, we can adopt the rationale that the object-to-be-proxified could not have taken any action inconsistent with it always having been a proxy.

In both cases, we need the further restriction that it is a kind of object that can be emulated by a proxy. Today, this is technically only objects of [[Class]] "Object" or "Function", but we're talking about relaxing that in any case.

# David Bruant (14 years ago)

Le 27/05/2011 04:13, Mark S. Miller a écrit :

On Thu, May 26, 2011 at 5:04 PM, Cormac Flanagan <cormac at cs.ucsc.edu <mailto:cormac at cs.ucsc.edu>> wrote:

[documenting/expanding some ideas briefly discussed at today's
meeting]

The current proxy proposal has a method to create a new proxy:

var proxy = Proxy.create(handler, proto);

We could extend this proposal to allow an existing object to be
converted to a proxy, via:

var proxy = Proxy.createFrom(object, handler, proto);

I am surprised by this proposal since a design goal of proxies was to not be able to turn existing objects into proxies. 4th "driving force" of the proxy proposal: "security: avoid enabling arbitrary ES objects to be able to intercept the properties of another object"

The API itself (Proxy.create, Proxy.createFunction) seems to have been design with this in mind.

Here, the return value 'proxy' is the same address as the argument
'object'.
The original object thus becomes a proxy. Any state of the
original object
is discarded.

The notion of object "state" is not defined anywhere in the ES5 spec. What is your definition of an "object state" ?

This extension appears to support additional applications, such as
registering an observer on an existing object. The target object would
first be cloned, then the target object would be converted into a
proxy that
dispatches to the cloned object, but which also notifies observers
about
accesses/updates to the (now proxified) object.

Is this the only use case? If so, would it rather make sense to provide a specific Object event API for existing objects? It would allow to observe accesses/updates without turning the object into a proxy with arbitrary handler methods.

There are a number of open issues relating to security etc:
In particular, what objects can be proxified in this way - perhaps not
frozen object,
or objects with non-configurable properties or with unique names.

In today's meeting, I made two suggestions along these lines:

  • Given the current proxy semantics, we should allow this only if the object-to-be-proxified is extensible and has no non-configurable own properties.
  • We have on occasion discussed modifying the proxy proposal so that individual properties could be fixed rather than just the proxy as a whole. (Note: I am not in favor of such a change, but it could be done soundly.) Given that this change to proxies were done, then we should allow proxification only if the object-to-be-proxified is extensible, period.

In both cases, as you state, one effect of the operation is to remove all configurable own properties from the object. In both cases, we can adopt the rationale that the object-to-be-proxified could not have taken any action inconsistent with it always having been a proxy.

In both cases, we need the further restriction that it is a kind of object that can be emulated by a proxy. Today, this is technically only objects of [[Class]] "Object" or "Function", but we're talking about relaxing that in any case.

A design goal is that for any object that could be proxified,
we can replace it with a proxy in a way that is semantically
transparent.

If you provide the ability to put any functions in the handler object, there in no such thing as "semantically transparent". The handler methods can do whatever they wants; they are functions. Moreover with an API like Proxy.createFrom(object, handler, proto), the handler methods would need some help to initialize their internal proxy state in order to align with the object passed as argument.

Maybe that what you were suggesting was to use a forwarding handler by default as handler and to put the handler argument as "this.target"?

Even in this case, if creating proxies, an external user of the same object has no guarantee whatsoever of seeing no semantic difference since, for instance, any handler trap could throw an error when called.

My very first (mis)understanding of proxies was that they were an object access/update event API and that the handler functions were nice event handler. They are not. They are much more powerful. They are not an /addition/ to regular internal object methods. They are a /replacement/ of it.

However, I agree that an object event API is a valid use case (I have actually had a need for that a couple of days ago), but I don't think it should be a proxy extension. In my opinion, it should be its own library/API. Maybe implementable/standardizable with proxies + "become" internal function (see slide 56 at [1]), but its own thing.

I would be much more in favor of something like:

Object.observe(o, observers);

Observers would be an object that looks like a proxy handler called on every object interactions, but wouldn't be able to interact with the object internal methods. It would just be an addition to internal methods rather than a replacement as it's the case with proxies. There wouldn't be any restriction based on property attributes (enumerable, configurable, get, set...) or extensibility. One question to discuss is to know if they should be called before or after (or one observer method for each?) internal calls.

Of course, if there is another use case than object access/update events, maybe that turning existing objects into proxies is a good idea, but if there isn't, I think that a specific API may be safer.

David

[1] www.slideshare.net/BrendanEich/metaprog-5303821?from=ss_embed

# Brendan Eich (14 years ago)

On May 27, 2011, at 2:26 AM, David Bruant wrote:

Le 27/05/2011 04:13, Mark S. Miller a écrit :

On Thu, May 26, 2011 at 5:04 PM, Cormac Flanagan <cormac at cs.ucsc.edu> wrote: [documenting/expanding some ideas briefly discussed at today's meeting]

The current proxy proposal has a method to create a new proxy:

var proxy = Proxy.create(handler, proto);

We could extend this proposal to allow an existing object to be converted to a proxy, via:

var proxy = Proxy.createFrom(object, handler, proto); I am surprised by this proposal since a design goal of proxies was to not be able to turn existing objects into proxies. 4th "driving force" of the proxy proposal: "security: avoid enabling arbitrary ES objects to be able to intercept the properties of another object"

That is not the same as "avoid turning an object into a proxy", though.

The idea of permitting this "becomes" operation only for extensible objects with configurable properties is crucial.

The motivation is to unify ideas in proxies (in Harmony) with those proposed in strawman:observe.

# Tom Van Cutsem (14 years ago)

Since I gather this proposal is meant primarily to support "observable" objects, two remarks:

  1. Are proxies the appropriate building block performance-wise? From the notes, it seems that current proxies are not a good building block because of performance issues. Are the performance issues related to the membraning needed for maintaining object identity, or are they more fundamental?

  2. IIUC, one of the more interesting uses of observables would be to make e.g. DOM nodes observable. That raises the issue of whether Proxy.createFrom can be applied to Host objects, and of so, how to deal with built-in [[...]] properties.

I tend to agree with David about having a separate observable API, if that is indeed the driving use case. That API can be very similar to the Proxy API though. Racket's "chaperones" come to mind: a chaperone is a strictly less powerful kind of proxy, one that is not allowed to arbitrarily change the result of the original operation (and IIRC, a chaperone also cannot stop the original operation from taking place on the target object).

Cheers, Tom

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

# Tom Van Cutsem (14 years ago)

I tend to agree with David about having a separate observable API, if that is indeed the driving use case. That API can be very similar to the Proxy API though. Racket's "chaperones" come to mind: a chaperone is a strictly less powerful kind of proxy, one that is not allowed to arbitrarily change the result of the original operation (and IIRC, a chaperone also cannot stop the original operation from taking place on the target object).

Spoke too soon: from < web.mit.edu/drracket_v501/share/racket/doc/reference/chaperones.html>

I learn that chaperones can throw exceptions, and thus presumably abort an intercepted operation early. Still, the idea of a "tamed" kind of proxy is worth exploring.

# David Bruant (14 years ago)

Le 27/05/2011 11:35, Brendan Eich a écrit :

On May 27, 2011, at 2:26 AM, David Bruant wrote:

Le 27/05/2011 04:13, Mark S. Miller a écrit :

On Thu, May 26, 2011 at 5:04 PM, Cormac Flanagan <cormac at cs.ucsc.edu <mailto:cormac at cs.ucsc.edu>> wrote:

[documenting/expanding some ideas briefly discussed at today's
meeting]

The current proxy proposal has a method to create a new proxy:

var proxy = Proxy.create(handler, proto);

We could extend this proposal to allow an existing object to be
converted to a proxy, via:

var proxy = Proxy.createFrom(object, handler, proto);

I am surprised by this proposal since a design goal of proxies was to not be able to turn existing objects into proxies. 4th "driving force" of the proxy proposal: "security: avoid enabling arbitrary ES objects to be able to intercept the properties of another object"

That is not the same as "avoid turning an object into a proxy", though.

I actually thought that it's what it meant, but you're right. I think it should be clarify, because the way the API is design, it's quite clear that only new objects can be proxies. In the presentation you gave, it's clear that proxies can only go from "trapping" to "fixed" and that there is no way to go in the opposite direction

The idea of permitting this "becomes" operation only for extensible objects with configurable properties is crucial.

Is it? The first time I heard about the "become" operation was in the context of the proxy fix trap. Second time was this message. It doesn't sounds crucial yet to me.

The motivation is to unify ideas in proxies (in Harmony) with those proposed in strawman:observe.

I hadn't read this proposal yet. Thanks.

I think that this discussion raises the following question: Should all usages of the "become" operation be associated with proxies?

I would answer no because they sound like orthogonal concerns, but I'm interested in listening to arguments saying otherwise.

# Sam Tobin-Hochstadt (14 years ago)

On Fri, May 27, 2011 at 5:45 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

I tend to agree with David about having a separate observable API, if that is indeed the driving use case. That API can be very similar to the Proxy API though. Racket's "chaperones" come to mind: a chaperone is a strictly less powerful kind of proxy, one that is not allowed to arbitrarily change the result of the original operation (and IIRC, a chaperone also cannot stop the original operation from taking place on the target object).

Spoke too soon: from web.mit.edu/drracket_v501/share/racket/doc/reference/chaperones.html I learn that chaperones can throw exceptions, and thus presumably abort an intercepted operation early. Still, the idea of a "tamed" kind of proxy is worth exploring.

I believe that the semantics of observers allow for throwing exceptions, and thus terminating an operation, at least according to the wiki page.

# Boris Zbarsky (14 years ago)

On 5/27/11 5:40 AM, Tom Van Cutsem wrote:

  1. IIUC, one of the more interesting uses of observables would be to make e.g. DOM nodes observable. That raises the issue of whether Proxy.createFrom can be applied to Host objects, and of so, how to deal with built-in [[...]] properties.

How useful would observables be for DOM nodes?

I guess you could notice expandos being added or removed, but that's about it, right?

# Brendan Eich (14 years ago)

On May 27, 2011, at 3:04 AM, David Bruant wrote:

The idea of permitting this "becomes" operation only for extensible objects with configurable properties is crucial. Is it? The first time I heard about the "become" operation was in the context of the proxy fix trap. Second time was this message. It doesn't sounds crucial yet to me.

The "crucial" applied to both "only for extensible objects with configurable properties" in that sentence. ;-)

The issue as I understand it is not becomes per se (more below), rather that an extensible object with configurable (own) properties could have those properties wrapped by accessors to emulate some of the intercessive power of proxies. From the security point of view, you want frozen and sealed objects, at least, to be immune to proxification because they are immune to such property-by-property wrapping with accessors, today.

See Mark's reply on the list dated 7:33pm yesterday.

The motivation is to unify ideas in proxies (in Harmony) with those proposed in strawman:observe. I hadn't read this proposal yet. Thanks.

I think that this discussion raises the following question: Should all usages of the "become" operation be associated with proxies?

We are not exposing becomes in the language -- certainly not for two arbitrary live objects.

In both the proxy fix case, and in this Proxy.createFrom case, one of the two objects is a newborn, not yet accessible. We brain-transplant between the accessible and inaccessible, swapping the objects' data. The identity is preserved but the internal and own properties are exchanged.

Questions about the proxify-existing-object case:

  1. Won't the proxy want the original "brain", the one transplanted into a newborn proxy (now "become" the regular object)?

  2. Will the costs here be acceptable compared to something unstratified requiring no extra object, a la strawman:observe?

# Sean Eagan (14 years ago)

I think the separation between proxies and regular objects is too much. I would prefer to think of proxies as just a special kind of object. All objects could be thought of as having a handler ( [[Handler]] ), and "proxy objects" could be those objects whose handler is an actual ES object as opposed to an abstract language specification object. This would be similar to the distinction betweeen "data" and "accessor" properties, since you could think of a default abstract [[Set]] and [[Get]] implementation for property descriptors. Then fixing a proxy object could just be thought of as setting the object's [[Handler]] to an abstract specification handler, and Proxy.createFrom can just be thought of as setting the object's [[Handler]] to an actual ES object. The abstract [[Handler]] could either be implemented via pseudocode similar to the ES5 spec or by actual ES code if a testable / executable object spec is desired.

I've been working on a concept called "object descriptors" which are an object level equivalent of property descriptors. One of the object descriptor attributes I'm including is "handler". Object descriptors could also have a "configurable" attribute which could control whether an object's "handler" (and other attributes) can be changed.

# Sean Eagan (14 years ago)

On Fri, May 27, 2011 at 10:10 AM, Sean Eagan <seaneagan1 at gmail.com> wrote:

I think the separation between proxies and regular objects is too much.  I would prefer to think of proxies as just a special kind of object.  All objects could be thought of as having a handler ( [[Handler]] ), and "proxy objects" could be those objects whose handler is an actual ES object as opposed to an abstract language specification object.

We could instead say that all objects have a [[DefaultHandler]] which contains the default trap implementations. Then object's that have a [[Handler]] are "proxy objects". For trap resolution if an object has a [[Handler]] and the [[Handler]] has the given trap, invoke it. If an object does not have a [[Handler]] or [[Handler]] does not have the given trap, then, for derived traps invoke the object's [[DefaultHandler]]'s trap, for fundamental traps perform the abstract spec defined pseudocode for the given internal method. Then proxy fixing would mean that the object will no longer have a [[Handler]], and "Proxy.createFrom" means that the object will now have a [[Handler]].

We could also define default handler's for object types which override certain internal methods such as Arrays and Functions. These would inherit the trap implementations from Object's default handler.

Thanks, Sean Eagan

# Brendan Eich (14 years ago)

On May 27, 2011, at 8:10 AM, Sean Eagan wrote:

I think the separation between proxies and regular objects is too much. I would prefer to think of proxies as just a special kind of object. All objects could be thought of as having a handler ( [[Handler]] ), and "proxy objects" could be those objects whose handler is an actual ES object as opposed to an abstract language specification object.

This is a way of thinking about proxies in the context of JS, for sure. Please see this slide

brendaneich.com/brendaneich_content/uploads/selective-interception.png

from Tom and Mark.

This would be similar to the distinction betweeen "data" and "accessor" properties, since you could think of a default abstract [[Set]] and [[Get]] implementation for property descriptors. Then fixing a proxy object could just be thought of as setting the object's [[Handler]] to an abstract specification handler, and Proxy.createFrom can just be thought of as setting the object's [[Handler]] to an actual ES object. The abstract [[Handler]] could either be implemented via pseudocode similar to the ES5 spec or by actual ES code if a testable / executable object spec is desired.

The problem is making the "VM handlers" (to use the slide's terms) concrete, not abstract. No VM implementor wants that. Nor do security folks -- at least not read/write VM handler access.

When we first started discussing "catch-alls", before Proxies were proposed, TC39 members stopped progress on catch-alls by citing the "climbing the meta ladder" problem. This is where the spec and real code, instead of bottoming out at unspoofable core semantics, trap back into "user code" doing meta-programming of arbitrary objects.

This is why VM implementors object. They can't optimize as well if they have to check more potentially varying "invariants of ES1-5". This is why Proxies are a special kind of object.

The security issue is similar. If an attacker can vary invariants the VM needs to enforce via a built-in handler to preserve integrity or other safety properties, that's a security hole. It may be that frozen and sealed objects being immune is enough, for some operations such as watching gets and sets. As noted, for a non-sealed object, those could be done by wrapping each property in an accessor after transplanting it to an inner object or a peer property with a private name. But intercession at the VM handler layer may be strictly more powerful than get, set, or even delete trapping.

I've been working on a concept called "object descriptors" which are an object level equivalent of property descriptors. One of the object descriptor attributes I'm including is "handler". Object descriptors could also have a "configurable" attribute which could control whether an object's "handler" (and other attributes) can be changed.

This helps but the VM implementors may still object to reflecting currently-hidden VM handlers. There may be security issues we are missing. And unless the built-in objects of Clause 15 all have non-configurable handlers, you are talking about climbing the meta ladder in the spec.

# Sean Eagan (14 years ago)

Thanks, I wasn't aware of that history.

I'm not suggesting that default handlers or traps even should exist from an implementation perspective, just that they could be a nice specification mechanism now that we have proxies. Otherwise we need to separately maintain pseudocode and ES versions of the derived traps. The default handlers would specify the required effective behavior of internal methods corresponding to derived traps. Each of the internal spec methods would still be abstract, and allowed to perform additional behavior, but internal methods corresponding to derived traps would need to also perform the behavior specified by the default trap when the object does not have a [[Handler]] with that trap. It would need to behave as if the original bindings of the builtin objects referenced within the default trap implementation were used, except that even if the implementation allows property access watching, no such notifications would be sent for property access on builtins that occur within the default trap's code.

# Sean Eagan (14 years ago)

On Fri, May 27, 2011 at 12:18 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

Thanks, I wasn't aware of that history.

I'm not suggesting that default handlers or traps even should exist from an implementation perspective, just that they could be a nice specification mechanism now that we have proxies.  Otherwise we need to separately maintain pseudocode and ES versions of the derived traps.

Of course you could go the other way, and remove the default trap implementations. All objects could have the same internal method implementation regardless of whether or not they are a proxy. Within each internal method there could be a check as to whether the object is a proxy (has a [[Handler]]) AND has the trap, if so, it is invoked, otherwise the default pseudocode is performed.

Thanks, Sean Eagan

# Andreas Rossberg (14 years ago)

I'm puzzled about this idea. I thought that one of the main design goals of proxies was that they are transparent, i.e. you cannot tell a proxy apart from a proper object. How can this be maintained for Proxy.createFrom? AFAICS, there is no way you can perform this operation on an object that already is a proxy.

# Cormac Flanagan (14 years ago)

On Fri, May 27, 2011 at 2:26 AM, David Bruant <david.bruant at labri.fr> wrote:

On Thu, May 26, 2011 at 5:04 PM, Cormac Flanagan <cormac at cs.ucsc.edu> wrote:

A design goal is that for any object that could be proxified, we can replace it with a proxy in a way that is semantically transparent.

If you provide the ability to put any functions in the handler object, there in no such thing as "semantically transparent". The handler methods can do whatever they wants; they are functions.

What I meant was that by writing the handler functions in the appropriate way, we can create a proxy that is semantically transparent -- not that all proxies must be semantically transparent.

# Sean Eagan (14 years ago)

On Fri, May 27, 2011 at 12:43 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

Of course you could go the other way, and remove the default trap implementations.  All objects could have the same internal method implementation regardless of whether or not they are a proxy.  Within each internal method there could be a check as to whether the object is a proxy (has a [[Handler]]) AND has the trap, if so, it is invoked, otherwise the default pseudocode is performed.

The pseudocode for checking if the object has a [[Handler]], and if that [[Handler]] has a trap, and then invoking the trap could all be factored out into a [[Trap]] (or something) internal method, which takes a trap name, and a list of arguments to pass to the trap, and returns whether or not the object has the trap, and if so, also a return value from the trap. This would make for a very concise and clear spec with regard to proxies, and it would be relatively easy to update the current proposal to do this.

Thanks, Sean Eagan

# Sean Eagan (14 years ago)

On Fri, May 27, 2011 at 1:05 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

On Fri, May 27, 2011 at 12:43 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

Of course you could go the other way, and remove the default trap implementations.  All objects could have the same internal method implementation regardless of whether or not they are a proxy.  Within each internal method there could be a check as to whether the object is a proxy (has a [[Handler]]) AND has the trap, if so, it is invoked, otherwise the default pseudocode is performed.

The pseudocode for checking if the object has a [[Handler]], and if that [[Handler]] has a trap, and then invoking the trap could all be factored out into a [[Trap]] (or something) internal method, which takes a trap name, and a list of arguments to pass to the trap, and returns whether or not the object has the trap, and if so, also a return value from the trap.  This would make for a very concise and clear spec with regard to proxies, and it would be relatively easy to update the current proposal to do this.

For expository purposes it might still be useful to have actual ES versions of the default pseudocode sections of the internal methods somewhere. This could either be via a non-normative addendum to the spec, or left up to the community to provide outside the spec. The latter option might be better since it could be updated independent of the spec in case of any bugs found.

Thanks, Sean Eagan

# Sean Eagan (14 years ago)

On Fri, May 27, 2011 at 1:05 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

On Fri, May 27, 2011 at 12:43 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

Of course you could go the other way, and remove the default trap implementations.  All objects could have the same internal method implementation regardless of whether or not they are a proxy.  Within each internal method there could be a check as to whether the object is a proxy (has a [[Handler]]) AND has the trap, if so, it is invoked, otherwise the default pseudocode is performed.

The pseudocode for checking if the object has a [[Handler]], and if that [[Handler]] has a trap, and then invoking the trap could all be factored out into a [[Trap]] (or something) internal method, which takes a trap name, and a list of arguments to pass to the trap, and returns whether or not the object has the trap, and if so, also a return value from the trap.  This would make for a very concise and clear spec with regard to proxies, and it would be relatively easy to update the current proposal to do this.

[[Trap]] would also need to take a boolean "throw" argument as to whether it should throw if the object has the handler, but does not have the trap. This argument would be set to true for fundamental traps, and false for derived traps. It would not need to take an argument corresponding to the trap "proxy" argument since that could be appended within [[Trap]] itself.

Thanks, Sean Eagan

# Andreas Rossberg (14 years ago)

On 27 May 2011 19:58, Cormac Flanagan <cormac at cs.ucsc.edu> wrote:

What I meant was that by writing the handler functions in the appropriate way, we can create a proxy that is semantically transparent -- not that all proxies must be semantically transparent.

Well, but how would I create a proxy that is transparent w.r.t. invoking Proxy.createFrom on it?

# LungZeno (14 years ago)

On Sat, May 28, 2011 at 2:05 AM, Sean Eagan <seaneagan1 at gmail.com> wrote:

On Fri, May 27, 2011 at 12:43 PM, Sean Eagan <seaneagan1 at gmail.com> wrote:

Of course you could go the other way, and remove the default trap implementations.  All objects could have the same internal method implementation regardless of whether or not they are a proxy.  Within each internal method there could be a check as to whether the object is a proxy (has a [[Handler]]) AND has the trap, if so, it is invoked, otherwise the default pseudocode is performed.

The pseudocode for checking if the object has a [[Handler]], and if that [[Handler]] has a trap, and then invoking the trap could all be factored out into a [[Trap]] (or something) internal method, which takes a trap name, and a list of arguments to pass to the trap, and returns whether or not the object has the trap, and if so, also a return value from the trap.  This would make for a very concise and clear spec with regard to proxies, and it would be relatively easy to update the current proposal to do this.

Another way, every object has a [[Proxify]] internal method corresponding to Object.createFrom. Host objects have their [[Proxify]] different to native objects. [[Proxify]] can also be trapped w.r.t. invoking Proxy.createFrom(aProxy, handler, proto).