skip to Main Content

Disclaimer: The code mentioned in the question is assumed to be running in the trusted environment (not the browser), and the code itself is trusted. So any security considerations about eval are not applicable. The main goal of the question is to prevent the pollution of the global scope performed by the eval.


When using the eval, then the result of the last expression is considered as the return value of the whole eval, e.g. result of the eval-ing of the following code will be the value of the y+2=12 expression.

var res = eval("
    var x = 5;
    var y = x*2;
    y+2;
"); //res = 12 

At the same time, if we will wrap such a code into the Function constructor, then the result will be undefined

var res = (new Function("
    var x = 5;
    var y = x*2;
    y+2;
")).call(); //res=undefined

To fix the last snippet, we need to add explicit return to the last expression:

var res = (new Function("
    var x = 5;
    var y = x*2;
    return y+2;    // <------- changes here
")).call(); //res=12

So, the eval and Function have the different return semantics.

And I can’t just replace eval with the Function() in the legacy code. I will also be forced to change the calling side to add the return in the code, passed to Function().

Is there a way to avoid adding explicit return, so it will be possible to replace eval with the Function() in a transparent way (let’s assume that we are not interested in the access to the local scope)?

Or maybe another technique exists, which can provide the equivalent of the eval which will minimise the risks of the global scope pollution and will provide the same return semantics as the original eval?

Note: The JS engine used in the system is quite old, so it does not support strict mode and ES6+ features.

2

Answers


  1. Just return evaled code from Function:

    const code = "var p = 2; p*2";
    const fn = new Function('', "return eval('${code}')");
    
    console.log(fn());

    Or as Mike suggested:

    const code = "var p = 2; p*2";
    const fn = function(){ return eval(code); };
    
    console.log(fn());
    Login or Signup to reply.
  2. Note that for technical correctness, the initial eval block you’re showing does not just result in 12, it results in everything you wrote =)

    eval is a macro that halts JS execution, subs in the code you give it, executes it, and then resumes regular execution. So in this case the result is two new variables x and y in the same scope that eval was run in, and the "12" simply comes from the fact that assignments are returning operations.

    If you don’t want to introduce anything new to your scope, then remember that eval is a macro: make it sub in the code you need:

    eval("(function() { var x=5; y=7; return x + y; })()");
    

    And of course, that can take arguments just fine, too:

    var someVar = 5;
    var someOtherVar = 7;
    var result = 0;
    eval(
      "result = (function(a,b) { return a+b; })(" + someVar + "," + someOtherVar + ")"
    );
    

    This will now run an IIFE that does whatever it needs to do, returns 12, and then gets garbage collected.

    But of course, and this is the important part: if that’s what you need, then you don’t need to eval anything. You can just write the function and call it with the values you already have. You almost never actually need eval, even in Adobe scripts.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search