@@ -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 |
|