Browse Source

vm: support parsing a script in a specific context

PR-URL: https://github.com/nodejs/node/pull/14888
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com>
canary-base
Timothy Gu 8 years ago
parent
commit
d932e80231
No known key found for this signature in database GPG Key ID: 7FE6B095B582B0D4
  1. 41
      lib/vm.js
  2. 1
      src/env.h
  3. 49
      src/node_contextify.cc
  4. 101
      test/inspector/test-scriptparsed-context.js

41
lib/vm.js

@ -21,8 +21,14 @@
'use strict';
const binding = process.binding('contextify');
const Script = binding.ContextifyScript;
const {
ContextifyScript: Script,
kParsingContext,
makeContext,
isContext,
runInDebugContext
} = process.binding('contextify');
// The binding provides a few useful primitives:
// - Script(code, { filename = "evalmachine.anonymous",
@ -62,11 +68,11 @@ Script.prototype.runInNewContext = function(sandbox, options) {
function createContext(sandbox) {
if (sandbox === undefined) {
sandbox = {};
} else if (binding.isContext(sandbox)) {
} else if (isContext(sandbox)) {
return sandbox;
}
binding.makeContext(sandbox);
makeContext(sandbox);
return sandbox;
}
@ -99,16 +105,33 @@ function sigintHandlersWrap(fn, thisArg, argsArray) {
}
}
function runInDebugContext(code) {
return binding.runInDebugContext(code);
}
function runInContext(code, contextifiedSandbox, options) {
if (typeof options === 'string') {
options = {
filename: options,
[kParsingContext]: contextifiedSandbox
};
} else {
options = Object.assign({}, options, {
[kParsingContext]: contextifiedSandbox
});
}
return createScript(code, options)
.runInContext(contextifiedSandbox, options);
}
function runInNewContext(code, sandbox, options) {
sandbox = createContext(sandbox);
if (typeof options === 'string') {
options = {
filename: options,
[kParsingContext]: sandbox
};
} else {
options = Object.assign({}, options, {
[kParsingContext]: sandbox
});
}
return createScript(code, options).runInNewContext(sandbox, options);
}
@ -124,5 +147,5 @@ module.exports = {
runInContext,
runInNewContext,
runInThisContext,
isContext: binding.isContext
isContext
};

1
src/env.h

@ -318,6 +318,7 @@ struct http2_state;
V(tls_wrap_constructor_function, v8::Function) \
V(tty_constructor_template, v8::FunctionTemplate) \
V(udp_constructor_function, v8::Function) \
V(vm_parsing_context_symbol, v8::Symbol) \
V(url_constructor_function, v8::Function) \
V(write_wrap_constructor_function, v8::Function) \

49
src/node_contextify.cc

