Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.3k
vm: introduce vanilla contexts via vm.constants.DONT_CONTEXTIFY#54394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
4 commits Select commit Hold shift + click to select a range
e5b0f87 vm: introduce vanilla contexts via vm.constants.DONT_CONTEXTIFY
joyeecheung c6cb027 fixup! vm: introduce vanilla contexts via vm.constants.DONT_CONTEXTIFY
joyeecheung 85c7090 fixup! fixup! vm: introduce vanilla contexts via vm.constants.DONT_CO…
joyeecheung d43c426 fixup! vm: introduce vanilla contexts via vm.constants.DONT_CONTEXTIFY
joyeecheung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -229,6 +229,9 @@ overhead. | ||
| <!-- YAML | ||
| added: v0.3.1 | ||
| changes: | ||
| - version: REPLACEME | ||
| pr-url: https://github.com/nodejs/node/pull/54394 | ||
| description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| - version: v14.6.0 | ||
| pr-url: https://github.com/nodejs/node/pull/34023 | ||
| description: The `microtaskMode` option is supported now. | ||
| @@ -240,8 +243,9 @@ changes: | ||
| description: The `breakOnSigint` option is supported now. | ||
| --> | ||
| * `contextObject`{Object} An object that will be [contextified][]. If | ||
| `undefined`, a new object will be created. | ||
| * `contextObject`{Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| * `options`{Object} | ||
| * `displayErrors`{boolean} When `true`, if an [`Error`][] occurs | ||
| while compiling the `code`, the line of code causing the error is attached | ||
| @@ -275,9 +279,16 @@ changes: | ||
| `breakOnSigint` scopes in that case. | ||
| * Returns:{any} the result of the very last statement executed in the script. | ||
| First contextifies the given `contextObject`, runs the compiled code contained | ||
| by the `vm.Script` object within the created context, and returns the result. | ||
| Running code does not have access to local scope. | ||
| This method is a shortcut to `script.runInContext(vm.createContext(options), options)`. | ||
| It does several things at once: | ||
| 1. Creates a new context. | ||
| 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 3. Runs the compiled code contained by the `vm.Script` object within the created context. The code | ||
| does not have access to the scope in which this method is called. | ||
| 4. Returns the result. | ||
| The following example compiles code that sets a global variable, then executes | ||
| the code multiple times in different contexts. The globals are set on and | ||
| @@ -295,6 +306,12 @@ contexts.forEach((context) =>{ | ||
| console.log(contexts); | ||
| // Prints: [{globalVar: 'set'},{globalVar: 'set'},{globalVar: 'set'}] | ||
| // This would throw if the context is created from a contextified object. | ||
| // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary | ||
| // global objects that can be frozen. | ||
| const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;'); | ||
| const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY); | ||
| ``` | ||
| ### `script.runInThisContext([options])` | ||
| @@ -1072,6 +1089,10 @@ For detailed information, see | ||
| <!-- YAML | ||
| added: v0.3.1 | ||
| changes: | ||
| - version: | ||
| - REPLACEME | ||
| pr-url: https://github.com/nodejs/node/pull/54394 | ||
| description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| - version: | ||
| - v21.7.0 | ||
| - v20.12.0 | ||
| @@ -1094,7 +1115,9 @@ changes: | ||
| description: The `codeGeneration` option is supported now. | ||
| --> | ||
| * `contextObject`{Object} | ||
| * `contextObject`{Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| * `options`{Object} | ||
| * `name`{string} Human-readable name of the newly created context. | ||
| **Default:** `'VM Context i'`, where `i` is an ascending numerical index of | ||
| @@ -1124,10 +1147,10 @@ changes: | ||
| [Support of dynamic `import()` in compilation APIs][]. | ||
| * Returns:{Object} contextified object. | ||
| If given a `contextObject`, the `vm.createContext()` method will [prepare that | ||
| If the given `contextObject` is an object, the `vm.createContext()` method will [prepare that | ||
| object][contextified] and return a reference to it so that it can be used in | ||
| calls to [`vm.runInContext()`][] or [`script.runInContext()`][]. Inside such | ||
| scripts, the `contextObject` will be the global object, retaining all of its | ||
| scripts, the global object will be wrapped by the `contextObject`, retaining all of its | ||
| existing properties but also having the built-in objects and functions any | ||
| standard [global object][] has. Outside of scripts run by the vm module, global | ||
| variables will remain unchanged. | ||
| @@ -1152,6 +1175,11 @@ console.log(global.globalVar); | ||
| If `contextObject` is omitted (or passed explicitly as `undefined`), a new, | ||
| empty [contextified][] object will be returned. | ||
| When the global object in the newly created context is [contextified][], it has some quirks | ||
| compared to ordinary global objects. For example, it cannot be frozen. To create a context | ||
| without the contextifying quirks, pass [`vm.constants.DONT_CONTEXTIFY`][] as the `contextObject` | ||
| argument. See the documentation of [`vm.constants.DONT_CONTEXTIFY`][] for details. | ||
| The `vm.createContext()` method is primarily useful for creating a single | ||
| context that can be used to run multiple scripts. For instance, if emulating a | ||
| web browser, the method can be used to create a single context representing a | ||
| @@ -1171,7 +1199,8 @@ added: v0.11.7 | ||
| * Returns:{boolean} | ||
| Returns `true` if the given `object` object has been [contextified][] using | ||
| [`vm.createContext()`][]. | ||
| [`vm.createContext()`][], or if it's the global object of a context created | ||
| using [`vm.constants.DONT_CONTEXTIFY`][]. | ||
| ## `vm.measureMemory([options])` | ||
| @@ -1332,6 +1361,10 @@ console.log(contextObject); | ||
| <!-- YAML | ||
| added: v0.3.1 | ||
| changes: | ||
| - version: | ||
| - REPLACEME | ||
| pr-url: https://github.com/nodejs/node/pull/54394 | ||
| description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| - version: | ||
| - v21.7.0 | ||
| - v20.12.0 | ||
| @@ -1356,8 +1389,9 @@ changes: | ||
| --> | ||
| * `code`{string} The JavaScript code to compile and run. | ||
| * `contextObject`{Object} An object that will be [contextified][]. If | ||
| `undefined`, a new object will be created. | ||
| * `contextObject`{Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| * `options`{Object|string} | ||
| * `filename`{string} Specifies the filename used in stack traces produced | ||
| by this script. **Default:** `'evalmachine.<anonymous>'`. | ||
| @@ -1407,13 +1441,21 @@ changes: | ||
| `breakOnSigint` scopes in that case. | ||
| * Returns:{any} the result of the very last statement executed in the script. | ||
| The `vm.runInNewContext()` first contextifies the given `contextObject` (or | ||
| creates a new `contextObject` if passed as `undefined`), compiles the `code`, | ||
| runs it within the created context, then returns the result. Running code | ||
| does not have access to the local scope. | ||
| This method is a shortcut to | ||
| `(new vm.Script(code, options)).runInContext(vm.createContext(options), options)`. | ||
| If `options` is a string, then it specifies the filename. | ||
| It does several things at once: | ||
| 1. Creates a new context. | ||
| 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 3. Compiles the code as a`vm.Script` | ||
| 4. Runs the compield code within the created context. The code does not have access to the scope in | ||
| which this method is called. | ||
| 5. Returns the result. | ||
| The following example compiles and executes code that increments a global | ||
| variable and sets a new one. These globals are contained in the `contextObject`. | ||
| @@ -1428,6 +1470,11 @@ const contextObject ={ | ||
| vm.runInNewContext('count += 1; name = "kitty"', contextObject); | ||
| console.log(contextObject); | ||
| // Prints:{animal: 'cat', count: 3, name: 'kitty'} | ||
| // This would throw if the context is created from a contextified object. | ||
| // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that | ||
| // can be frozen. | ||
| const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY); | ||
| ``` | ||
| ## `vm.runInThisContext(code[, options])` | ||
| @@ -1555,13 +1602,85 @@ According to the [V8 Embedder's Guide][]: | ||
| > JavaScript applications to run in a single instance of V8. You must explicitly | ||
| > specify the context in which you want any JavaScript code to be run. | ||
| When the method `vm.createContext()` is called, the `contextObject` argument | ||
| (or a newly-created object if `contextObject` is `undefined`) is associated | ||
| internally with a new instance of a V8 Context. This V8 Context provides the | ||
| `code` run using the `node:vm` module's methods with an isolated global | ||
| environment within which it can operate. The process of creating the V8 Context | ||
| and associating it with the `contextObject` is what this document refers to as | ||
| "contextifying" the object. | ||
| When the method `vm.createContext()` is called with an object, the `contextObject` argument | ||
| will be used to wrap the global object of a new instance of a V8 Context | ||
| (if `contextObject` is `undefined`, a new object will be created from the current context | ||
| before its contextified). This V8 Context provides the `code` run using the `node:vm` | ||
| module's methods with an isolated global environment within which it can operate. | ||
| The process of creating the V8 Context and associating it with the `contextObject` | ||
| in the outer context is what this document refers to as "contextifying" the object. | ||
| The contextifying would introduce some quirks to the `globalThis` value in the context. | ||
| For example, it cannot be frozen, and it is not reference equal to the `contextObject` | ||
| in the outer context. | ||
| ```js | ||
| const vm = require('node:vm'); | ||
| // An undefined `contextObject` option makes the global object contextified. | ||
| let context = vm.createContext(); | ||
| console.log(vm.runInContext('globalThis', context) === context); // false | ||
| // A contextified global object cannot be frozen. | ||
| try{ | ||
| vm.runInContext('Object.freeze(globalThis);', context); | ||
| } catch(e){ | ||
| console.log(e); // TypeError: Cannot freeze | ||
| } | ||
| console.log(vm.runInContext('globalThis.foo = 1; foo;', context)); // 1 | ||
| ``` | ||
| To create a context with an ordinary global object and get access to a global proxy in | ||
| the outer context with fewer quirks, specify `vm.constants.DONT_CONTEXTIFY` as the | ||
| `contextObject` argument. | ||
| ### `vm.constants.DONT_CONTEXTIFY` | ||
| This constant, when used as the `contextObject` argument in vm APIs, instructs Node.js to create | ||
| a context without wrapping its global object with another object in a Node.js-specific manner. | ||
| As a result, the `globalThis` value inside the new context would behave more closely to an ordinary | ||
| one. | ||
| ```js | ||
| const vm = require('node:vm'); | ||
| // Use vm.constants.DONT_CONTEXTIFY to freeze the global object. | ||
| const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| vm.runInContext('Object.freeze(globalThis);', context); | ||
| try{ | ||
| vm.runInContext('bar = 1; bar;', context); | ||
| } catch(e){ | ||
| console.log(e); // Uncaught ReferenceError: bar is not defined | ||
| } | ||
| ``` | ||
| When `vm.constants.DONT_CONTEXTIFY` is used as the `contextObject` argument to [`vm.createContext()`][], | ||
| the returned object is a proxy-like object to the global object in the newly created context with | ||
joyeecheung marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| fewer Node.js-specific quirks. It is reference equal to the `globalThis` value in the new context, | ||
| can be modified from outside the context, and can be used to access built-ins in the new context directly. | ||
| ```js | ||
| const vm = require('node:vm'); | ||
| const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| // Returned object is reference equal to globalThis in the new context. | ||
| console.log(vm.runInContext('globalThis', context) === context); // true | ||
| // Can be used to access globals in the new context directly. | ||
| console.log(context.Array); // [Function: Array] | ||
| vm.runInContext('foo = 1;', context); | ||
| console.log(context.foo); // 1 | ||
| context.bar = 1; | ||
| console.log(vm.runInContext('bar;', context)); // 1 | ||
| // Can be frozen and it affects the inner context. | ||
| Object.freeze(context); | ||
| try{ | ||
| vm.runInContext('baz = 1; baz;', context); | ||
| } catch(e){ | ||
| console.log(e); // Uncaught ReferenceError: baz is not defined | ||
| } | ||
| ``` | ||
| ## Timeout interactions with asynchronous tasks and Promises | ||
| @@ -1851,6 +1970,7 @@ const{Script, SyntheticModule } = require('node:vm'); | ||
| [`script.runInThisContext()`]: #scriptruninthiscontextoptions | ||
| [`url.origin`]: url.md#urlorigin | ||
| [`vm.compileFunction()`]: #vmcompilefunctioncode-params-options | ||
| [`vm.constants.DONT_CONTEXTIFY`]: #vmconstantsdont_contextify | ||
| [`vm.createContext()`]: #vmcreatecontextcontextobject-options | ||
| [`vm.runInContext()`]: #vmrunincontextcode-contextifiedobject-options | ||
| [`vm.runInThisContext()`]: #vmruninthiscontextcode-options | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.