Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indirect eval call in strict mode

I understand about how eval() works in non-strict contexts, however the case of using eval() in strict mode has completely befuddled me. When eval() is called directly in the global scope, variables are kept inside the new eval() scope:

'use strict'; eval('var a = 1;'); console.log(a); // ReferenceError: a is not defined 

However, if I perform an indirect call to eval() in the global scope (should be the same thing, right?), it acts as though it is not in strict mode (if you don't believe me, see this JSFiddle):

'use strict'; (0, eval)('var a = 1;'); // indirect call to eval console.log(a); // 1??? 

For an explanation of what (0, eval) does: see Why does google main page use (0, obj.func)(args) syntax?.

At least according to my understanding of how eval() is supposed to work in strict mode, it is meant to (no matter whether eval() is called directly or indirectly) create a new scope for variables defined in the eval() call, however this doesn't seem to be the case here. (ECMA-262 spec 5th ed 10.4.2)

This is the case in all major browsers (including Internet Explorer 10, Chrome 30 and Firefox 24) so I don't think it's a bug. Aren't they both meant to do the same thing, and if not, why is this the case?

Note: yes, I know the "dangers" of using eval() - I simply want to understand the logic behind this :)

like image 398
Qantas 94 Heavy Avatar asked Oct 14 '13 10:10

Qantas 94 Heavy


People also ask

When would you not use strict mode?

If you have such an unrestrictedly typed code, that is used variables without declaring. One variable declared within some function/scope and used from somewhere else(it will be undeclared there) and you can't rewrite/change them, then you should not go for "use strict;" mode because it will break the code.

What is correct way to run a JavaScript in strict mode?

Using Strict mode for a function: Likewise, to invoke strict mode for a function, put the exact statement “use strict”; (or 'use strict';) in the function's body before any other statements. Examples of using Strict mode: Example: In normal JavaScript, mistyping a variable name creates a new global variable.

What is strict mode?

Strict mode changes some previously-accepted mistakes into errors. JavaScript was designed to be easy for novice developers, and sometimes it gives operations which should be errors non-error semantics. Sometimes this fixes the immediate problem, but sometimes this creates worse problems in the future.

Is eval () bad?

eval() Warning: Executing JavaScript from a string is an enormous security risk. It is far too easy for a bad actor to run arbitrary code when you use eval() .


1 Answers

tl;dr

The second (0, eval)('var a = 1;'); case is in fact not a direct call.

You can see this more prevalently in:

(function(){ "use strict"     var x = eval;     x("var y = 10"); // look at me all indirect     window.y;// 10     eval("var y = 11");     window.y;// still 10, direct call in strict mode gets a new context })(); 

The issue can be seen in:

If the eval code is strict code, then (me: fix context)

But strict eval code is defined as:

Eval code is strict eval code if it begins with a Directive Prologue that contains a Use Strict Directive or if the call to eval is a direct call.

Since the call is not direct, the eval code is not strict eval code - and the execution is on global scope.


First of all great question.

"Eval Code" is more general than direct or indirect call to eval.

Let's check the exact specification for the eval function

15.1.2.1 eval (x)

When the eval function is called with one argument x, the following steps are taken:

  1. If Type(x) is not String, return x.

  2. Let prog be the ECMAScript code that is the result of parsing x as a Program. If the parse fails, throw a SyntaxError exception (but see also clause 16).

  3. Let evalCtx be the result of establishing a new execution context (10.4.2) for the eval code prog.

  4. Let result be the result of evaluating the program prog.

  5. Exit the running execution context evalCtx, restoring the previous execution context. ...

So, let's explore what 10.4.2 tells us, you cited that - in specific let's look at the first clause:

If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then ... Initialise the execution context as if it was a global execution context

So what is a direct call?

A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:

The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".

The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.

So, what's the MemberExpression in both cases?

In eval('var a = 1;'); indeed the result of evaluating it has a reference name eval and calling GetValue resolution on it returns the built in function.

In (0, eval)('var a = 1;'); the result of evaluating the member expression does not have a reference name eval. (It does resolve to the built in function on GetValue though).

What are reference names anyway?

Section 8.7 in the spec tells us:

A Reference is a resolved name binding. A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1). A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.

This requires us to look into the GetReferencedName:

GetReferencedName(V). Returns the referenced name component of the reference V.

So, while the expression (0,eval) === eval is true, when evaluating the function, this is actually an indirect call because of naming.

May I offer the Function constructor instead :)?

like image 142
Benjamin Gruenbaum Avatar answered Oct 05 '22 20:10

Benjamin Gruenbaum