custom proto arg for Proxy.createFunction?

David Bruant bruant at enseirb-matmeca.fr
Wed Feb 23 16:38:36 PST 2011


Le 23/02/2011 23:54, David Herman a écrit :
>> With your optional argument, I see a second solution that could be
>> consistent. The prototype chain could contain the provided prototype
>> then Function.prototype ("obj --> proto --> Function.prototype -->
>> null"  as opposed to your proposition which is: "obj --> proto --> null"
>> ). Hence, there would be no need to enforce anything: instanceof would
>> naturally find Function.prototype in the chain and function proxies
>> would still be functions.
> I don't quite see how that works; prototype inheritance is inherently singly-linked. If the user specifies a prototype object, it already has its own single prototype -- and we certainly aren't going to spec something that *mutates* its existing prototype.
>
> I suppose you could have some sort of multiple inheritance chain semantics that says "follow this chain to the end, then when you're done, here's another chain." But that seems kind of crazy.
>
> You could spec something a bit more ad hoc, just for the purposes of inheritance: that no matter what object the user provides, `instanceof' always says true for Function.prototype as well. I don't have a concrete argument against it, but it still looks funny to me.
I'm sorry, I've been a little bit crazy. I was wrong.
I thought for a minute, that prototype chain appending was possible and
I was wrong.

> The simple instanceof check seems simpler and reasonably intuitive.
By "hardcoding" p instanceof Function === true, you're breaking your
user expectation of finding the Function.prototype methods ("call",
"apply" or any further addition) in the object.
In current function proxies, they are "available", I have "fixed" your
example below (and explained why I quote both "available" and "fixed").

>> With your solution, by removing Function.prototype from the chain, proxy
>> functions couldn't be .call()-ed or .apply()-ed
> That's already true, even without my extension:
>
>     js> var proxy = Proxy.createFunction({}, function(){}, function(){})
>     js> proxy.call(this)   
>     typein:11: TypeError: getPropertyDescriptor is not a function
The bug is in your code:
When you do "proxy.call(this)", the "proxy.call" getter is trapped as
the "get" trap. Since you do not define it (your handler objectis
empty), the default get trap is called (see here :
http://wiki.ecmascript.org/doku.php?id=harmony:proxies#trap_defaults).
In this default get trap, the first thing that is done is a call to
this.getPropertyDescriptor ("this" refers to the handler object which is
empty in your example). And here, the error is thrown by the engine
since handler.getPropertyDescriptor isn't a function because it's undefined.

You need to provide (at least) all fundamental traps in your proxies to
make them work in all situations.
(https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Proxy#Common_mistakes_and_misunderstanding)

The following code is ugly, but works:
var proxy = Proxy.createFunction({getOwnPropertyDescriptor:
function(name) {},
   getPropertyDescriptor:  function(name) {},
   getOwnPropertyNames: function() {},
   getPropertyNames: function() {},
   defineProperty: function(name, desc) {},
   delete:       function(name) {},  
   fix:          function() {},
   get: function(rec, name){
              if(name=='call')
                  return Object.getPrototypeOf(proxy).call;}
   }, function(){alert('called');}, function(){});
proxy.call(); // alerts 'called'

In this example, since the other traps (fundamental or derived) aren't
used (that's why I've used stub methods), they don't have to be
implemented, but if your proxy is planned on being used in the wild,
it's safer.

This code is ugly, because my get handler methods assumes that the proxy
object is reachable from the lexical scope which could not be the case
(I could have used the "rec" argument, but that's not very clean). There
is an ongoing discussion to make the proxy object available as an
argument in each trap. This way, the get trap could be rewritten as:
get: function (rec, name, proxy){
    return Object.getPrototypeOf(proxy)[name];
    // Object.getPrototypeOf(proxy) is Function.prototype by definition here
}
This is actually one more argument in the direction of making this happen.

Regards,

David


More information about the es-discuss mailing list