| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a4bebf8 commit db80eac
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -229,6 +229,9 @@ overhead. | |||
| 229 | 229 | <!-- YAML | |
| 230 | 230 | added: v0.3.1 | |
| 231 | 231 | changes: | |
| 232 | + - version: REPLACEME | ||
| 233 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 234 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 232 | 235 | - version: v14.6.0 | |
| 233 | 236 | pr-url: https://github.com/nodejs/node/pull/34023 | |
| 234 | 237 | description: The `microtaskMode` option is supported now. | |
@@ -240,8 +243,9 @@ changes: | |||
| 240 | 243 | description: The `breakOnSigint` option is supported now. | |
| 241 | 244 | --> | |
| 242 | 245 | ||
| 243 | - * `contextObject` {Object} An object that will be [contextified][]. If | ||
| 244 | - `undefined`, a new object will be created. | ||
| 246 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 247 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 248 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 245 | 249 | * `options` {Object} | |
| 246 | 250 | * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs | |
| 247 | 251 | while compiling the `code`, the line of code causing the error is attached | |
@@ -275,9 +279,16 @@ changes: | |||
| 275 | 279 | `breakOnSigint` scopes in that case. | |
| 276 | 280 | * Returns: {any} the result of the very last statement executed in the script. | |
| 277 | 281 | ||
| 278 | - First contextifies the given `contextObject`, runs the compiled code contained | ||
| 279 | - by the `vm.Script` object within the created context, and returns the result. | ||
| 280 | - Running code does not have access to local scope. | ||
| 282 | + This method is a shortcut to `script.runInContext(vm.createContext(options), options)`. | ||
| 283 | + It does several things at once: | ||
| 284 | + | ||
| 285 | + 1. Creates a new context. | ||
| 286 | + 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| 287 | + If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| 288 | + If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 289 | + 3. Runs the compiled code contained by the `vm.Script` object within the created context. The code | ||
| 290 | + does not have access to the scope in which this method is called. | ||
| 291 | + 4. Returns the result. | ||
| 281 | 292 | ||
| 282 | 293 | The following example compiles code that sets a global variable, then executes | |
| 283 | 294 | the code multiple times in different contexts. The globals are set on and | |
@@ -295,6 +306,12 @@ contexts.forEach((context) => { | |||
| 295 | 306 | ||
| 296 | 307 | console.log(contexts); | |
| 297 | 308 | // Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] | |
| 309 | + | ||
| 310 | + // This would throw if the context is created from a contextified object. | ||
| 311 | + // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary | ||
| 312 | + // global objects that can be frozen. | ||
| 313 | + const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;'); | ||
| 314 | + const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY); | ||
| 298 | 315 | ``` | |
| 299 | 316 | ||
| 300 | 317 | ### `script.runInThisContext([options])` | |
@@ -1072,6 +1089,10 @@ For detailed information, see | |||
| 1072 | 1089 | <!-- YAML | |
| 1073 | 1090 | added: v0.3.1 | |
| 1074 | 1091 | changes: | |
| 1092 | + - version: | ||
| 1093 | + - REPLACEME | ||
| 1094 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 1095 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 1075 | 1096 | - version: | |
| 1076 | 1097 | - v21.7.0 | |
| 1077 | 1098 | - v20.12.0 | |
@@ -1094,7 +1115,9 @@ changes: | |||
| 1094 | 1115 | description: The `codeGeneration` option is supported now. | |
| 1095 | 1116 | --> | |
| 1096 | 1117 | ||
| 1097 | - * `contextObject` {Object} | ||
| 1118 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 1119 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 1120 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 1098 | 1121 | * `options` {Object} | |
| 1099 | 1122 | * `name` {string} Human-readable name of the newly created context. | |
| 1100 | 1123 | **Default:** `'VM Context i'`, where `i` is an ascending numerical index of | |
@@ -1124,10 +1147,10 @@ changes: | |||
| 1124 | 1147 | [Support of dynamic `import()` in compilation APIs][]. | |
| 1125 | 1148 | * Returns: {Object} contextified object. | |
| 1126 | 1149 | ||
| 1127 | - If given a `contextObject`, the `vm.createContext()` method will [prepare that | ||
| 1150 | + If the given `contextObject` is an object, the `vm.createContext()` method will [prepare that | ||
| 1128 | 1151 | object][contextified] and return a reference to it so that it can be used in | |
| 1129 | 1152 | calls to [`vm.runInContext()`][] or [`script.runInContext()`][]. Inside such | |
| 1130 | - scripts, the `contextObject` will be the global object, retaining all of its | ||
| 1153 | + scripts, the global object will be wrapped by the `contextObject`, retaining all of its | ||
| 1131 | 1154 | existing properties but also having the built-in objects and functions any | |
| 1132 | 1155 | standard [global object][] has. Outside of scripts run by the vm module, global | |
| 1133 | 1156 | variables will remain unchanged. | |
@@ -1152,6 +1175,11 @@ console.log(global.globalVar); | |||
| 1152 | 1175 | If `contextObject` is omitted (or passed explicitly as `undefined`), a new, | |
| 1153 | 1176 | empty [contextified][] object will be returned. | |
| 1154 | 1177 | ||
| 1178 | + When the global object in the newly created context is [contextified][], it has some quirks | ||
| 1179 | + compared to ordinary global objects. For example, it cannot be frozen. To create a context | ||
| 1180 | + without the contextifying quirks, pass [`vm.constants.DONT_CONTEXTIFY`][] as the `contextObject` | ||
| 1181 | + argument. See the documentation of [`vm.constants.DONT_CONTEXTIFY`][] for details. | ||
| 1182 | + | ||
| 1155 | 1183 | The `vm.createContext()` method is primarily useful for creating a single | |
| 1156 | 1184 | context that can be used to run multiple scripts. For instance, if emulating a | |
| 1157 | 1185 | web browser, the method can be used to create a single context representing a | |
@@ -1171,7 +1199,8 @@ added: v0.11.7 | |||
| 1171 | 1199 | * Returns: {boolean} | |
| 1172 | 1200 | ||
| 1173 | 1201 | Returns `true` if the given `object` object has been [contextified][] using | |
| 1174 | - [`vm.createContext()`][]. | ||
| 1202 | + [`vm.createContext()`][], or if it's the global object of a context created | ||
| 1203 | + using [`vm.constants.DONT_CONTEXTIFY`][]. | ||
| 1175 | 1204 | ||
| 1176 | 1205 | ## `vm.measureMemory([options])` | |
| 1177 | 1206 | ||
@@ -1332,6 +1361,10 @@ console.log(contextObject); | |||
| 1332 | 1361 | <!-- YAML | |
| 1333 | 1362 | added: v0.3.1 | |
| 1334 | 1363 | changes: | |
| 1364 | + - version: | ||
| 1365 | + - REPLACEME | ||
| 1366 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 1367 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 1335 | 1368 | - version: | |
| 1336 | 1369 | - v21.7.0 | |
| 1337 | 1370 | - v20.12.0 | |
@@ -1356,8 +1389,9 @@ changes: | |||
| 1356 | 1389 | --> | |
| 1357 | 1390 | ||
| 1358 | 1391 | * `code` {string} The JavaScript code to compile and run. | |
| 1359 | - * `contextObject` {Object} An object that will be [contextified][]. If | ||
| 1360 | - `undefined`, a new object will be created. | ||
| 1392 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 1393 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 1394 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 1361 | 1395 | * `options` {Object|string} | |
| 1362 | 1396 | * `filename` {string} Specifies the filename used in stack traces produced | |
| 1363 | 1397 | by this script. **Default:** `'evalmachine.<anonymous>'`. | |
@@ -1407,13 +1441,21 @@ changes: | |||
| 1407 | 1441 | `breakOnSigint` scopes in that case. | |
| 1408 | 1442 | * Returns: {any} the result of the very last statement executed in the script. | |
| 1409 | 1443 | ||
| 1410 | - The `vm.runInNewContext()` first contextifies the given `contextObject` (or | ||
| 1411 | - creates a new `contextObject` if passed as `undefined`), compiles the `code`, | ||
| 1412 | - runs it within the created context, then returns the result. Running code | ||
| 1413 | - does not have access to the local scope. | ||
| 1414 | - | ||
| 1444 | + This method is a shortcut to | ||
| 1445 | + `(new vm.Script(code, options)).runInContext(vm.createContext(options), options)`. | ||
| 1415 | 1446 | If `options` is a string, then it specifies the filename. | |
| 1416 | 1447 | ||
| 1448 | + It does several things at once: | ||
| 1449 | + | ||
| 1450 | + 1. Creates a new context. | ||
| 1451 | + 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| 1452 | + If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| 1453 | + If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 1454 | + 3. Compiles the code as a`vm.Script` | ||
| 1455 | + 4. Runs the compield code within the created context. The code does not have access to the scope in | ||
| 1456 | + which this method is called. | ||
| 1457 | + 5. Returns the result. | ||
| 1458 | + | ||
| 1417 | 1459 | The following example compiles and executes code that increments a global | |
| 1418 | 1460 | variable and sets a new one. These globals are contained in the `contextObject`. | |
| 1419 | 1461 | ||
@@ -1428,6 +1470,11 @@ const contextObject = { | |||
| 1428 | 1470 | vm.runInNewContext('count += 1; name = "kitty"', contextObject); | |
| 1429 | 1471 | console.log(contextObject); | |
| 1430 | 1472 | // Prints: { animal: 'cat', count: 3, name: 'kitty' } | |
| 1473 | + | ||
| 1474 | + // This would throw if the context is created from a contextified object. | ||
| 1475 | + // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that | ||
| 1476 | + // can be frozen. | ||
| 1477 | + const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY); | ||
| 1431 | 1478 | ``` | |
| 1432 | 1479 | ||
| 1433 | 1480 | ## `vm.runInThisContext(code[, options])` | |
@@ -1555,13 +1602,85 @@ According to the [V8 Embedder's Guide][]: | |||
| 1555 | 1602 | > JavaScript applications to run in a single instance of V8. You must explicitly | |
| 1556 | 1603 | > specify the context in which you want any JavaScript code to be run. | |
| 1557 | 1604 | ||
| 1558 | - When the method `vm.createContext()` is called, the `contextObject` argument | ||
| 1559 | - (or a newly-created object if `contextObject` is `undefined`) is associated | ||
| 1560 | - internally with a new instance of a V8 Context. This V8 Context provides the | ||
| 1561 | - `code` run using the `node:vm` module's methods with an isolated global | ||
| 1562 | - environment within which it can operate. The process of creating the V8 Context | ||
| 1563 | - and associating it with the `contextObject` is what this document refers to as | ||
| 1564 | - "contextifying" the object. | ||
| 1605 | + When the method `vm.createContext()` is called with an object, the `contextObject` argument | ||
| 1606 | + will be used to wrap the global object of a new instance of a V8 Context | ||
| 1607 | + (if `contextObject` is `undefined`, a new object will be created from the current context | ||
| 1608 | + before its contextified). This V8 Context provides the `code` run using the `node:vm` | ||
| 1609 | + module's methods with an isolated global environment within which it can operate. | ||
| 1610 | + The process of creating the V8 Context and associating it with the `contextObject` | ||
| 1611 | + in the outer context is what this document refers to as "contextifying" the object. | ||
| 1612 | + | ||
| 1613 | + The contextifying would introduce some quirks to the `globalThis` value in the context. | ||
| 1614 | + For example, it cannot be frozen, and it is not reference equal to the `contextObject` | ||
| 1615 | + in the outer context. | ||
| 1616 | + | ||
| 1617 | + ```js | ||
| 1618 | + const vm = require('node:vm'); | ||
| 1619 | + | ||
| 1620 | + // An undefined `contextObject` option makes the global object contextified. | ||
| 1621 | + let context = vm.createContext(); | ||
| 1622 | + console.log(vm.runInContext('globalThis', context) === context); // false | ||
| 1623 | + // A contextified global object cannot be frozen. | ||
| 1624 | + try { | ||
| 1625 | + vm.runInContext('Object.freeze(globalThis);', context); | ||
| 1626 | + } catch(e) { | ||
| 1627 | + console.log(e); // TypeError: Cannot freeze | ||
| 1628 | + } | ||
| 1629 | + console.log(vm.runInContext('globalThis.foo = 1; foo;', context)); // 1 | ||
| 1630 | + ``` | ||
| 1631 | + | ||
| 1632 | + To create a context with an ordinary global object and get access to a global proxy in | ||
| 1633 | + the outer context with fewer quirks, specify `vm.constants.DONT_CONTEXTIFY` as the | ||
| 1634 | + `contextObject` argument. | ||
| 1635 | + | ||
| 1636 | + ### `vm.constants.DONT_CONTEXTIFY` | ||
| 1637 | + | ||
| 1638 | + This constant, when used as the `contextObject` argument in vm APIs, instructs Node.js to create | ||
| 1639 | + a context without wrapping its global object with another object in a Node.js-specific manner. | ||
| 1640 | + As a result, the `globalThis` value inside the new context would behave more closely to an ordinary | ||
| 1641 | + one. | ||
| 1642 | + | ||
| 1643 | + ```js | ||
| 1644 | + const vm = require('node:vm'); | ||
| 1645 | + | ||
| 1646 | + // Use vm.constants.DONT_CONTEXTIFY to freeze the global object. | ||
| 1647 | + const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| 1648 | + vm.runInContext('Object.freeze(globalThis);', context); | ||
| 1649 | + try { | ||
| 1650 | + vm.runInContext('bar = 1; bar;', context); | ||
| 1651 | + } catch(e) { | ||
| 1652 | + console.log(e); // Uncaught ReferenceError: bar is not defined | ||
| 1653 | + } | ||
| 1654 | + ``` | ||
| 1655 | + | ||
| 1656 | + When `vm.constants.DONT_CONTEXTIFY` is used as the `contextObject` argument to [`vm.createContext()`][], | ||
| 1657 | + the returned object is a proxy-like object to the global object in the newly created context with | ||
| 1658 | + fewer Node.js-specific quirks. It is reference equal to the `globalThis` value in the new context, | ||
| 1659 | + can be modified from outside the context, and can be used to access built-ins in the new context directly. | ||
| 1660 | + | ||
| 1661 | + ```js | ||
| 1662 | + const vm = require('node:vm'); | ||
| 1663 | + | ||
| 1664 | + const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| 1665 | + | ||
| 1666 | + // Returned object is reference equal to globalThis in the new context. | ||
| 1667 | + console.log(vm.runInContext('globalThis', context) === context); // true | ||
| 1668 | + | ||
| 1669 | + // Can be used to access globals in the new context directly. | ||
| 1670 | + console.log(context.Array); // [Function: Array] | ||
| 1671 | + vm.runInContext('foo = 1;', context); | ||
| 1672 | + console.log(context.foo); // 1 | ||
| 1673 | + context.bar = 1; | ||
| 1674 | + console.log(vm.runInContext('bar;', context)); // 1 | ||
| 1675 | + | ||
| 1676 | + // Can be frozen and it affects the inner context. | ||
| 1677 | + Object.freeze(context); | ||
| 1678 | + try { | ||
| 1679 | + vm.runInContext('baz = 1; baz;', context); | ||
| 1680 | + } catch(e) { | ||
| 1681 | + console.log(e); // Uncaught ReferenceError: baz is not defined | ||
| 1682 | + } | ||
| 1683 | + ``` | ||
| 1565 | 1684 | ||
| 1566 | 1685 | ## Timeout interactions with asynchronous tasks and Promises | |
| 1567 | 1686 | ||
@@ -1851,6 +1970,7 @@ const { Script, SyntheticModule } = require('node:vm'); | |||
| 1851 | 1970 | [`script.runInThisContext()`]: #scriptruninthiscontextoptions | |
| 1852 | 1971 | [`url.origin`]: url.md#urlorigin | |
| 1853 | 1972 | [`vm.compileFunction()`]: #vmcompilefunctioncode-params-options | |
| 1973 | + [`vm.constants.DONT_CONTEXTIFY`]: #vmconstantsdont_contextify | ||
| 1854 | 1974 | [`vm.createContext()`]: #vmcreatecontextcontextobject-options | |
| 1855 | 1975 | [`vm.runInContext()`]: #vmrunincontextcode-contextifiedobject-options | |
| 1856 | 1976 | [`vm.runInThisContext()`]: #vmruninthiscontextcode-options | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -65,6 +65,7 @@ const { | |||
| 65 | 65 | } = require('internal/vm'); | |
| 66 | 66 | const { | |
| 67 | 67 | vm_dynamic_import_main_context_default, | |
| 68 | + vm_context_no_contextify, | ||
| 68 | 69 | } = internalBinding('symbols'); | |
| 69 | 70 | const kParsingContext = Symbol('script parsing context'); | |
| 70 | 71 | ||
@@ -222,7 +223,7 @@ function getContextOptions(options) { | |||
| 222 | 223 | ||
| 223 | 224 | let defaultContextNameIndex = 1; | |
| 224 | 225 | function createContext(contextObject = {}, options = kEmptyObject) { | |
| 225 | - if (isContext(contextObject)) { | ||
| 226 | + if (contextObject !== vm_context_no_contextify && isContext(contextObject)) { | ||
| 226 | 227 | return contextObject; | |
| 227 | 228 | } | |
| 228 | 229 | ||
@@ -258,10 +259,10 @@ function createContext(contextObject = {}, options = kEmptyObject) { | |||
| 258 | 259 | const hostDefinedOptionId = | |
| 259 | 260 | getHostDefinedOptionId(importModuleDynamically, name); | |
| 260 | 261 | ||
| 261 | - makeContext(contextObject, name, origin, strings, wasm, microtaskQueue, hostDefinedOptionId); | ||
| 262 | + const result = makeContext(contextObject, name, origin, strings, wasm, microtaskQueue, hostDefinedOptionId); | ||
| 262 | 263 | // Register the context scope callback after the context was initialized. | |
| 263 | - registerImportModuleDynamically(contextObject, importModuleDynamically); | ||
| 264 | - return contextObject; | ||
| 264 | + registerImportModuleDynamically(result, importModuleDynamically); | ||
| 265 | + return result; | ||
| 265 | 266 | } | |
| 266 | 267 | ||
| 267 | 268 | function createScript(code, options) { | |
@@ -394,6 +395,7 @@ function measureMemory(options = kEmptyObject) { | |||
| 394 | 395 | const vmConstants = { | |
| 395 | 396 | __proto__: null, | |
| 396 | 397 | USE_MAIN_CONTEXT_DEFAULT_LOADER: vm_dynamic_import_main_context_default, | |
| 398 | + DONT_CONTEXTIFY: vm_context_no_contextify, | ||
| 397 | 399 | }; | |
| 398 | 400 | ||
| 399 | 401 | ObjectFreeze(vmConstants); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,6 +57,7 @@ | |||
| 57 | 57 | V(resource_symbol, "resource_symbol") \ | |
| 58 | 58 | V(trigger_async_id_symbol, "trigger_async_id_symbol") \ | |
| 59 | 59 | V(source_text_module_default_hdo, "source_text_module_default_hdo") \ | |
| 60 | + V(vm_context_no_contextify, "vm_context_no_contextify") \ | ||
| 60 | 61 | V(vm_dynamic_import_default_internal, "vm_dynamic_import_default_internal") \ | |
| 61 | 62 | V(vm_dynamic_import_main_context_default, \ | |
| 62 | 63 | "vm_dynamic_import_main_context_default") \ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments