GeezerVison is showing github.com — vm: support parsing a script in a specific context · nodejs/node@7d95dc3
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/vm.js +32-9Lines changed: 32 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 21 | 21 |
| 22 | 22 | 'use strict'; | 23 | 23 |
| 24 | | -const binding = process.binding('contextify'); | 25 | | -const Script = binding.ContextifyScript; | | 24 | +const { | | 25 | + ContextifyScript: Script, | | 26 | + kParsingContext, | | 27 | + | | 28 | + makeContext, | | 29 | + isContext, | | 30 | + runInDebugContext | | 31 | +} = process.binding('contextify'); | 26 | 32 |
| 27 | 33 | // The binding provides a few useful primitives: | 28 | 34 | // - Script(code, { filename = "evalmachine.anonymous", | @@ -62,11 +68,11 @@ Script.prototype.runInNewContext = function(sandbox, options) { | 62 | 68 | function createContext(sandbox) { | 63 | 69 | if (sandbox === undefined) { | 64 | 70 | sandbox = {}; | 65 | | - } else if (binding.isContext(sandbox)) { | | 71 | + } else if (isContext(sandbox)) { | 66 | 72 | return sandbox; | 67 | 73 | } | 68 | 74 |
| 69 | | - binding.makeContext(sandbox); | | 75 | + makeContext(sandbox); | 70 | 76 | return sandbox; | 71 | 77 | } | 72 | 78 |
| @@ -99,16 +105,33 @@ function sigintHandlersWrap(fn, thisArg, argsArray) { | 99 | 105 | } | 100 | 106 | } | 101 | 107 |
| 102 | | -function runInDebugContext(code) { | 103 | | - return binding.runInDebugContext(code); | 104 | | -} | 105 | | - | 106 | 108 | function runInContext(code, contextifiedSandbox, options) { | | 109 | + if (typeof options === 'string') { | | 110 | + options = { | | 111 | + filename: options, | | 112 | + [kParsingContext]: contextifiedSandbox | | 113 | + }; | | 114 | + } else { | | 115 | + options = Object.assign({}, options, { | | 116 | + [kParsingContext]: contextifiedSandbox | | 117 | + }); | | 118 | + } | 107 | 119 | return createScript(code, options) | 108 | 120 | .runInContext(contextifiedSandbox, options); | 109 | 121 | } | 110 | 122 |
| 111 | 123 | function runInNewContext(code, sandbox, options) { | | 124 | + sandbox = createContext(sandbox); | | 125 | + if (typeof options === 'string') { | | 126 | + options = { | | 127 | + filename: options, | | 128 | + [kParsingContext]: sandbox | | 129 | + }; | | 130 | + } else { | | 131 | + options = Object.assign({}, options, { | | 132 | + [kParsingContext]: sandbox | | 133 | + }); | | 134 | + } | 112 | 135 | return createScript(code, options).runInNewContext(sandbox, options); | 113 | 136 | } | 114 | 137 |
| @@ -124,5 +147,5 @@ module.exports = { | 124 | 147 | runInContext, | 125 | 148 | runInNewContext, | 126 | 149 | runInThisContext, | 127 | | - isContext: binding.isContext | | 150 | + isContext | 128 | 151 | }; |
Collapse file Copy file name to clipboardExpand all lines: src/env.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -316,6 +316,7 @@ namespace node { | 316 | 316 | V(tls_wrap_constructor_function, v8::Function) \ | 317 | 317 | V(tty_constructor_template, v8::FunctionTemplate) \ | 318 | 318 | V(udp_constructor_function, v8::Function) \ | | 319 | + V(vm_parsing_context_symbol, v8::Symbol) \ | 319 | 320 | V(url_constructor_function, v8::Function) \ | 320 | 321 | V(write_wrap_constructor_function, v8::Function) \ | 321 | 322 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/node_contextify.cc +49Lines changed: 49 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -61,6 +61,7 @@ using v8::Script; | 61 | 61 | using v8::ScriptCompiler; | 62 | 62 | using v8::ScriptOrigin; | 63 | 63 | using v8::String; | | 64 | +using v8::Symbol; | 64 | 65 | using v8::TryCatch; | 65 | 66 | using v8::Uint8Array; | 66 | 67 | using v8::UnboundScript; | @@ -531,6 +532,16 @@ class ContextifyScript : public BaseObject { | 531 | 532 |
| 532 | 533 | target->Set(class_name, script_tmpl->GetFunction()); | 533 | 534 | env->set_script_context_constructor_template(script_tmpl); | | 535 | + | | 536 | + Local<Symbol> parsing_context_symbol = | | 537 | + Symbol::New(env->isolate(), | | 538 | + FIXED_ONE_BYTE_STRING(env->isolate(), | | 539 | + "script parsing context")); | | 540 | + env->set_vm_parsing_context_symbol(parsing_context_symbol); | | 541 | + target->Set(env->context(), | | 542 | + FIXED_ONE_BYTE_STRING(env->isolate(), "kParsingContext"), | | 543 | + parsing_context_symbol) | | 544 | + .FromJust(); | 534 | 545 | } | 535 | 546 |
| 536 | 547 |
| @@ -555,6 +566,7 @@ class ContextifyScript : public BaseObject { | 555 | 566 | Maybe<bool> maybe_display_errors = GetDisplayErrorsArg(env, options); | 556 | 567 | MaybeLocal<Uint8Array> cached_data_buf = GetCachedData(env, options); | 557 | 568 | Maybe<bool> maybe_produce_cached_data = GetProduceCachedData(env, options); | | 569 | + MaybeLocal<Context> maybe_context = GetContext(env, options); | 558 | 570 | if (try_catch.HasCaught()) { | 559 | 571 | try_catch.ReThrow(); | 560 | 572 | return; | @@ -583,6 +595,8 @@ class ContextifyScript : public BaseObject { | 583 | 595 | else if (produce_cached_data) | 584 | 596 | compile_options = ScriptCompiler::kProduceCodeCache; | 585 | 597 |
| | 598 | + Context::Scope scope(maybe_context.FromMaybe(env->context())); | | 599 | + | 586 | 600 | MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript( | 587 | 601 | env->isolate(), | 588 | 602 | &source, | @@ -935,6 +949,41 @@ class ContextifyScript : public BaseObject { | 935 | 949 | return value->ToInteger(env->context()); | 936 | 950 | } | 937 | 951 |
| | 952 | + static MaybeLocal<Context> GetContext(Environment* env, | | 953 | + Local<Value> options) { | | 954 | + if (!options->IsObject()) | | 955 | + return MaybeLocal<Context>(); | | 956 | + | | 957 | + MaybeLocal<Value> maybe_value = | | 958 | + options.As<Object>()->Get(env->context(), | | 959 | + env->vm_parsing_context_symbol()); | | 960 | + Local<Value> value; | | 961 | + if (!maybe_value.ToLocal(&value)) | | 962 | + return MaybeLocal<Context>(); | | 963 | + | | 964 | + if (!value->IsObject()) { | | 965 | + if (!value->IsNullOrUndefined()) { | | 966 | + env->ThrowTypeError( | | 967 | + "contextifiedSandbox argument must be an object."); | | 968 | + } | | 969 | + return MaybeLocal<Context>(); | | 970 | + } | | 971 | + | | 972 | + ContextifyContext* sandbox = | | 973 | + ContextifyContext::ContextFromContextifiedSandbox( | | 974 | + env, value.As<Object>()); | | 975 | + if (!sandbox) { | | 976 | + env->ThrowTypeError( | | 977 | + "sandbox argument must have been converted to a context."); | | 978 | + return MaybeLocal<Context>(); | | 979 | + } | | 980 | + | | 981 | + Local<Context> context = sandbox->context(); | | 982 | + if (context.IsEmpty()) | | 983 | + return MaybeLocal<Context>(); | | 984 | + return context; | | 985 | + } | | 986 | + | 938 | 987 |
| 939 | 988 | static bool EvalMachine(Environment* env, | 940 | 989 | const int64_t timeout, | |
Collapse file Copy file name to clipboard +101Lines changed: 101 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +const common = require('../common'); | | 3 | +common.skipIfInspectorDisabled(); | | 4 | +common.crashOnUnhandledRejection(); | | 5 | +const { NodeInstance } = require('./inspector-helper.js'); | | 6 | +const assert = require('assert'); | | 7 | + | | 8 | +const script = ` | | 9 | + 'use strict'; | | 10 | + const assert = require('assert'); | | 11 | + const vm = require('vm'); | | 12 | + const { kParsingContext } = process.binding('contextify'); | | 13 | + debugger; | | 14 | +
| | 15 | + global.outer = true; | | 16 | + global.inner = false; | | 17 | + const context = vm.createContext({ | | 18 | + outer: false, | | 19 | + inner: true | | 20 | + }); | | 21 | + debugger; | | 22 | +
| | 23 | + const scriptMain = new vm.Script("outer"); | | 24 | + debugger; | | 25 | +
| | 26 | + const scriptContext = new vm.Script("inner", { | | 27 | + [kParsingContext]: context | | 28 | + }); | | 29 | + debugger; | | 30 | +
| | 31 | + assert.strictEqual(scriptMain.runInThisContext(), true); | | 32 | + assert.strictEqual(scriptMain.runInContext(context), false); | | 33 | + assert.strictEqual(scriptContext.runInThisContext(), false); | | 34 | + assert.strictEqual(scriptContext.runInContext(context), true); | | 35 | + debugger; | | 36 | +
| | 37 | + vm.runInContext('inner', context); | | 38 | + debugger; | | 39 | +
| | 40 | + vm.runInNewContext('Array', {}); | | 41 | + debugger; | | 42 | +`; | | 43 | + | | 44 | +async function getContext(session) { | | 45 | + const created = | | 46 | + await session.waitForNotification('Runtime.executionContextCreated'); | | 47 | + return created.params.context; | | 48 | +} | | 49 | + | | 50 | +async function checkScriptContext(session, context) { | | 51 | + const scriptParsed = | | 52 | + await session.waitForNotification('Debugger.scriptParsed'); | | 53 | + assert.strictEqual(scriptParsed.params.executionContextId, context.id); | | 54 | +} | | 55 | + | | 56 | +async function runTests() { | | 57 | + const instance = new NodeInstance(['--inspect-brk=0', '--expose-internals'], | | 58 | + script); | | 59 | + const session = await instance.connectInspectorSession(); | | 60 | + await session.send([ | | 61 | + { 'method': 'Debugger.enable' }, | | 62 | + { 'method': 'Runtime.runIfWaitingForDebugger' } | | 63 | + ]); | | 64 | + await session.waitForBreakOnLine(5, '[eval]'); | | 65 | + | | 66 | + await session.send({ 'method': 'Runtime.enable' }); | | 67 | + const topContext = await getContext(session); | | 68 | + await session.send({ 'method': 'Debugger.resume' }); | | 69 | + const childContext = await getContext(session); | | 70 | + await session.waitForBreakOnLine(13, '[eval]'); | | 71 | + | | 72 | + console.error('[test]', 'Script associated with current context by default'); | | 73 | + await session.send({ 'method': 'Debugger.resume' }); | | 74 | + await checkScriptContext(session, topContext); | | 75 | + await session.waitForBreakOnLine(16, '[eval]'); | | 76 | + | | 77 | + console.error('[test]', 'Script associated with selected context'); | | 78 | + await session.send({ 'method': 'Debugger.resume' }); | | 79 | + await checkScriptContext(session, childContext); | | 80 | + await session.waitForBreakOnLine(21, '[eval]'); | | 81 | + | | 82 | + console.error('[test]', 'Script is unbound'); | | 83 | + await session.send({ 'method': 'Debugger.resume' }); | | 84 | + await session.waitForBreakOnLine(27, '[eval]'); | | 85 | + | | 86 | + console.error('[test]', 'vm.runInContext associates script with context'); | | 87 | + await session.send({ 'method': 'Debugger.resume' }); | | 88 | + await checkScriptContext(session, childContext); | | 89 | + await session.waitForBreakOnLine(30, '[eval]'); | | 90 | + | | 91 | + console.error('[test]', 'vm.runInNewContext associates script with context'); | | 92 | + await session.send({ 'method': 'Debugger.resume' }); | | 93 | + const thirdContext = await getContext(session); | | 94 | + await checkScriptContext(session, thirdContext); | | 95 | + await session.waitForBreakOnLine(33, '[eval]'); | | 96 | + | | 97 | + await session.runToCompletion(); | | 98 | + assert.strictEqual(0, (await instance.expectShutdown()).exitCode); | | 99 | +} | | 100 | + | | 101 | +runTests(); |
|