On Oct 14, 2010, at 11:57 AM, Tom Van Cutsem wrote:

> It is, however, debatable whether it is appropriate to override `o.n` with 
> the external function just because it does not exist on o2. After all, the 
> proxy can handle missing methods. Presumably, the code in the else-branch is 
> going to make use of `o.n` (either as a funarg, or it may call it as a method 
> `o.n(...)`. This will not crash, the proxy will deal with it. It's not clear 
> that overriding the `n` method with the function of the then-branch is the 
> right thing to do. Normally such feature-testing is done to make sure that 
> later calls to `o.n(...)` won't crash. When using proxies that deal with 
> missing methods, calling `o.n(...)` won't crash the code, so why should the 
> method be replaced?

Tom, thanks for this, assuming it is an accurate reading of Dmitry's post, your 
reply clarifies something for me. When we bridged Java to JS in the '90s, we 
had to deal with Java objects having overloaded members. A member o.m could be 
an int field or a String -> int method, say. And methods could be overloaded to 
take different argument types.

We implemented something like proxies in C code, and for any overloaded field, 
we returned a proxy that could access the int field or call the 
String-returning method, depending on context. If called, we tried the method. 
If got or set with a number value, we accessed the field. For overloaded 
methods we tried disambiguated by JS actual argument type, but this could fail. 
There was an elaborate property name, accessibly only via o["m(i):S"] or some 
such (I forget the syntax), by which one could unambiguously name a specific 
method in the overload.

But in no case would one want the overloaded member to test as if it were not 
present. This may be where Dmitry parts company from us on TC39. Whatever 
happens, having methods appear only when invoked is a bug in our view.


>  
> - Another minor thing -- `delete` does not really delete.
> 
> delete foo.bar;
> foo.bar; // function
> 
> Well, it depends on how you implement the proxy. It could keep track of 
> deleted property names (I agree this would be cumbersome).

It is indeed more work:

function makeLazyMethodCloner(eager) {
    function DELETED() {}
    var cache = Object.create(null);
    var handler = {
        get: function (self, name) {
            if (cache[name] === DELETED)
                return Object.getPrototypeOf(eager)[name];
            if (!cache[name]) {
                if (name === 'hasOwnProperty') {
                    cache[name] = Proxy.createFunction({}, function (name) {
                        return cache[name] !== DELETED && 
eager.hasOwnProperty(name);
                    });
                } else {
                    cache[name] = Proxy.createFunction({}, function () {
                        return eager[name].apply(eager, arguments);
                    });
                }
            }
            return cache[name];
        },
        hasOwn: function (name) {
            return cache[name] && cache[name] !== DELETED;
        },
        has: function (name) {
            return this.hasOwn(name) || name in Object.getPrototypeOf(eager);
        },
        delete: function (name) {
            cache[name] = DELETED;
            return true;
        }
    };
    return Proxy.create(handler, Object.getPrototypeOf(eager));
}


Running this:

js -f proxymethod2.js -
m1
m2
js> p.hasOwnProperty('m1')
true
js> 'm1' in p
true
js> delete p.m1
true
js> p.hasOwnProperty('m1')
false
js> 'm1' in p              
false
js> o.__proto__.m1 = function(){return "new m1"}
(function () {return "new m1";})
js> p.hasOwnProperty('m1')                       
false
js> 'm1' in p                                    
true
js> p.m1                                         
(function () {return "new m1";})
js> p.m1()
"new m1"

Comments welcome. There's no comparison with __noSuchMethod__ since it does not 
even try to fix the delete bug, as you note:


> But would a separate `noSuchMethod` trap really help here? Consider:
> 
> delete foo.bar;
> foo.bar(); // I expect this to crash now, but it will still call 
> `noSuchMethod`

Exactly! Apples-to-apples comparison, the original makeLazyMethodCloner I 
showed compares favorably to the __noSuchMethod__ alternative (13 lines to 9; 
both fail to handle delete). But you can't fix the __noSuchMethod__ version to 
handle delete.

Calling that a feature in the __noSuchMethod__ case is defining away the 
problem. It goes beyond invoke-only methods. These are invoke-only with a 
vengeance -- you can't get rid of them. They smell like a certain browser's 
hated host objects.

Anyway, it's useful to agree to disagree sometimes. I do not agree that 
invoke-only methods are a valid use-case for proxies. I waved the 
__noSuchMethod__ flag hard when we discussed proxies and tried to make an 
invoke trap, but I'm convinced that the design you and Mark favored, with no 
invoke trap, is strictly better.

/be
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to