Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Fix #1801 vm: Use 'sandbox' as global_prototype
Browse files Browse the repository at this point in the history
Squashed commit:

(- re tests) Cleaning up the `Script` test suite.

For whatever reason, there were several duplicate test files related to `Script`
and the `'vm'` module. I removed these, and fixed a few other small issues.
(More fixes coming in subsequent commits.)

Squashes: 19e86045a0..1e3dcff4eb

(api fix:1801 new:1801) `'vm'` module uses sandbox as prototype

As described in GH-1801, the `'vm'` module was handling the `sandbox` object
provided by the API consumer in a particularly terrible and fragile fashion: it
was simply shallow-copying any enumerable properties from the sandbox onto the
global context before executing the code, and then eventually copying any values
on the global context back into the sandbox object *afterwards*.

This commit removes all of that implementation, and utilizes the passed sandbox
object as the *prototype of the context* instead. A bit of a hack, but a very
effective one.

This no longer allows for new variables created in the global context to be
placed into your sandbox after execution has completed, but that’s for the best
anyway, as it’s not very in line with the concept of a “box of passed-in
context.” I’m planning to further implement an interface for API consumers to
acquire the *actual global* from within the VM soon, thus allowing for
separation-of-concerns: providing data *to* the VM via the sandbox-prototype,
and exploring the internal environment of the VM itself.

// GitHub cruft: closes #1801

Squashes: 43b8e3c..209ed86
  • Loading branch information
