Node: ES6 class defined via VM runInNewContext is not accessible

Created on 10 Jul 2017  路  14Comments  路  Source: nodejs/node

  • Node.js Version: 6.2.2
  • OS: Windows 10 64 bit
  • Scope: code
  • Module: VM

Sample Code

const vm = require('vm');
const sandbox = {};

vm.runInNewContext('class Foo{}', sandbox);
vm.runInNewContext('function Bar(){}', sandbox);

console.log(sandbox.Bar); // Prints "[Function: Bar]"
console.log(sandbox.Foo); // Prints "undefined"

Expected result:

console.log(sandbox.Foo); // Prints "[Function: Foo]"

Actual result

console.log(sandbox.Foo); // Prints "undefined"

Can someone please explain why I am unable to access the class Foo within the specified sandbox context?

Is my only option to use function instead of class? This really doesn't look right to me.

vm

Most helpful comment

If you're reusing the context, wrap the code in a block:

const Foo = vm.runInContext(`
  {  
    class Foo { /* ... */ }
    Foo  // return Foo
  }
`, ctx);

All 14 comments

I thought it was something to do with the iterable nature of classes but the following shows that's not the case:

> Object.getOwnPropertyNames(sandbox);
[ 'Bar' ]

/cc @AnnaMag @fhinkel @ofrobots

This behavior is also seen in the REPL with regards to tab completion. function behaves like var, and class behaves like let and const.

What Colin said, a class is block-scoped, it's not global. To illustrate:

$ node
> vm.runInNewContext('class C {} function F() {} this')
{ F: [Function: F] }  // F but not C

Not a bug, IMO. Close?

Adding to the above: since it is not available in the global context, it won't be set on a sandbox.

It appears that function declarations are also block scoped, but create properties on the global object while class declarations don't create properties on the global object.

This is a significant nuisance IMO - we have some isomorphic code that needs to be loaded on the server into a sandbox, and now every class will have to be associated with a var or exported from a "namespace". ... oh well.

It appears that function declarations are also block scoped

Not always. Functions are block-scoped in strict mode but function/global-scoped in sloppy mode. Classes are always block-scoped, regardless of 'use strict'.

$ node -e '{ function f() {} } f()'
# no error
$ --use_strict -e '{ function f() {} } f()'
[eval]:1
{ function f() {} } f()
                    ^

ReferenceError: f is not defined
# <snip>

I'll go ahead and close this out.

Is there a workaround for this?

I have a language that is compiled to Javascript classes at runtime and I am trying to eval the code like vm.runInNewContext("class Foo {}", {}) but cannot figure out how to access that new class in the parent context.

if you're transpiling it you have access to the class name right? could you emit globalThis.Foo = class {}; instead of class Foo {}?

I do indeed have access to the class name, and that approach works. Thanks @devsnek

Another approach that doesn't pollute the global object:

const Foo = vm.runInNewContext(`
  class Foo { /* ... */ }
  Foo  // return Foo
`);

Thanks @bnoordhuis

I gave that a try but did not seem to work: https://github.com/breck7/jtree/blob/master/src/GrammarLanguage.ts#L1483

I started getting "Identifier 'X' has already been declared" errors.

If you're reusing the context, wrap the code in a block:

const Foo = vm.runInContext(`
  {  
    class Foo { /* ... */ }
    Foo  // return Foo
  }
`, ctx);

Thanks @bnoordhuis! That worked and seems cleaner. (https://github.com/breck7/jtree/blob/master/src/GrammarLanguage.ts#L1455)

I didn't know that the last line of block would be returned.

Was this page helpful?
0 / 5 - 0 ratings