Claudiu
Claudiu

Reputation: 229341

evaluate a string using a different object as the global

Is there any way, using pure javascript, to do something like the following?

var x = 12;
var subGlobal = {};
evalInGlobal(subGlobal, "x = 100;");

console.log(x); //--> 12
console.log(subGlobal.x); //--> 100

The rationale is that I'd like to run scripts in isolated environments, so each script can be referring to an x yet have that x be different (for example).

Upvotes: 2

Views: 63

Answers (3)

Claudiu
Claudiu

Reputation: 229341

It appears what I'm going to do is do a 100% JavaScript approach by using esprima to parse the code, modifying the parse tree by replacing root var declarations and unbound variable assignments with assignments to a property of __thisglobal, then regenerating the script using escodegen and running it. Will see how it goes.

EDIT: It worked! Now it turns stuff like this:

window.addSprite("Logo--oceanGamesLabel", Text("arial.ttf", [112, 112, 255], "Ocean Games"));

// ------------------- labels ----------------------
function labelText(text, halign) {
    halign = halign || "right";
    return Text("arial.ttf", [255, 255, 255], text,
        halign, "center", "labels");
}
window.addSprite("Inputs--usernameLabel", labelText("Username: "));
window.addSprite("Inputs--passwordLabel", labelText("Password: "));

Into stuff like this:

/*-!-OGGlobalified-!-*/
(function ($$GLOBAL) { 
    return (function ($$GLOBAL) {
        {
            $$GLOBAL.TESTING = false;
        }
        $$GLOBAL.console.logf('login.init.js running on window %s(%s)', $$GLOBAL.window.name, $$GLOBAL.window.handle);
        $$GLOBAL.window.addSprite('Logo--oceanGamesLabel', $$GLOBAL.Text('arial.ttf', [
            112,
            112,
            255
            ], 'Ocean Games'));
        {
            $$GLOBAL.labelText = function (text, halign) {
                $$GLOBAL.halign = $$GLOBAL.halign || 'right';
                return $$GLOBAL.Text('arial.ttf', [
                    255,
                    255,
                    255
                    ], $$GLOBAL.text, $$GLOBAL.halign, 'center', 'labels');
            };
        }
        $$GLOBAL.window.addSprite('Inputs--usernameLabel', $$GLOBAL.labelText('Username: '));
        $$GLOBAL.window.addSprite('Inputs--passwordLabel', $$GLOBAL.labelText('Password: '));
    }).apply($$GLOBAL, [$$GLOBAL]);
})

That is, it returns a string which evaluates to a function that can then be called with a global object of choice. And it does scoping properly and such. Fun stuff.

Upvotes: 0

Buu
Buu

Reputation: 50345

Closest I can get is this. It requires initializing the set of variables of global used in code beforehand.

var evalInGlobal = function(global, code) {
    global.x = null; // this is necessary
    with(global) { 
        eval(code); 
    };
};
var x = 12;
var subGlobal = {};
evalInGlobal(subGlobal, "x = 100;");

console.log(x); //--> 12
console.log(subGlobal.x); //--> 100

To get rid of the initialization, I think you have to translate the code block to something aware of a scope. For example, translate x = 100; to function(ctx) { ctx.x = 100; } and invoke it by calling fn(global).

That is similar to the way AngularJS builds their parser to allow expressions (a small subset of JS) evaluated against a given scope.

Upvotes: 1

fred02138
fred02138

Reputation: 3361

I'll agree with @LightStyle 's comment above. You could, however, do something like this, which probably isn't exactly what you want.

var x = 12;
var subGlobal = function() {};
subGlobal.x = undefined;

function m() {
    this.x = 100;
}

m.apply(subGlobal);

console.log(x); //--> 12
console.log(subGlobal.x); //--> 100

Upvotes: 0

Related Questions