Eval Locally with Persistent Context

I've been working on a remote console as part of our IoT JavaScript platform Ruff. The console needs to evaluate expressions with following constraints:

  1. It needs to evaluate inside an immediately called function. We are compiling modules within an immediately called function just like what Node.js does. Developer might define variables and functions inside a module, and if its the entrance module of an application, we would like to access variables and functions inside.
  2. It needs access to variables or functions previously defined in other evaluated expressions. This is relatively easy to understand, as we usually want to keep the context.

The solution applied is to transform expressions with Babel by writing a simple Babel plugin that visits VariableDeclaration and FunctionDeclaration and transform them into global assignments. For example, var abc = 123; is transformed to global.abc = 123; undefined;.

However, a colleague of mine became interested into this thing and thought it might be possible to do so without transforming the expressions. My attitude to that thought was like: come on, I know JavaScript much better than you do, and I've told you it cannot be done! But soon on the road to lunch I realized that I might be wrong. We exchanged the idea and came up with two similar implementations.

Here's my version (refined):

var __eval = function () {
    __eval = eval(`(${arguments.callee.toString()})`);
    return eval(arguments[0]);
};

The idea is to evaluate the function expression that contains eval that evaluates the function expression "recursively". It might be a little tricky to understand but let's unfold the eval expression for function:

var __eval = function () {
    __eval = function () {
        __eval = eval(`(${arguments.callee.toString()})`);
        return eval(arguments[0]);
    };
    return eval(arguments[0]);
};

And again:

var __eval = function () {
    __eval = function () {
        __eval = function () {
            __eval = eval(`(${arguments.callee.toString()})`);
            return eval(arguments[0]);
        };
        return eval(arguments[0]);
    };
    return eval(arguments[0]);
};

So everytime __eval is called, it creates another __eval function inside previous __eval, thus the context may persist!

However, this approach is not perfect. On the one hand, it makes the chain unable to be freed; and on the other hand, newly defined variables could "block" the access to previously defined ones. Though the idea of recursively evaluating is still inspiring, and hope you enjoy it as well.