unknownPrivateSymbol trap (was: WeakMap better than Private Symbols? (was: direct_proxies "problem"))

Nathan Wall nathan.wall at live.com
Tue Jan 15 22:23:11 PST 2013


> These use cases can be made dynamic be some kind of
>
> Proxy.fromNowOnForwardUnknownPrivateSymbols(proxy, false);
>
> API. This is not truly dynamic. I'd say no truly dynamic use cases
> exist, where you must decide _at_the_exact_point_of_access_. IMO.


I would be ok with something more along the lines of:

    Proxy.preventAllFurtherCommunicationsThrough(proxy);

Meaning that the target becomes inaccessible through the proxy, through both traps that were specified in the handler and implicit forwarding, including private symbols.

One of the major problems I have with `unknownPrivateSymbol` can be represented in the following case:

    let create = Object.create,
        getOwnPropertyNames = Object.getOwnPropertyNames,

        $size = new Symbol(/* private */),
        $data = new Symbol(/* private */);

    class StringMap {

        constructor() {
            this[$size] = 0;
            this[$data] = create(null);
        }

        set(key, value) {

            let data = this[$data];

            if (!data)
                throw new TypeError('this object must be a StringMap');

            if (!(key in data))
                this[$size]++;

            data[key] = value;

        }

        get(key) {

            let data = this[$data];

            if (!data)
                throw new TypeError('this object must be a StringMap');

            return data[key];

        }

        size() {
            return this[$size];
        }

        keys() {

            let data = this[$data];

            if (!data)
                throw new TypeError('this object must be a StringMap');

            return getOwnPropertyNames(data);

        }

    }

I think it is wrong that a Proxy should be allowed to introduce an inconsistency in my object by permitting me to increase the private size property but then preventing me from adding the new key to the data set. For example:

    function getBrokenMap() {

        let map = new StringMap();
        map.set('apple', 'red');
        map.set('kiwi', 'green');

        let beEvil = false,
            evilProxy = new Proxy(map, {
                unknownPrivateSymbol: function() {
                    if (beEvil)
                        throw new Error();
                    beEvil = true;
                }
            });

        try {
            map.set('lemon', 'yellow');
        } catch(x) { }

        return map;

    }

    let map = getBrokenMap();

    map.size();        // => 3
    map.keys().length; // => 2

I think this is a problem from the standpoint of a library developer who wants to ensure the integrity of one's components.

In the discussion with David, I think we agreed that it is possible for a membrane to prevent communication between two objects without `unknownPrivateSymbol` given (1) they don't know each other directly and (2) no other third party knows both of them directly. We also agreed that given either 1 or 2, the objects could establish means to communicate behind the membrane's back **regardless of whether `unknownPrivateSymbol` exists**.

In light of these considerations, I would strongly urge the removal of `unknownPrivateSymbol`. It allows breaking all private symbol communication in one fell swoop, but it also allows other things which, in my opinion, should not be allowed. As Brendan said, it's really none of your business to be able to interfere with my object's internals -- including, I would add, internal access to private symbols you don't know about.

If you want a way to cut communications, how about a function that disables all traps and all forwarding on the proxy (including private symbol forwarding)? That prevents the public method `set(key, value)` from being called which uses the internal private keys, so it doesn't mess up the integrity of my object.

Nathan 		 	   		  


More information about the es-discuss mailing list