javascriptactionscriptevalsemantics

eval vs Function() return semantics


We have the product (normal desktop app) that wants to pull some data from the Adobe products (from the documents, opened in these products). Since the Apple events are too slow, we implemented a custom interop that sends generated jsx (Adobe's ActionScript3 based on the not long-dead JavaScript 1.5) to Adobe product processes via socket. This is 2000+ times faster than using events, but forces us to do some tricks involving eval, with the current goal is to minimise the risks of inadvertently corrupting the state of the JSX engine by the non-isolated eval.

This code runs in a trusted environment (not the browser), and the code itself is also 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?


Solution

  • 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; })()");
    

    (Or use Alexander's solution where you create a "real" function with the eval as its function body)

    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.