This seems like a very interesting problem. The problem with your code is that you generate new function every time you execute limitedEval
. This means that whatever variables you create using var
keyword, will only exist within the context of that function. What you really need is to have 1 function per context and reuse that function's context. The most obvious way to do that is by using generators. Generators are essentially functions that can be paused and then restarted.
// IIFE to store gen_name
var limitedEval = function() {
// Symbol for generator property so we don't pollute `ctx` namespace
var gen_name = Symbol();
return function limitedEval(src, context) {
if(!(gen_name in context)) {
// Generator that will run eval and pause til getting the next source code
var generator = function * () {
with(this) {
while(true) {
yield eval( yield );
}
}
};
context[gen_name] = generator.call(context);
// Initially, we need to execute code until reaching the first `yield`
context[gen_name].next();
}
// First, send the source to the `eval`
context[gen_name].next( src );
// Second, get the `eval` result
return context[gen_name].next().value;
};
}();
And now you can call
var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo);', ctx);
Every limitedEval
call will now reuse whatever generator function it will find on the provided ctx
object. If the function doesn't exist, it will create it and put it on the ctx
object. Because you now have only one function per ctx
, it will reuse the function's context when creating variables with var
keyword. Note: you will not be able to look up those variables via the ctx
object, because they will only exist within the function's context.
I'm not entirely sure if you can achieve the same result without using generators.
Edit: others made great suggestions so I replaced randomly generated property with Symbol
and did with(this)
instead of with(context)