Search code examples
node.jses6-proxy

Illegal invocation error using ES6 Proxy and node.js


I can not figure out why the following code does not work:

var os = new Proxy(require('os'), {});
console.log( os.cpus() ); // TypeError: Illegal invocation

whereas

var os = require('os');
console.log(Reflect.apply(os.cpus, os, []));

or

var os = new Proxy(require('os'), {});
console.log( os.platform() );

works as expected.


Solution

  • Having just skim read the source for the os package in the Node repo, it appears that the cpus() is exported from binding.getCPUs which is a C hook in the Node runtime environment.

    cpus() therefore has the binding object as a function context, which is then lost through the proxy, giving you the IllegalInvocation error because there is no context to the function when you call it — although I'm hazy on the details.

    platform() on the other hand is exported as function () { return process.platform; }, and hence it's just a function that returns an object, and doesn't need to be run under a specific context because Node function contexts will have the process variable specified by default (unless it has been overridden).

    The following behaviour shows that applying the os as a context to the cpus function will work — proxies on function objects evidently lose the function context when calling properties.

    const os = require('os');
    const proxy = new Proxy(os, {});  // proxy of object, functions called get proxy context rather than os context
    const cpus = new Proxy(os.cpus, {});  // proxy of function, still has os context
    
    console.log(os.cpus());  // works (duh)
    console.log(cpus());     // works
    console.log(proxy.cpus.apply(os, []));  // works
    console.log(proxy.cpus());  // fails with IllegalInvocation
    

    Note: If someone can clear up the details on the JS function context for an answer I'd love to read it too.