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>
This commit is contained in:
parent
86e7c61a07
commit
d932e80231
41
lib/vm.js
41
lib/vm.js
@ -21,8 +21,14 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const binding = process.binding('contextify');
|
const {
|
||||||
const Script = binding.ContextifyScript;
|
ContextifyScript: Script,
|
||||||
|
kParsingContext,
|
||||||
|
|
||||||
|
makeContext,
|
||||||
|
isContext,
|
||||||
|
runInDebugContext
|
||||||
|
} = process.binding('contextify');
|
||||||
|
|
||||||
// The binding provides a few useful primitives:
|
// The binding provides a few useful primitives:
|
||||||
// - Script(code, { filename = "evalmachine.anonymous",
|
// - Script(code, { filename = "evalmachine.anonymous",
|
||||||
@ -62,11 +68,11 @@ Script.prototype.runInNewContext = function(sandbox, options) {
|
|||||||
function createContext(sandbox) {
|
function createContext(sandbox) {
|
||||||
if (sandbox === undefined) {
|
if (sandbox === undefined) {
|
||||||
sandbox = {};
|
sandbox = {};
|
||||||
} else if (binding.isContext(sandbox)) {
|
} else if (isContext(sandbox)) {
|
||||||
return sandbox;
|
return sandbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.makeContext(sandbox);
|
makeContext(sandbox);
|
||||||
return sandbox;
|
return sandbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,16 +105,33 @@ function sigintHandlersWrap(fn, thisArg, argsArray) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runInDebugContext(code) {
|
|
||||||
return binding.runInDebugContext(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
function runInContext(code, contextifiedSandbox, options) {
|
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)
|
return createScript(code, options)
|
||||||
.runInContext(contextifiedSandbox, options);
|
.runInContext(contextifiedSandbox, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runInNewContext(code, sandbox, 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);
|
return createScript(code, options).runInNewContext(sandbox, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,5 +147,5 @@ module.exports = {
|
|||||||
runInContext,
|
runInContext,
|
||||||
runInNewContext,
|
runInNewContext,
|
||||||
runInThisContext,
|
runInThisContext,
|
||||||
isContext: binding.isContext
|
isContext
|
||||||
};
|
};
|
||||||
|
@ -318,6 +318,7 @@ struct http2_state;
|
|||||||
V(tls_wrap_constructor_function, v8::Function) \
|
V(tls_wrap_constructor_function, v8::Function) \
|
||||||
V(tty_constructor_template, v8::FunctionTemplate) \
|
V(tty_constructor_template, v8::FunctionTemplate) \
|
||||||
V(udp_constructor_function, v8::Function) \
|
V(udp_constructor_function, v8::Function) \
|
||||||
|
V(vm_parsing_context_symbol, v8::Symbol) \
|
||||||
V(url_constructor_function, v8::Function) \
|
V(url_constructor_function, v8::Function) \
|
||||||
V(write_wrap_constructor_function, v8::Function) \
|
V(write_wrap_constructor_function, v8::Function) \
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ using v8::Script;
|
|||||||
using v8::ScriptCompiler;
|
using v8::ScriptCompiler;
|
||||||
using v8::ScriptOrigin;
|
using v8::ScriptOrigin;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
|
using v8::Symbol;
|
||||||
using v8::TryCatch;
|
using v8::TryCatch;
|
||||||
using v8::Uint8Array;
|
using v8::Uint8Array;
|
||||||
using v8::UnboundScript;
|
using v8::UnboundScript;
|
||||||
@ -531,6 +532,16 @@ class ContextifyScript : public BaseObject {
|
|||||||
|
|
||||||
target->Set(class_name, script_tmpl->GetFunction());
|
target->Set(class_name, script_tmpl->GetFunction());
|
||||||
env->set_script_context_constructor_template(script_tmpl);
|
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);
|
Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, options);
|
||||||
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
|
MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options);
|
||||||
Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
|
Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options);
|
||||||
|
MaybeLocal<Context> maybe_context = GetContext(env, options);
|
||||||
if (try_catch.HasCaught()) {
|
if (try_catch.HasCaught()) {
|
||||||
try_catch.ReThrow();
|
try_catch.ReThrow();
|
||||||
return;
|
return;
|
||||||
@ -583,6 +595,8 @@ class ContextifyScript : public BaseObject {
|
|||||||
else if (produce_cached_data)
|
else if (produce_cached_data)
|
||||||
compile_options = ScriptCompiler::kProduceCodeCache;
|
compile_options = ScriptCompiler::kProduceCodeCache;
|
||||||
|
|
||||||
|
Context::Scope scope(maybe_context.FromMaybe(env->context()));
|
||||||
|
|
||||||
MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
|
MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript(
|
||||||
env->isolate(),
|
env->isolate(),
|
||||||
&source,
|
&source,
|
||||||
@ -935,6 +949,41 @@ class ContextifyScript : public BaseObject {
|
|||||||
return value->ToInteger(env->context());
|
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,
|
static bool EvalMachine(Environment* env,
|
||||||
const int64_t timeout,
|
const int64_t timeout,
|
||||||
|
101
test/inspector/test-scriptparsed-context.js
Normal file
101
test/inspector/test-scriptparsed-context.js
Normal file
@ -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…
x
Reference in New Issue
Block a user