Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Calling a strict-scoped function defined via eval in a non-strict context forgoes it's closure #8721

Closed
amasad opened this issue Nov 13, 2014 · 17 comments
Labels

Comments

@amasad
Copy link

amasad commented Nov 13, 2014

  • Create foo.js and add:
var code = [
'function x() {',
  '"use strict";',
  'console.log(x);',
'}',
'x();'
].join('\n');

eval(code);
  • Run using node v0.10.x or v0.11.x node foo.js
  • Expected output: [Function x]
  • Actual output:
undefined:3
console.log(x);
            ^
ReferenceError: x is not defined

This repros in v0.10.x and v0.11.x (including v0.11.14) but not in v0.8.x. Additionally, this doesn't repro in latest stable chrome.

@joliss
Copy link

joliss commented Nov 13, 2014

I can reproduce this on Node v0.10.33, and interestingly it only seems to happen when you run node foo.js. If you paste it in interactive mode, it works as expected:

> var code = ['function x() {', '"use strict";', 'console.log(x);', '}', 'x();'].join('\n'); eval(code);
[Function: x]

@WebReflection
Copy link

FWIW this works as expected for me in OSX and v0.10.28

@WebReflection
Copy link

@joliss … true that, it also works in foo.js if you set everything as strict:

"use strict";
var code = [
'function x() {',
  '"use strict";',
  'console.log(x);',
'}',
'x();'
].join('\n');

eval(code);

interesting bug!

@amasad
Copy link
Author

amasad commented Nov 13, 2014

I should mention, my use case is I'm testing a compile-to-js so I'm eval'ing the compiled code.

@joliss that's an interesting find, I think it's because the repl uses the vm module. If you create another file, call it bar.js:

var fs = require('fs')
var vm = require('vm')
var script = vm.createScript(fs.readFileSync('foo.js').toString())
script.runInThisContext()

Now, it works fine. So it has to do with the way node cli runs scripts

@a0viedo
Copy link
Member

a0viedo commented Dec 2, 2014

I'm able to reproduce this on branches v0.10 and v0.11 but not in branch v0.8.

@jasnell
Copy link
Member

jasnell commented Jun 3, 2015

Interestingly, this doesn't happen within the repl. @joyent/node-coreteam ... thoughts?

@jasnell
Copy link
Member

jasnell commented Jun 25, 2015

@domenic ... any thoughts? @a0viedo ... can you give this a quick test on the latest io.js?

@jasnell jasnell added the V8 label Jun 25, 2015
@a0viedo
Copy link
Member

a0viedo commented Jun 25, 2015

@jasnell Using Node 0.10.39, 0.12.5 and iojs 2.3.1 the result is the same:

undefined:3
console.log(x);
            ^
ReferenceError: x is not defined

@trevnorris
Copy link

Just built and ran from io.js next branch. Which uses V8 4.4. Got same error.

/cc @domenic This also affects io.js.

@domenic
Copy link

domenic commented Jun 25, 2015

That's bizarre. I wonder what we are doing to confuse V8 about this.

Maybe it has to do with how all Node code is wrapped in a (sloppy) module-closure?

@trevnorris
Copy link

@domenic Good point. I'll test the code in d8 and see if we get the same result.

@trevnorris
Copy link

@domenic I just ran the following slightly modified example on latest d8 4.2:

var code = [
'function x() {',
  '"use strict";',
  'print(x);',
'}',
'x();'
].join('\n');

eval(code);

And there was no issue. Though I tried the following:

var code = [
'(function (exports, require, module, __filename, __dirname) {',
  'function x() {',
    '"use strict";',
    'print(x);',
  '}',
  'x();',
'\n})();'
].join('\n');

eval(code);

Which also didn't have an issue. Thoughts?

@domenic
Copy link

domenic commented Jun 26, 2015

Well, the latter should not evil the wrapper, to better emulate io.js. But yeah, if that doesn't show the difference, I've got nothing off the top of my head...

@trevnorris
Copy link

Another slight variation that does work:

var code = [
  '"use strict"',
  'function x() {',
    '"use strict"',
    'console.log(x);',
  '}',
  'x();',
].join('\n');
eval(code);

I tested this all the way back to v0.10 and it succeeds with the extra 'use strict' put in. This must be something to do with variable scope. The following also succeeds:

var code = [
'(function() {',
  'function x() {',
    '"use strict"',
    'console.log(x);',
  '}',
  'x();',
'}())',
].join('\n');
new Function(code)();

I'm out of ideas. Though this does appear to be a bug.

@domenic
Copy link

domenic commented Jun 26, 2015

To be clear, now that I'm at a computer, I was suggesting testing

(function (exports, require, module, __filename, __dirname) {

var code = [
  'function x() {',
    '"use strict";',
    'print(x);',
  '}',
  'x();',
].join('\n');

eval(code);

}();

since that is closer to how modules work. But yeah, my next guess is that vm.runInThisContext (which is used for modules, I believe) is responsible.

@amasad
Copy link
Author

amasad commented Jun 26, 2015

Actually, it worked using the vm module. See my earlier comment #8721 (comment)

@domenic
Copy link

domenic commented Jun 26, 2015

Right, but I mean that Node executes modules via vm.runInThisContext('wrapper stuff' + fs.readFileSync(...) + ' wrapper stuff'), instead of directly like d8 does.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

8 participants