| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 10bea1c commit 2d90340
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -228,6 +228,9 @@ overhead. | |||
| 228 | 228 | <!-- YAML | |
| 229 | 229 | added: v0.3.1 | |
| 230 | 230 | changes: | |
| 231 | + - version: REPLACEME | ||
| 232 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 233 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 231 | 234 | - version: v14.6.0 | |
| 232 | 235 | pr-url: https://github.com/nodejs/node/pull/34023 | |
| 233 | 236 | description: The `microtaskMode` option is supported now. | |
@@ -239,8 +242,9 @@ changes: | |||
| 239 | 242 | description: The `breakOnSigint` option is supported now. | |
| 240 | 243 | --> | |
| 241 | 244 | ||
| 242 | - * `contextObject` {Object} An object that will be [contextified][]. If | ||
| 243 | - `undefined`, a new object will be created. | ||
| 245 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 246 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 247 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 244 | 248 | * `options` {Object} | |
| 245 | 249 | * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs | |
| 246 | 250 | while compiling the `code`, the line of code causing the error is attached | |
@@ -274,9 +278,16 @@ changes: | |||
| 274 | 278 | `breakOnSigint` scopes in that case. | |
| 275 | 279 | * Returns: {any} the result of the very last statement executed in the script. | |
| 276 | 280 | ||
| 277 | - First contextifies the given `contextObject`, runs the compiled code contained | ||
| 278 | - by the `vm.Script` object within the created context, and returns the result. | ||
| 279 | - Running code does not have access to local scope. | ||
| 281 | + This method is a shortcut to `script.runInContext(vm.createContext(options), options)`. | ||
| 282 | + It does several things at once: | ||
| 283 | + | ||
| 284 | + 1. Creates a new context. | ||
| 285 | + 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| 286 | + If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| 287 | + If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 288 | + 3. Runs the compiled code contained by the `vm.Script` object within the created context. The code | ||
| 289 | + does not have access to the scope in which this method is called. | ||
| 290 | + 4. Returns the result. | ||
| 280 | 291 | ||
| 281 | 292 | The following example compiles code that sets a global variable, then executes | |
| 282 | 293 | the code multiple times in different contexts. The globals are set on and | |
@@ -294,6 +305,12 @@ contexts.forEach((context) => { | |||
| 294 | 305 | ||
| 295 | 306 | console.log(contexts); | |
| 296 | 307 | // Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] | |
| 308 | + | ||
| 309 | + // This would throw if the context is created from a contextified object. | ||
| 310 | + // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary | ||
| 311 | + // global objects that can be frozen. | ||
| 312 | + const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;'); | ||
| 313 | + const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY); | ||
| 297 | 314 | ``` | |
| 298 | 315 | ||
| 299 | 316 | ### `script.runInThisContext([options])` | |
@@ -1063,6 +1080,10 @@ For detailed information, see | |||
| 1063 | 1080 | <!-- YAML | |
| 1064 | 1081 | added: v0.3.1 | |
| 1065 | 1082 | changes: | |
| 1083 | + - version: | ||
| 1084 | + - REPLACEME | ||
| 1085 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 1086 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 1066 | 1087 | - version: | |
| 1067 | 1088 | - v20.12.0 | |
| 1068 | 1089 | pr-url: https://github.com/nodejs/node/pull/51244 | |
@@ -1083,7 +1104,9 @@ changes: | |||
| 1083 | 1104 | description: The `codeGeneration` option is supported now. | |
| 1084 | 1105 | --> | |
| 1085 | 1106 | ||
| 1086 | - * `contextObject` {Object} | ||
| 1107 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 1108 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 1109 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 1087 | 1110 | * `options` {Object} | |
| 1088 | 1111 | * `name` {string} Human-readable name of the newly created context. | |
| 1089 | 1112 | **Default:** `'VM Context i'`, where `i` is an ascending numerical index of | |
@@ -1113,10 +1136,10 @@ changes: | |||
| 1113 | 1136 | [Support of dynamic `import()` in compilation APIs][]. | |
| 1114 | 1137 | * Returns: {Object} contextified object. | |
| 1115 | 1138 | ||
| 1116 | - If given a `contextObject`, the `vm.createContext()` method will [prepare that | ||
| 1139 | + If the given `contextObject` is an object, the `vm.createContext()` method will [prepare that | ||
| 1117 | 1140 | object][contextified] and return a reference to it so that it can be used in | |
| 1118 | 1141 | calls to [`vm.runInContext()`][] or [`script.runInContext()`][]. Inside such | |
| 1119 | - scripts, the `contextObject` will be the global object, retaining all of its | ||
| 1142 | + scripts, the global object will be wrapped by the `contextObject`, retaining all of its | ||
| 1120 | 1143 | existing properties but also having the built-in objects and functions any | |
| 1121 | 1144 | standard [global object][] has. Outside of scripts run by the vm module, global | |
| 1122 | 1145 | variables will remain unchanged. | |
@@ -1141,6 +1164,11 @@ console.log(global.globalVar); | |||
| 1141 | 1164 | If `contextObject` is omitted (or passed explicitly as `undefined`), a new, | |
| 1142 | 1165 | empty [contextified][] object will be returned. | |
| 1143 | 1166 | ||
| 1167 | + When the global object in the newly created context is [contextified][], it has some quirks | ||
| 1168 | + compared to ordinary global objects. For example, it cannot be frozen. To create a context | ||
| 1169 | + without the contextifying quirks, pass [`vm.constants.DONT_CONTEXTIFY`][] as the `contextObject` | ||
| 1170 | + argument. See the documentation of [`vm.constants.DONT_CONTEXTIFY`][] for details. | ||
| 1171 | + | ||
| 1144 | 1172 | The `vm.createContext()` method is primarily useful for creating a single | |
| 1145 | 1173 | context that can be used to run multiple scripts. For instance, if emulating a | |
| 1146 | 1174 | web browser, the method can be used to create a single context representing a | |
@@ -1160,7 +1188,8 @@ added: v0.11.7 | |||
| 1160 | 1188 | * Returns: {boolean} | |
| 1161 | 1189 | ||
| 1162 | 1190 | Returns `true` if the given `object` object has been [contextified][] using | |
| 1163 | - [`vm.createContext()`][]. | ||
| 1191 | + [`vm.createContext()`][], or if it's the global object of a context created | ||
| 1192 | + using [`vm.constants.DONT_CONTEXTIFY`][]. | ||
| 1164 | 1193 | ||
| 1165 | 1194 | ## `vm.measureMemory([options])` | |
| 1166 | 1195 | ||
@@ -1320,6 +1349,10 @@ console.log(contextObject); | |||
| 1320 | 1349 | <!-- YAML | |
| 1321 | 1350 | added: v0.3.1 | |
| 1322 | 1351 | changes: | |
| 1352 | + - version: | ||
| 1353 | + - REPLACEME | ||
| 1354 | + pr-url: https://github.com/nodejs/node/pull/54394 | ||
| 1355 | + description: The `contextObject` argument now accepts `vm.constants.DONT_CONTEXTIFY`. | ||
| 1323 | 1356 | - version: | |
| 1324 | 1357 | - v20.12.0 | |
| 1325 | 1358 | pr-url: https://github.com/nodejs/node/pull/51244 | |
@@ -1343,8 +1376,9 @@ changes: | |||
| 1343 | 1376 | --> | |
| 1344 | 1377 | ||
| 1345 | 1378 | * `code` {string} The JavaScript code to compile and run. | |
| 1346 | - * `contextObject` {Object} An object that will be [contextified][]. If | ||
| 1347 | - `undefined`, a new object will be created. | ||
| 1379 | + * `contextObject` {Object|vm.constants.DONT\_CONTEXTIFY|undefined} | ||
| 1380 | + Either [`vm.constants.DONT_CONTEXTIFY`][] or an object that will be [contextified][]. | ||
| 1381 | + If `undefined`, an empty contextified object will be created for backwards compatibility. | ||
| 1348 | 1382 | * `options` {Object|string} | |
| 1349 | 1383 | * `filename` {string} Specifies the filename used in stack traces produced | |
| 1350 | 1384 | by this script. **Default:** `'evalmachine.<anonymous>'`. | |
@@ -1394,13 +1428,21 @@ changes: | |||
| 1394 | 1428 | `breakOnSigint` scopes in that case. | |
| 1395 | 1429 | * Returns: {any} the result of the very last statement executed in the script. | |
| 1396 | 1430 | ||
| 1397 | - The `vm.runInNewContext()` first contextifies the given `contextObject` (or | ||
| 1398 | - creates a new `contextObject` if passed as `undefined`), compiles the `code`, | ||
| 1399 | - runs it within the created context, then returns the result. Running code | ||
| 1400 | - does not have access to the local scope. | ||
| 1401 | - | ||
| 1431 | + This method is a shortcut to | ||
| 1432 | + `(new vm.Script(code, options)).runInContext(vm.createContext(options), options)`. | ||
| 1402 | 1433 | If `options` is a string, then it specifies the filename. | |
| 1403 | 1434 | ||
| 1435 | + It does several things at once: | ||
| 1436 | + | ||
| 1437 | + 1. Creates a new context. | ||
| 1438 | + 2. If `contextObject` is an object, [contextifies][contextified] it with the new context. | ||
| 1439 | + If `contextObject` is undefined, creates a new object and [contextifies][contextified] it. | ||
| 1440 | + If `contextObject` is [`vm.constants.DONT_CONTEXTIFY`][], don't [contextify][contextified] anything. | ||
| 1441 | + 3. Compiles the code as a`vm.Script` | ||
| 1442 | + 4. Runs the compield code within the created context. The code does not have access to the scope in | ||
| 1443 | + which this method is called. | ||
| 1444 | + 5. Returns the result. | ||
| 1445 | + | ||
| 1404 | 1446 | The following example compiles and executes code that increments a global | |
| 1405 | 1447 | variable and sets a new one. These globals are contained in the `contextObject`. | |
| 1406 | 1448 | ||
@@ -1415,6 +1457,11 @@ const contextObject = { | |||
| 1415 | 1457 | vm.runInNewContext('count += 1; name = "kitty"', contextObject); | |
| 1416 | 1458 | console.log(contextObject); | |
| 1417 | 1459 | // Prints: { animal: 'cat', count: 3, name: 'kitty' } | |
| 1460 | + | ||
| 1461 | + // This would throw if the context is created from a contextified object. | ||
| 1462 | + // vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that | ||
| 1463 | + // can be frozen. | ||
| 1464 | + const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY); | ||
| 1418 | 1465 | ``` | |
| 1419 | 1466 | ||
| 1420 | 1467 | ## `vm.runInThisContext(code[, options])` | |
@@ -1541,13 +1588,85 @@ According to the [V8 Embedder's Guide][]: | |||
| 1541 | 1588 | > JavaScript applications to run in a single instance of V8. You must explicitly | |
| 1542 | 1589 | > specify the context in which you want any JavaScript code to be run. | |
| 1543 | 1590 | ||
| 1544 | - When the method `vm.createContext()` is called, the `contextObject` argument | ||
| 1545 | - (or a newly-created object if `contextObject` is `undefined`) is associated | ||
| 1546 | - internally with a new instance of a V8 Context. This V8 Context provides the | ||
| 1547 | - `code` run using the `node:vm` module's methods with an isolated global | ||
| 1548 | - environment within which it can operate. The process of creating the V8 Context | ||
| 1549 | - and associating it with the `contextObject` is what this document refers to as | ||
| 1550 | - "contextifying" the object. | ||
| 1591 | + When the method `vm.createContext()` is called with an object, the `contextObject` argument | ||
| 1592 | + will be used to wrap the global object of a new instance of a V8 Context | ||
| 1593 | + (if `contextObject` is `undefined`, a new object will be created from the current context | ||
| 1594 | + before its contextified). This V8 Context provides the `code` run using the `node:vm` | ||
| 1595 | + module's methods with an isolated global environment within which it can operate. | ||
| 1596 | + The process of creating the V8 Context and associating it with the `contextObject` | ||
| 1597 | + in the outer context is what this document refers to as "contextifying" the object. | ||
| 1598 | + | ||
| 1599 | + The contextifying would introduce some quirks to the `globalThis` value in the context. | ||
| 1600 | + For example, it cannot be frozen, and it is not reference equal to the `contextObject` | ||
| 1601 | + in the outer context. | ||
| 1602 | + | ||
| 1603 | + ```js | ||
| 1604 | + const vm = require('node:vm'); | ||
| 1605 | + | ||
| 1606 | + // An undefined `contextObject` option makes the global object contextified. | ||
| 1607 | + const context = vm.createContext(); | ||
| 1608 | + console.log(vm.runInContext('globalThis', context) === context); // false | ||
| 1609 | + // A contextified global object cannot be frozen. | ||
| 1610 | + try { | ||
| 1611 | + vm.runInContext('Object.freeze(globalThis);', context); | ||
| 1612 | + } catch (e) { | ||
| 1613 | + console.log(e); // TypeError: Cannot freeze | ||
| 1614 | + } | ||
| 1615 | + console.log(vm.runInContext('globalThis.foo = 1; foo;', context)); // 1 | ||
| 1616 | + ``` | ||
| 1617 | + | ||
| 1618 | + To create a context with an ordinary global object and get access to a global proxy in | ||
| 1619 | + the outer context with fewer quirks, specify `vm.constants.DONT_CONTEXTIFY` as the | ||
| 1620 | + `contextObject` argument. | ||
| 1621 | + | ||
| 1622 | + ### `vm.constants.DONT_CONTEXTIFY` | ||
| 1623 | + | ||
| 1624 | + This constant, when used as the `contextObject` argument in vm APIs, instructs Node.js to create | ||
| 1625 | + a context without wrapping its global object with another object in a Node.js-specific manner. | ||
| 1626 | + As a result, the `globalThis` value inside the new context would behave more closely to an ordinary | ||
| 1627 | + one. | ||
| 1628 | + | ||
| 1629 | + ```js | ||
| 1630 | + const vm = require('node:vm'); | ||
| 1631 | + | ||
| 1632 | + // Use vm.constants.DONT_CONTEXTIFY to freeze the global object. | ||
| 1633 | + const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| 1634 | + vm.runInContext('Object.freeze(globalThis);', context); | ||
| 1635 | + try { | ||
| 1636 | + vm.runInContext('bar = 1; bar;', context); | ||
| 1637 | + } catch (e) { | ||
| 1638 | + console.log(e); // Uncaught ReferenceError: bar is not defined | ||
| 1639 | + } | ||
| 1640 | + ``` | ||
| 1641 | + | ||
| 1642 | + When `vm.constants.DONT_CONTEXTIFY` is used as the `contextObject` argument to [`vm.createContext()`][], | ||
| 1643 | + the returned object is a proxy-like object to the global object in the newly created context with | ||
| 1644 | + fewer Node.js-specific quirks. It is reference equal to the `globalThis` value in the new context, | ||
| 1645 | + can be modified from outside the context, and can be used to access built-ins in the new context directly. | ||
| 1646 | + | ||
| 1647 | + ```js | ||
| 1648 | + const vm = require('node:vm'); | ||
| 1649 | + | ||
| 1650 | + const context = vm.createContext(vm.constants.DONT_CONTEXTIFY); | ||
| 1651 | + | ||
| 1652 | + // Returned object is reference equal to globalThis in the new context. | ||
| 1653 | + console.log(vm.runInContext('globalThis', context) === context); // true | ||
| 1654 | + | ||
| 1655 | + // Can be used to access globals in the new context directly. | ||
| 1656 | + console.log(context.Array); // [Function: Array] | ||
| 1657 | + vm.runInContext('foo = 1;', context); | ||
| 1658 | + console.log(context.foo); // 1 | ||
| 1659 | + context.bar = 1; | ||
| 1660 | + console.log(vm.runInContext('bar;', context)); // 1 | ||
| 1661 | + | ||
| 1662 | + // Can be frozen and it affects the inner context. | ||
| 1663 | + Object.freeze(context); | ||
| 1664 | + try { | ||
| 1665 | + vm.runInContext('baz = 1; baz;', context); | ||
| 1666 | + } catch (e) { | ||
| 1667 | + console.log(e); // Uncaught ReferenceError: baz is not defined | ||
| 1668 | + } | ||
| 1669 | + ``` | ||
| 1551 | 1670 | ||
| 1552 | 1671 | ## Timeout interactions with asynchronous tasks and Promises | |
| 1553 | 1672 | ||
@@ -1837,6 +1956,7 @@ const { Script, SyntheticModule } = require('node:vm'); | |||
| 1837 | 1956 | [`script.runInThisContext()`]: #scriptruninthiscontextoptions | |
| 1838 | 1957 | [`url.origin`]: url.md#urlorigin | |
| 1839 | 1958 | [`vm.compileFunction()`]: #vmcompilefunctioncode-params-options | |
| 1959 | + [`vm.constants.DONT_CONTEXTIFY`]: #vmconstantsdont_contextify | ||
| 1840 | 1960 | [`vm.createContext()`]: #vmcreatecontextcontextobject-options | |
| 1841 | 1961 | [`vm.runInContext()`]: #vmrunincontextcode-contextifiedobject-options | |
| 1842 | 1962 | [`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 | |
|---|---|---|---|
@@ -52,6 +52,7 @@ | |||
| 52 | 52 | V(resource_symbol, "resource_symbol") \ | |
| 53 | 53 | V(trigger_async_id_symbol, "trigger_async_id_symbol") \ | |
| 54 | 54 | V(source_text_module_default_hdo, "source_text_module_default_hdo") \ | |
| 55 | + V(vm_context_no_contextify, "vm_context_no_contextify") \ | ||
| 55 | 56 | V(vm_dynamic_import_default_internal, "vm_dynamic_import_default_internal") \ | |
| 56 | 57 | V(vm_dynamic_import_main_context_default, \ | |
| 57 | 58 | "vm_dynamic_import_main_context_default") \ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments