with(scope)
{
eval(code);
}
e.g. nodejs:
VM.runInNewContext(code, sandbox, file);
Christoph
You could use this code in GPSEE, but with(scope) does not imply that
local variable assignments will assign to properties on scope. For
example:
js> var o = {foo:'bar'}
js> with(o){print(foo)}
bar
js> with(o){bar='baz'}
"baz"
js> bar
"baz"
You can, though, shadow global variables:
js> with(o){foo='presto!'}
"presto!"
js> o.foo
"presto!"
js> foo
typein:6: ReferenceError: foo is not defined
To really sandbox though you need to shadow every global variable.
There are also still a number of ways to access the built-in object
prototypes and the global variable scope. It's may also be worth
noting that, IIRC, with() will not trace (JIT optimize) in
Spidermonkey.
> For my case it is fine if modifying prototype in sandbox affects sandboxe's
> parent prototype.
Then you have a string, which means you can use some simple
language-transformation techniques to create a function scope around
your module code before evaluating it. For instance if your module
code reads:
exports.Ball = function Ball(radius){this.volume =
Math.PI*4/3*radius*radius*radius}
Then simply wrapping that code with other code will give you the desired effect:
(function(exports){ // added
exports.Ball = function Ball(radius){this.volume =
Math.PI*4/3*radius*radius*radius}
}) // added
> The eval is used to compile module code. The objective is to only pass in
> "module", maybe "console" and nothing else should exist in global scope for
> the module. If globals are created by the module they should be local to the
> module.
>
> "sandbox" is not really the right term here I don't think.
It appears you are writing a custom module loader, you may also want
to shadow require(). Take a look at BravoJS:
http://code.google.com/p/bravojs/
I think Wes can supply you with examples if they are not forthcoming.
BravoJS was developed for web browsers, but the same principles and
some of the code apply to both environments.
> Using:
>
> with(scope)
> {
> eval(code);
> }
>
> works but causes globals to be present that in a "strict" environment should
> not be.
You will need to shadow any global variables you don't want showing
up. You can do this with function arguments, but just as good or
perhaps better to shadow them with "var" declarations:
(function(exports){ // added
var secretFunction = void 0; // added
exports.Ball = function Ball(radius){this.volume =
Math.PI*4/3*radius*radius*radius}
}) // added
Now the code will not have naive access to secretFunction.
Unfortunately there are still ways to access the global variable
scope.
> So given:
>
> var global = {foo:true};
>
> with(globals) {
> with(scope)
> {
> eval('typeof foo === "undefined"');
> }
> }
>
> should evaluate to TRUE.
This is another way to do it :)
> The limitation here is that if a module does modify a prototype it *IS*
> global for the whole runtime which would be a restriction placed on the
> module author.
Correct. It is unfortunate.
Good to hear your input! I have considered modifying Spidermonkey to
return treat accessors of properties on "global prototypes"
differently depending on where the code that is accessing them came
from; the goal being to give different modules their own prototypes
for Object, Array, etc.
It appears you are writing a custom module loader, you may also want to shadow require(). Take a look at BravoJS: http://code.google.com/p/bravojs/ I think Wes can supply you with examples if they are not forthcoming. BravoJS was developed for web browsers, but the same principles and some of the code apply to both environments.
> The limitation here is that if a module does modify a prototype it *IS* > global for the whole runtime which would be a restriction placed on the > module author.Correct. It is unfortunate. Good to hear your input! I have considered modifying Spidermonkey to return treat accessors of properties on "global prototypes" differently depending on where the code that is accessing them came from; the goal being to give different modules their own prototypes for Object, Array, etc.
> Given what you're doing, I'd encourage you to study the blog post I
> mentioned the other day that lets GSR run wrapped modules:
> http://gpsee.blogspot.com/2010/12/wrapped-modules-with-gpsee-02-gsr-10.html
>
> The technique is straightfoward: we supply module.declare via the
> module constructor, then invoke the module factory function with fresh
> require/exports/module "locals", supplied by the function's argument
> vector, rather than "with(scope)".
>
> The code works by accessing SpiderMonkey internals through FFI and
> accessing the "global" object, which is actually the module's scope
> object.
>
> Is there a way that you can get the module scope need via invocation
> of the module factory, rather than trying to fudge up a scope?
Should be able to. I'll take a look tomorrow.
Christoph