1

I am debugging another person's package (currently unmaintained) that runs in the Atom editor. At one point, this package uses vm.runInContext to define a class object, using code equivalent to the following:

const vm = require('vm')
myClass = vm.runInNewContext('class x {}', {}, 'file')

I think this code runs in node.js, but I don't really know all the details of Atom's javascript environment. At any rate, I'm pretty sure it's running in ES6 or later. I believe that when this package was last updated in April 2017, the call to vm.runInNewContext returned a valid class object. However, it currently returns undefined, probably due to some upgrade in Atom since then (maybe around June 1, 2018). Because of this, references to attributes of myClass now fail, which is the bug I am trying to fix.

I believe what is happening here is that javascript interprets the class x {} code as a class declaration, which has no value, so vm.runInNewContext returns undefined. So I'm looking for a graceful way to force this code to return a class, rather than undefined. I have found the following possibilities:

// add a reference to the class, which will be returned as the final value
vm.runInNewContext('class x {}; x', {}, 'file')

// use an assignment, which treats this as a class expression, 
// then the assignment is returned as the final value
vm.runInNewContext('var x; x = class x {}', {}, 'file')
// note: a simpler version ('var x = class x {}') returns undefined

// wrap the class statement in parentheses, so it is evaluated as
// a class expression instead of a class declaration
vm.runInNewContext('(class x {})', {}, 'file')

All of these work, but they all seem clunky. Is there some "standard" way to force javascript / ES6 to treat class x {} as a class expression rather than a class declaration?

Matthias Fripp
  • 17,670
  • 5
  • 28
  • 45
  • 1
    You've already hit upon it - the standard way to convert things to an expression in JS-wordld is to use parens. `vm.runInNewContext('(class x {})', {}, 'file')` is the answer you're looking for, and honestly isn't really that clunky. – AnilRedshift Jul 23 '18 at 20:03
  • @AnilRedshift, thanks for the confirmation, I'll go with the parentheses. I'm not sure how the author got away without them in the first place, but they seem like a pretty minimal and clear change to the code. – Matthias Fripp Jul 23 '18 at 20:46

1 Answers1

1

This behaviour isn't specific to Node.js vm.runInNewContext. The code should be forced to be evaulated as expression.

eval('function foo () {}') === undefined

because it's treated as function declaration instead of function expression. For the same reason this line will result in syntax error:

function () {};

It isn't valid function declaration and it's not used as expression.

So parentheses

vm.runInNewContext('(class x {})', {}, 'file')

or comma operator

vm.runInNewContext('0, class x {}', {}, 'file')

are conventional ways to make evaluated code an expression.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565