ELLIOTTCABLE authored and isaacs committed Oct 19, 2011
1 parent d9bc845 commit 200df86
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 171 deletions.
25 changes: 18 additions & 7 deletions src/node_script.cc
Expand Up @@ -364,12 +364,25 @@ Handle<Value> WrappedScript::EvalMachine(const Arguments& args) {


// New and user context share code. DRY it up. // New and user context share code. DRY it up.
if (context_flag == userContext || context_flag == newContext) { if (context_flag == userContext || context_flag == newContext) {
// Enter the context // First, we grab the "global proxy" (see v8's documentation for an
context->Enter(); // explanation of this) by calling `Context::Global()` *before* we call
// `Context::DetachGlobal()`, which will then give us access to the *actual*
// global object.

// We have to set the prototype of the *actual* global object, because the
// prototype of v8's 'global proxy' is the global object itself, and v8
// blows up in approximately twenty different ways if you mess with that
// relationship.


// Copy everything from the passed in sandbox (either the persistent // Once we have the `global_proxy` reference, we utilize that to
// context for runInContext(), or the sandbox arg to runInNewContext()). // `ReattachGlobal()` before entering the context.
CloneObject(args.This(), sandbox, context->Global()->GetPrototype()); Handle<Object> global_proxy = context->Global();

context->DetachGlobal();
context->Global()->SetPrototype(sandbox);
context->ReattachGlobal(global_proxy);

context->Enter();
} }


// Catch errors // Catch errors
Expand Down Expand Up @@ -409,7 +422,6 @@ Handle<Value> WrappedScript::EvalMachine(const Arguments& args) {
result = script->Run(); result = script->Run();
if (result.IsEmpty()) { if (result.IsEmpty()) {
if (context_flag == newContext) { if (context_flag == newContext) {
context->DetachGlobal();
context->Exit(); context->Exit();
context.Dispose(); context.Dispose();
} }
Expand All @@ -432,7 +444,6 @@ Handle<Value> WrappedScript::EvalMachine(const Arguments& args) {


if (context_flag == newContext) { if (context_flag == newContext) {
// Clean up, clean up, everybody everywhere! // Clean up, clean up, everybody everywhere!
context->DetachGlobal();
context->Exit(); context->Exit();
context.Dispose(); context.Dispose();
} else if (context_flag == userContext) { } else if (context_flag == userContext) {
Expand Down
9 changes: 8 additions & 1 deletion test/simple/test-script-context.js
Expand Up @@ -32,7 +32,8 @@ var result = script.runInContext(context);
assert.equal('passed', result); assert.equal('passed', result);


common.debug('create a new pre-populated context'); common.debug('create a new pre-populated context');
context = script.createContext({'foo': 'bar', 'thing': 'lala'}); var sandbox = {'foo': 'bar', 'thing': 'lala'};
context = script.createContext(sandbox);
assert.equal('bar', context.foo); assert.equal('bar', context.foo);
assert.equal('lala', context.thing); assert.equal('lala', context.thing);


Expand All @@ -42,6 +43,12 @@ result = script.runInContext(context);
assert.equal(3, context.foo); assert.equal(3, context.foo);
assert.equal('lala', context.thing); assert.equal('lala', context.thing);


// Issue GH-1801
common.debug('test dynamic modification');
context.fun = function(){ context.widget = 42 };
script = new Script('fun(); widget');
result = script.runInContext(context);
assert.equal(42, result);


// Issue GH-227: // Issue GH-227:
Script.runInNewContext('', null, 'some.js'); Script.runInNewContext('', null, 'some.js');
Expand Down
31 changes: 27 additions & 4 deletions test/simple/test-script-new.js
Expand Up @@ -66,11 +66,11 @@ code = 'foo = 1;' +
'bar = 2;' + 'bar = 2;' +
'if (baz !== 3) throw new Error(\'test fail\');'; 'if (baz !== 3) throw new Error(\'test fail\');';
foo = 2; foo = 2;
obj = { foo: 0, baz: 3 }; sandbox = { foo: 0, baz: 3 };
script = new Script(code); script = new Script(code);
var baz = script.runInNewContext(obj); var baz = script.runInNewContext(sandbox);
assert.equal(1, obj.foo); assert.equal(1, sandbox.foo);
assert.equal(2, obj.bar); assert.equal(2, sandbox.bar);
assert.equal(2, foo); assert.equal(2, foo);


common.debug('call a function by reference'); common.debug('call a function by reference');
Expand All @@ -85,6 +85,29 @@ var f = { a: 1 };
script.runInNewContext({ f: f }); script.runInNewContext({ f: f });
assert.equal(f.a, 2); assert.equal(f.a, 2);


// Issue GH-1801
common.debug('test dynamic modification');
sandbox.fun = function(){ sandbox.widget = 42 };
script = new Script('fun(); widget');
result = script.runInNewContext(sandbox);
assert.equal(42, result);

// Issue GH-1801
common.debug('indirectly modify an object');
var sandbox = { proxy: {}, common: common, process: process };
sandbox.finish = function(){
process.nextTick(function(){
common.debug('(FINISH)');
assert.equal(42, sandbox.proxy.widget)
})
};
script = new Script(" process.nextTick(function(){ " +
" common.debug('(TICK)'); " +
" proxy.widget = 42; " +
" }); " +
" finish() ");
script.runInNewContext(sandbox);

common.debug('invalid this'); common.debug('invalid this');
assert.throws(function() { assert.throws(function() {
script.runInNewContext.call('\'hello\';'); script.runInNewContext.call('\'hello\';');
Expand Down
40 changes: 0 additions & 40 deletions test/simple/test-script-static-context.js

This file was deleted.

62 changes: 0 additions & 62 deletions test/simple/test-script-static-new.js

This file was deleted.

56 changes: 0 additions & 56 deletions test/simple/test-script-static-this.js

This file was deleted.

2 changes: 1 addition & 1 deletion test/simple/test-script-this.js
Expand Up @@ -49,7 +49,7 @@ code = 'foo = 1;' +
foo = 2; foo = 2;
obj = { foo: 0, baz: 3 }; obj = { foo: 0, baz: 3 };
script = new Script(code); script = new Script(code);
script.runInThisContext(script); script.runInThisContext();
assert.equal(0, obj.foo); assert.equal(0, obj.foo);
assert.equal(2, bar); assert.equal(2, bar);
assert.equal(1, foo); assert.equal(1, foo);
Expand Down

0 comments on commit 200df86

Please sign in to comment.