Domanda

I'm experimenting with Harmony Proxies and I'd like to run code in a Proxy context, which means that the global object of the code will be a Proxy. For example, if I call the function foo() in the code, it will be managed by the proxy get() method.

But using Proxy.create() and vm.runInNewContext() doesn't work, it seems that the Proxy object is overwritten by a new context object and looses his properties.

var vm = require('vm');

var proxy = Proxy.create({
    get: function(a, name){
        return function(){
            console.log(arguments);
        }
    }
});

vm.runInNewContext("foo('bar')", proxy); // ReferenceError: foo is not defined

Is there any way to achieve what I'm trying to do?

// EDIT

(function() {
    eval("this.foo('bar')");
}).call(proxy);

The above code works well, but I'd like to be able not to use the this statement, and directly refer to the global context.

È stato utile?

Soluzione

This is possible in two ways. In Node, I was able to do this with a slightly modified contextify. Contextify is a module that allows for turning arbitrary objects into global contexts to run similar to the vm module. It does this by creating a global object that has a named property interceptor which then forwards accesses to the object, so it's able to keep the reference "live" instead of copying the properties as node's builtin vm does. The modification I made was to change it so that these accesses would trigger the correct proxy traps, IE changing ctx->sandbox->GetRealNamedProperty(property) (which doesn't trigger proxy get trap) to ctx->sandbox->Get(property). Similar changes for has, set, etc. Property enumeration doesn't work quite right (nor does it in contextify normally) because the ability to hand the property listing (for getOwnPropertyNames at least) isn't exposed to the API.

Contextify: https://github.com/brianmcd/contextify My fork: https://github.com/Benvie/contextify Pull request: https://github.com/brianmcd/contextify/pull/23

The second method will work universally but doesn't actually result in a proxy global. Essentially you create proxies for each existing object in global and then load the desired code inside a function created that shadows all the properties as function parameters. Something like:

var globals = Object.getOwnPropertyNames(global);
var proxies = globals.map(function(key){
  return forwardingProxy(global[key]);
});
globals.push(codeToRun);
var compiled = Function.apply(null, globals);
var returnValue = compiled.apply(forwardingProxy(global), proxies);

Altri suggerimenti

Short answer: Don't even think of it. :)

Longer answer: There is a lot of magic involved in V8's treatment of the global object, in order to deal with browser issues like frames, security access checks, etc. As it stands, that magic is completely incompatible with using a proxy in its place. This may change eventually, but not any time soon.

Having said that, I also don't think you should do that. The global object is a terribly hacky feature, it is going to be demoted in Harmony, and you are best advised not to try playing dirty tricks with it.

global.__proto__ = forwarder(global.__proto__);
console.log([hello, my, name, is, bob, weeeee])

function forwarder(target){
  var traps = {
    getOwnPropertyDescriptor: Object.getOwnPropertyDescriptor.bind(null, target),
    getOwnPropertyNames: Object.getOwnPropertyNames.bind(null, target),
    keys: Object.keys.bind(null, target),
    defineProperty: Object.defineProperty.bind(null, target),
    get: function(r,k){ return target[k] },
    set: function(r,k,v){ target[k] = v; return true },
    has: function(k){ return k in target },
    hasOwn: function(k){ return {}.hasOwnProperty.call(target, k) },
    delete: function(k){ delete target[k]; return true },
    enumerate: function(){ var i=0,k=[]; for (k[i++] in target); return k }
  };

  var names = {
    get: 1,
    set: 1,
    has: 0,
    hasOwn: 0,
    delete: 0,
    defineProperty: 0,
    getOwnPropertyDescriptor: 0
  }

  return Proxy.create(Proxy.create({
    get: function(r, trap){
      return function(a, b){
        if (trap in names)
          console.log(trap, arguments[names[trap]])
        else
          console.log(trap);

        if (trap === 'get' && !target[b]);
          return b;

        if (trap in traps)
          return traps[trap].apply(target, arguments);
      }
    }
  }), Object.getPrototypeOf(target));
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top