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.
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.