I have a JavaScript file, e.js
var global = Function('return this')();
var i = 1;
console.log(eval("100-1"));
console.log(eval("i"));
console.log(global.eval("100-1"));
console.log(global.eval("i"));
When I execute it by V8:
$ node e.js
99
1
99
undefined:1
i
^
ReferenceError: i is not defined
at eval (eval at <anonymous> (/private/tmp/xxxx/e.js:8:20), <anonymous>:1:1)
at eval (native)
at Object.<anonymous> (/private/tmp/xxxx/e.js:8:20)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:902:3
As a result, global.eval
works for math operator, but it is unable to visit the variable i
, while eval
works for both cases.
Is this behavior a limitation of V8? Or is it the expected behavior according to ECMAScript standard?
Yes, this is spec-compliant behavior. ES5 15.1.2.1.1, Direct Call to Eval, says that one requirement for a call to
eval
to be "direct" is that the reference toeval
"has an environment record as its base value." This means it cannot be a reference done by property access (in which case the owning object would the base value); it must be a "bare" function.This distinction is critical to step 1 of 10.4.2, Entering Eval Code:
Thus, an indirect call to
eval
is given a global variable environment, not the local variable environment. Only direct calls get access to the local environment.This is done for practical implementation reasons, because
eval
can signal to garbage collectors a need to avoid cleaning up any variables. For example, here's a case withouteval
:The function returned by
foo
might accessa
afterfoo
terminates, but we can be sureb
andc
will never be accessed ever again afterfoo
terminates.b
andc
can be safely garbage collected, whilea
remains uncollected.Now a case with
eval
:It's impossible to generally decide if the
eval
expressionexp
will refer to a given variable, so the garbage collector must never collect any of the variables so they are still available for use by the returned function.In order to decide that
eval
is in use, the parser must be able to reliably recognize a call toeval
. If theeval
is presented in an indirect way, likeglobal["e"+"va"+"l!"[0]]
, the spec says that thateval
ed code doesn't get access to any local variables, thereby avoiding the garbage collection problem.