@ -61,6 +61,7 @@ using v8::Script;
using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::String;
using v8::Symbol;
using v8::TryCatch;
using v8::Uint8Array;
using v8::UnboundScript;
@ -531,6 +532,16 @@ class ContextifyScript : public BaseObject {
target->Set(class_name, script_tmpl->GetFunction());
env->set_script_context_constructor_template(script_tmpl);
Local<Symbol> parsing_context_symbol =
Symbol::New(env->isolate(),
FIXED_ONE_BYTE_STRING(env->isolate(),
"script parsing context"));
env->set_vm_parsing_context_symbol(parsing_context_symbol);
target->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "kParsingContext"),
parsing_context_symbol)
.FromJust();
}
@ -555,6 +566,7 @@ class ContextifyScript : public BaseObject {
Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, options);
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
MaybeLocal<Context> maybe_context = GetContext(env, options);
if (try_catch.HasCaught()) {
try_catch.ReThrow();
return;
@ -583,6 +595,8 @@ class ContextifyScript : public BaseObject {
else if (produce_cached_data)
compile_options = ScriptCompiler::kProduceCodeCache;
Context::Scope scope(maybe_context.FromMaybe(env->context()));
MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
env->isolate(),
&source,
@ -935,6 +949,41 @@ class ContextifyScript : public BaseObject {
return value->ToInteger(env->context());
}
static MaybeLocal<Context> GetContext(Environment* env,
Local<Value> options) {
if (!options->IsObject())
return MaybeLocal<Context>();
MaybeLocal<Value> maybe_value =
options.As<Object>()->Get(env->context(),
env->vm_parsing_context_symbol());
Local<Value> value;
if (!maybe_value.ToLocal(&value))
return MaybeLocal<Context>();
if (!value->IsObject()) {
if (!value->IsNullOrUndefined()) {
env->ThrowTypeError(
"contextifiedSandbox argument must be an object.");
}
return MaybeLocal<Context>();
}
ContextifyContext* sandbox =
ContextifyContext::ContextFromContextifiedSandbox(
env, value.As<Object>());
if (!sandbox) {
env->ThrowTypeError(
"sandbox argument must have been converted to a context.");
return MaybeLocal<Context>();
}
Local<Context> context = sandbox->context();
if (context.IsEmpty())
return MaybeLocal<Context>();
return context;
}
static bool EvalMachine(Environment* env,
const int64_t timeout,

101
test/inspector/test-scriptparsed-context.js

@ -0,0 +1,101 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
common.crashOnUnhandledRejection();
const { NodeInstance } = require('./inspector-helper.js');
const assert = require('assert');
const script = `
'use strict';
const assert = require('assert');
const vm = require('vm');
const { kParsingContext } = process.binding('contextify');
debugger;
global.outer = true;
global.inner = false;
const context = vm.createContext({
outer: false,
inner: true
});
debugger;
const scriptMain = new vm.Script("outer");
debugger;
const scriptContext = new vm.Script("inner", {
[kParsingContext]: context
});
debugger;
assert.strictEqual(scriptMain.runInThisContext(), true);
assert.strictEqual(scriptMain.runInContext(context), false);
assert.strictEqual(scriptContext.runInThisContext(), false);
assert.strictEqual(scriptContext.runInContext(context), true);
debugger;
vm.runInContext('inner', context);
debugger;
vm.runInNewContext('Array', {});
debugger;
`;
async function getContext(session) {
const created =
await session.waitForNotification('Runtime.executionContextCreated');
return created.params.context;
}
async function checkScriptContext(session, context) {
const scriptParsed =
await session.waitForNotification('Debugger.scriptParsed');
assert.strictEqual(scriptParsed.params.executionContextId, context.id);
}
async function runTests() {
const instance = new NodeInstance(['--inspect-brk=0', '--expose-internals'],
script);
const session = await instance.connectInspectorSession();
await session.send([
{ 'method': 'Debugger.enable' },
{ 'method': 'Runtime.runIfWaitingForDebugger' }
]);
await session.waitForBreakOnLine(5, '[eval]');
await session.send({ 'method': 'Runtime.enable' });
const topContext = await getContext(session);
await session.send({ 'method': 'Debugger.resume' });
const childContext = await getContext(session);
await session.waitForBreakOnLine(13, '[eval]');
console.error('[test]', 'Script associated with current context by default');
await session.send({ 'method': 'Debugger.resume' });
await checkScriptContext(session, topContext);
await session.waitForBreakOnLine(16, '[eval]');
console.error('[test]', 'Script associated with selected context');
await session.send({ 'method': 'Debugger.resume' });
await checkScriptContext(session, childContext);
await session.waitForBreakOnLine(21, '[eval]');
console.error('[test]', 'Script is unbound');
await session.send({ 'method': 'Debugger.resume' });
await session.waitForBreakOnLine(27, '[eval]');
console.error('[test]', 'vm.runInContext associates script with context');
await session.send({ 'method': 'Debugger.resume' });
await checkScriptContext(session, childContext);
await session.waitForBreakOnLine(30, '[eval]');
console.error('[test]', 'vm.runInNewContext associates script with context');
await session.send({ 'method': 'Debugger.resume' });
const thirdContext = await getContext(session);
await checkScriptContext(session, thirdContext);
await session.waitForBreakOnLine(33, '[eval]');
await session.runToCompletion();
assert.strictEqual(0, (await instance.expectShutdown()).exitCode);
}
runTests();
Loading…
Cancel
Save