diff --git a/doc/api/vm.md b/doc/api/vm.md index 0fc486bc859186..808ed2f00d97b2 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -43,6 +43,264 @@ console.log(sandbox.y); // 17 console.log(x); // 1; y is not defined. ``` +## Class: vm.Script + + +Instances of the `vm.Script` class contain precompiled scripts that can be +executed in specific sandboxes (or "contexts"). + +### Constructor: new vm.Script(code[, options]) + + +* `code` {string} The JavaScript code to compile. +* `options` {Object|string} + * `filename` {string} Specifies the filename used in stack traces produced + by this script. **Default:** `'evalmachine.'`. + * `lineOffset` {number} Specifies the line number offset that is displayed + in stack traces produced by this script. **Default:** `0`. + * `columnOffset` {number} Specifies the column number offset that is displayed + in stack traces produced by this script. **Default:** `0`. + * `cachedData` {Buffer|TypedArray|DataView} Provides an optional `Buffer` or + `TypedArray`, or `DataView` with V8's code cache data for the supplied + source. When supplied, the `cachedDataRejected` value will be set to + either `true` or `false` depending on acceptance of the data by V8. + * `produceCachedData` {boolean} When `true` and no `cachedData` is present, V8 + will attempt to produce code cache data for `code`. Upon success, a + `Buffer` with V8's code cache data will be produced and stored in the + `cachedData` property of the returned `vm.Script` instance. + The `cachedDataProduced` value will be set to either `true` or `false` + depending on whether code cache data is produced successfully. + This option is **deprecated** in favor of `script.createCachedData()`. + **Default:** `false`. + * `importModuleDynamically` {Function} Called during evaluation of this module + when `import()` is called. If this option is not specified, calls to + `import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][]. + This option is part of the experimental API for the `--experimental-modules` + flag, and should not be considered stable. + * `specifier` {string} specifier passed to `import()` + * `module` {vm.SourceTextModule} + * Returns: {Module Namespace Object|vm.SourceTextModule} Returning a + `vm.SourceTextModule` is recommended in order to take advantage of error + tracking, and to avoid issues with namespaces that contain `then` + function exports. + +If `options` is a string, then it specifies the filename. + +Creating a new `vm.Script` object compiles `code` but does not run it. The +compiled `vm.Script` can be run later multiple times. The `code` is not bound to +any global object; rather, it is bound before each run, just for that run. + +### script.createCachedData() + + +* Returns: {Buffer} + +Creates a code cache that can be used with the Script constructor's +`cachedData` option. Returns a Buffer. This method may be called at any +time and any number of times. + +```js +const script = new vm.Script(` +function add(a, b) { + return a + b; +} + +const x = add(1, 2); +`); + +const cacheWithoutX = script.createCachedData(); + +script.runInThisContext(); + +const cacheWithX = script.createCachedData(); +``` + +### script.runInContext(contextifiedSandbox[, options]) + + +* `contextifiedSandbox` {Object} A [contextified][] object as returned by the + `vm.createContext()` method. +* `options` {Object} + * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs + while compiling the `code`, the line of code causing the error is attached + to the stack trace. **Default:** `true`. + * `timeout` {integer} Specifies the number of milliseconds to execute `code` + before terminating execution. If execution is terminated, an [`Error`][] + will be thrown. This value must be a strictly positive integer. + * `breakOnSigint` {boolean} If `true`, the execution will be terminated when + `SIGINT` (Ctrl+C) is received. Existing handlers for the + event that have been attached via `process.on('SIGINT')` will be disabled + during script execution, but will continue to work after that. If execution + is terminated, an [`Error`][] will be thrown. **Default:** `false`. +* Returns: {any} the result of the very last statement executed in the script. + +Runs the compiled code contained by the `vm.Script` object within the given +`contextifiedSandbox` and returns the result. Running code does not have access +to local scope. + +The following example compiles code that increments a global variable, sets +the value of another global variable, then execute the code multiple times. +The globals are contained in the `sandbox` object. + +```js +const util = require('util'); +const vm = require('vm'); + +const sandbox = { + animal: 'cat', + count: 2 +}; + +const script = new vm.Script('count += 1; name = "kitty";'); + +const context = vm.createContext(sandbox); +for (let i = 0; i < 10; ++i) { + script.runInContext(context); +} + +console.log(util.inspect(sandbox)); + +// { animal: 'cat', count: 12, name: 'kitty' } +``` + +Using the `timeout` or `breakOnSigint` options will result in new event loops +and corresponding threads being started, which have a non-zero performance +overhead. + +### script.runInNewContext([sandbox[, options]]) + + +* `sandbox` {Object} An object that will be [contextified][]. If `undefined`, a + new object will be created. +* `options` {Object} + * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs + while compiling the `code`, the line of code causing the error is attached + to the stack trace. **Default:** `true`. + * `timeout` {integer} Specifies the number of milliseconds to execute `code` + before terminating execution. If execution is terminated, an [`Error`][] + will be thrown. This value must be a strictly positive integer. + * `breakOnSigint` {boolean} If `true`, the execution will be terminated when + `SIGINT` (Ctrl+C) is received. Existing handlers for the + event that have been attached via `process.on('SIGINT')` will be disabled + during script execution, but will continue to work after that. If execution + is terminated, an [`Error`][] will be thrown. **Default:** `false`. + * `contextName` {string} Human-readable name of the newly created context. + **Default:** `'VM Context i'`, where `i` is an ascending numerical index of + the created context. + * `contextOrigin` {string} [Origin][origin] corresponding to the newly + created context for display purposes. The origin should be formatted like a + URL, but with only the scheme, host, and port (if necessary), like the + value of the [`url.origin`][] property of a [`URL`][] object. Most notably, + this string should omit the trailing slash, as that denotes a path. + **Default:** `''`. + * `contextCodeGeneration` {Object} + * `strings` {boolean} If set to false any calls to `eval` or function + constructors (`Function`, `GeneratorFunction`, etc) will throw an + `EvalError`. **Default:** `true`. + * `wasm` {boolean} If set to false any attempt to compile a WebAssembly + module will throw a `WebAssembly.CompileError`. **Default:** `true`. +* Returns: {any} the result of the very last statement executed in the script. + +First contextifies the given `sandbox`, runs the compiled code contained by +the `vm.Script` object within the created sandbox, and returns the result. +Running code does not have access to local scope. + +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 +contained within each individual `sandbox`. + +```js +const util = require('util'); +const vm = require('vm'); + +const script = new vm.Script('globalVar = "set"'); + +const sandboxes = [{}, {}, {}]; +sandboxes.forEach((sandbox) => { + script.runInNewContext(sandbox); +}); + +console.log(util.inspect(sandboxes)); + +// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] +``` + +### script.runInThisContext([options]) + + +* `options` {Object} + * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs + while compiling the `code`, the line of code causing the error is attached + to the stack trace. **Default:** `true`. + * `timeout` {integer} Specifies the number of milliseconds to execute `code` + before terminating execution. If execution is terminated, an [`Error`][] + will be thrown. This value must be a strictly positive integer. + * `breakOnSigint` {boolean} If `true`, the execution will be terminated when + `SIGINT` (Ctrl+C) is received. Existing handlers for the + event that have been attached via `process.on('SIGINT')` will be disabled + during script execution, but will continue to work after that. If execution + is terminated, an [`Error`][] will be thrown. **Default:** `false`. +* Returns: {any} the result of the very last statement executed in the script. + +Runs the compiled code contained by the `vm.Script` within the context of the +current `global` object. Running code does not have access to local scope, but +*does* have access to the current `global` object. + +The following example compiles code that increments a `global` variable then +executes that code multiple times: + +```js +const vm = require('vm'); + +global.globalVar = 0; + +const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' }); + +for (let i = 0; i < 1000; ++i) { + script.runInThisContext(); +} + +console.log(globalVar); + +// 1000 +``` + ## Class: vm.SourceTextModule - ```js - import foo from 'foo'; - // ^^^^^ the module specifier - ``` - * `referencingModule` {vm.SourceTextModule} The `Module` object `link()` is - called on. - * Returns: {vm.SourceTextModule|Promise} -* Returns: {Promise} - -Link module dependencies. This method must be called before instantiation, and -can only be called once per module. - -The function is expected to return a `Module` object or a `Promise` that -eventually resolves to a `Module` object. The returned `Module` must satisfy the -following two invariants: - -- It must belong to the same context as the parent `Module`. -- Its `linkingStatus` must not be `'errored'`. - -If the returned `Module`'s `linkingStatus` is `'unlinked'`, this method will be -recursively called on the returned `Module` with the same provided `linker` -function. - -`link()` returns a `Promise` that will either get resolved when all linking -instances resolve to a valid `Module`, or rejected if the linker function either -throws an exception or returns an invalid `Module`. - -The linker function roughly corresponds to the implementation-defined -[HostResolveImportedModule][] abstract operation in the ECMAScript -specification, with a few key differences: - -- The linker function is allowed to be asynchronous while - [HostResolveImportedModule][] is synchronous. -- The linker function is executed during linking, a Node.js-specific stage - before instantiation, while [HostResolveImportedModule][] is called during - instantiation. - -The actual [HostResolveImportedModule][] implementation used during module -instantiation is one that returns the modules linked during linking. Since at -that point all modules would have been fully linked already, the -[HostResolveImportedModule][] implementation is fully synchronous per -specification. - -### module.linkingStatus - -* {string} - -The current linking status of `module`. It will be one of the following values: - -- `'unlinked'`: `module.link()` has not yet been called. -- `'linking'`: `module.link()` has been called, but not all Promises returned by - the linker function have been resolved yet. -- `'linked'`: `module.link()` has been called, and all its dependencies have - been successfully linked. -- `'errored'`: `module.link()` has been called, but at least one of its - dependencies failed to link, either because the callback returned a `Promise` - that is rejected, or because the `Module` the callback returned is invalid. - -### module.namespace - -* {Object} - -The namespace object of the module. This is only available after instantiation -(`module.instantiate()`) has completed. - -Corresponds to the [GetModuleNamespace][] abstract operation in the ECMAScript -specification. - -### module.status - -* {string} - -The current status of the module. Will be one of: - -- `'uninstantiated'`: The module is not instantiated. It may because of any of - the following reasons: - - - The module was just created. - - `module.instantiate()` has been called on this module, but it failed for - some reason. - - This status does not convey any information regarding if `module.link()` has - been called. See `module.linkingStatus` for that. - -- `'instantiating'`: The module is currently being instantiated through a - `module.instantiate()` call on itself or a parent module. - -- `'instantiated'`: The module has been instantiated successfully, but - `module.evaluate()` has not yet been called. - -- `'evaluating'`: The module is being evaluated through a `module.evaluate()` on - itself or a parent module. - -- `'evaluated'`: The module has been successfully evaluated. - -- `'errored'`: The module has been evaluated, but an exception was thrown. - -Other than `'errored'`, this status string corresponds to the specification's -[Source Text Module Record][]'s `[[Status]]` field. `'errored'` corresponds to -`'evaluated'` in the specification, but with `[[EvaluationError]]` set to a -value that is not `undefined`. - -### module.url - -* {string} - -The URL of the current module, as set in the constructor. - -## Class: vm.Script - - -Instances of the `vm.Script` class contain precompiled scripts that can be -executed in specific sandboxes (or "contexts"). - -### Constructor: new vm.Script(code[, options]) - - -* `code` {string} The JavaScript code to compile. -* `options` {Object|string} - * `filename` {string} Specifies the filename used in stack traces produced - by this script. **Default:** `'evalmachine.'`. - * `lineOffset` {number} Specifies the line number offset that is displayed - in stack traces produced by this script. **Default:** `0`. - * `columnOffset` {number} Specifies the column number offset that is displayed - in stack traces produced by this script. **Default:** `0`. - * `cachedData` {Buffer|TypedArray|DataView} Provides an optional `Buffer` or - `TypedArray`, or `DataView` with V8's code cache data for the supplied - source. When supplied, the `cachedDataRejected` value will be set to - either `true` or `false` depending on acceptance of the data by V8. - * `produceCachedData` {boolean} When `true` and no `cachedData` is present, V8 - will attempt to produce code cache data for `code`. Upon success, a - `Buffer` with V8's code cache data will be produced and stored in the - `cachedData` property of the returned `vm.Script` instance. - The `cachedDataProduced` value will be set to either `true` or `false` - depending on whether code cache data is produced successfully. - This option is **deprecated** in favor of `script.createCachedData()`. - **Default:** `false`. - * `importModuleDynamically` {Function} Called during evaluation of this module - when `import()` is called. If this option is not specified, calls to - `import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][]. - This option is part of the experimental API for the `--experimental-modules` - flag, and should not be considered stable. - * `specifier` {string} specifier passed to `import()` - * `module` {vm.SourceTextModule} - * Returns: {Module Namespace Object|vm.SourceTextModule} Returning a - `vm.SourceTextModule` is recommended in order to take advantage of error - tracking, and to avoid issues with namespaces that contain `then` - function exports. - -If `options` is a string, then it specifies the filename. - -Creating a new `vm.Script` object compiles `code` but does not run it. The -compiled `vm.Script` can be run later multiple times. The `code` is not bound to -any global object; rather, it is bound before each run, just for that run. +(`module.status` is `'evaluating'`) to prevent infinite recursion. -### script.createCachedData() - +Corresponds to the [Evaluate() concrete method][] field of [Source Text Module +Record][]s in the ECMAScript specification. -* Returns: {Buffer} +### module.instantiate() -Creates a code cache that can be used with the Script constructor's -`cachedData` option. Returns a Buffer. This method may be called at any -time and any number of times. +Instantiate the module. This must be called after linking has completed +(`linkingStatus` is `'linked'`); otherwise it will throw an error. It may also +throw an exception if one of the dependencies does not provide an export the +parent module requires. -```js -const script = new vm.Script(` -function add(a, b) { - return a + b; -} +However, if this function succeeded, further calls to this function after the +initial instantiation will be no-ops, to be consistent with the ECMAScript +specification. -const x = add(1, 2); -`); +Unlike other methods operating on `Module`, this function completes +synchronously and returns nothing. -const cacheWithoutX = script.createCachedData(); +Corresponds to the [Instantiate() concrete method][] field of [Source Text +Module Record][]s in the ECMAScript specification. -script.runInThisContext(); +### module.link(linker) -const cacheWithX = script.createCachedData(); -``` +* `linker` {Function} + * `specifier` {string} The specifier of the requested module: + + ```js + import foo from 'foo'; + // ^^^^^ the module specifier + ``` + * `referencingModule` {vm.SourceTextModule} The `Module` object `link()` is + called on. + * Returns: {vm.SourceTextModule|Promise} +* Returns: {Promise} -### script.runInContext(contextifiedSandbox[, options]) - +Link module dependencies. This method must be called before instantiation, and +can only be called once per module. -* `contextifiedSandbox` {Object} A [contextified][] object as returned by the - `vm.createContext()` method. -* `options` {Object} - * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs - while compiling the `code`, the line of code causing the error is attached - to the stack trace. **Default:** `true`. - * `timeout` {integer} Specifies the number of milliseconds to execute `code` - before terminating execution. If execution is terminated, an [`Error`][] - will be thrown. This value must be a strictly positive integer. - * `breakOnSigint` {boolean} If `true`, the execution will be terminated when - `SIGINT` (Ctrl+C) is received. Existing handlers for the - event that have been attached via `process.on('SIGINT')` will be disabled - during script execution, but will continue to work after that. If execution - is terminated, an [`Error`][] will be thrown. **Default:** `false`. -* Returns: {any} the result of the very last statement executed in the script. +The function is expected to return a `Module` object or a `Promise` that +eventually resolves to a `Module` object. The returned `Module` must satisfy the +following two invariants: -Runs the compiled code contained by the `vm.Script` object within the given -`contextifiedSandbox` and returns the result. Running code does not have access -to local scope. +- It must belong to the same context as the parent `Module`. +- Its `linkingStatus` must not be `'errored'`. -The following example compiles code that increments a global variable, sets -the value of another global variable, then execute the code multiple times. -The globals are contained in the `sandbox` object. +If the returned `Module`'s `linkingStatus` is `'unlinked'`, this method will be +recursively called on the returned `Module` with the same provided `linker` +function. -```js -const util = require('util'); -const vm = require('vm'); +`link()` returns a `Promise` that will either get resolved when all linking +instances resolve to a valid `Module`, or rejected if the linker function either +throws an exception or returns an invalid `Module`. -const sandbox = { - animal: 'cat', - count: 2 -}; +The linker function roughly corresponds to the implementation-defined +[HostResolveImportedModule][] abstract operation in the ECMAScript +specification, with a few key differences: -const script = new vm.Script('count += 1; name = "kitty";'); +- The linker function is allowed to be asynchronous while + [HostResolveImportedModule][] is synchronous. +- The linker function is executed during linking, a Node.js-specific stage + before instantiation, while [HostResolveImportedModule][] is called during + instantiation. -const context = vm.createContext(sandbox); -for (let i = 0; i < 10; ++i) { - script.runInContext(context); -} +The actual [HostResolveImportedModule][] implementation used during module +instantiation is one that returns the modules linked during linking. Since at +that point all modules would have been fully linked already, the +[HostResolveImportedModule][] implementation is fully synchronous per +specification. -console.log(util.inspect(sandbox)); +### module.linkingStatus -// { animal: 'cat', count: 12, name: 'kitty' } -``` +* {string} -Using the `timeout` or `breakOnSigint` options will result in new event loops -and corresponding threads being started, which have a non-zero performance -overhead. +The current linking status of `module`. It will be one of the following values: -### script.runInNewContext([sandbox[, options]]) - +- `'unlinked'`: `module.link()` has not yet been called. +- `'linking'`: `module.link()` has been called, but not all Promises returned by + the linker function have been resolved yet. +- `'linked'`: `module.link()` has been called, and all its dependencies have + been successfully linked. +- `'errored'`: `module.link()` has been called, but at least one of its + dependencies failed to link, either because the callback returned a `Promise` + that is rejected, or because the `Module` the callback returned is invalid. -* `sandbox` {Object} An object that will be [contextified][]. If `undefined`, a - new object will be created. -* `options` {Object} - * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs - while compiling the `code`, the line of code causing the error is attached - to the stack trace. **Default:** `true`. - * `timeout` {integer} Specifies the number of milliseconds to execute `code` - before terminating execution. If execution is terminated, an [`Error`][] - will be thrown. This value must be a strictly positive integer. - * `breakOnSigint` {boolean} If `true`, the execution will be terminated when - `SIGINT` (Ctrl+C) is received. Existing handlers for the - event that have been attached via `process.on('SIGINT')` will be disabled - during script execution, but will continue to work after that. If execution - is terminated, an [`Error`][] will be thrown. **Default:** `false`. - * `contextName` {string} Human-readable name of the newly created context. - **Default:** `'VM Context i'`, where `i` is an ascending numerical index of - the created context. - * `contextOrigin` {string} [Origin][origin] corresponding to the newly - created context for display purposes. The origin should be formatted like a - URL, but with only the scheme, host, and port (if necessary), like the - value of the [`url.origin`][] property of a [`URL`][] object. Most notably, - this string should omit the trailing slash, as that denotes a path. - **Default:** `''`. - * `contextCodeGeneration` {Object} - * `strings` {boolean} If set to false any calls to `eval` or function - constructors (`Function`, `GeneratorFunction`, etc) will throw an - `EvalError`. **Default:** `true`. - * `wasm` {boolean} If set to false any attempt to compile a WebAssembly - module will throw a `WebAssembly.CompileError`. **Default:** `true`. -* Returns: {any} the result of the very last statement executed in the script. +### module.namespace -First contextifies the given `sandbox`, runs the compiled code contained by -the `vm.Script` object within the created sandbox, and returns the result. -Running code does not have access to local scope. +* {Object} -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 -contained within each individual `sandbox`. +The namespace object of the module. This is only available after instantiation +(`module.instantiate()`) has completed. -```js -const util = require('util'); -const vm = require('vm'); +Corresponds to the [GetModuleNamespace][] abstract operation in the ECMAScript +specification. -const script = new vm.Script('globalVar = "set"'); +### module.status -const sandboxes = [{}, {}, {}]; -sandboxes.forEach((sandbox) => { - script.runInNewContext(sandbox); -}); +* {string} -console.log(util.inspect(sandboxes)); +The current status of the module. Will be one of: -// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] -``` +- `'uninstantiated'`: The module is not instantiated. It may because of any of + the following reasons: -### script.runInThisContext([options]) - + - The module was just created. + - `module.instantiate()` has been called on this module, but it failed for + some reason. -* `options` {Object} - * `displayErrors` {boolean} When `true`, if an [`Error`][] occurs - while compiling the `code`, the line of code causing the error is attached - to the stack trace. **Default:** `true`. - * `timeout` {integer} Specifies the number of milliseconds to execute `code` - before terminating execution. If execution is terminated, an [`Error`][] - will be thrown. This value must be a strictly positive integer. - * `breakOnSigint` {boolean} If `true`, the execution will be terminated when - `SIGINT` (Ctrl+C) is received. Existing handlers for the - event that have been attached via `process.on('SIGINT')` will be disabled - during script execution, but will continue to work after that. If execution - is terminated, an [`Error`][] will be thrown. **Default:** `false`. -* Returns: {any} the result of the very last statement executed in the script. + This status does not convey any information regarding if `module.link()` has + been called. See `module.linkingStatus` for that. -Runs the compiled code contained by the `vm.Script` within the context of the -current `global` object. Running code does not have access to local scope, but -*does* have access to the current `global` object. +- `'instantiating'`: The module is currently being instantiated through a + `module.instantiate()` call on itself or a parent module. -The following example compiles code that increments a `global` variable then -executes that code multiple times: +- `'instantiated'`: The module has been instantiated successfully, but + `module.evaluate()` has not yet been called. -```js -const vm = require('vm'); +- `'evaluating'`: The module is being evaluated through a `module.evaluate()` on + itself or a parent module. -global.globalVar = 0; +- `'evaluated'`: The module has been successfully evaluated. -const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' }); +- `'errored'`: The module has been evaluated, but an exception was thrown. -for (let i = 0; i < 1000; ++i) { - script.runInThisContext(); -} +Other than `'errored'`, this status string corresponds to the specification's +[Source Text Module Record][]'s `[[Status]]` field. `'errored'` corresponds to +`'evaluated'` in the specification, but with `[[EvaluationError]]` set to a +value that is not `undefined`. -console.log(globalVar); +### module.url -// 1000 -``` +* {string} + +The URL of the current module, as set in the constructor. ## vm.compileFunction(code[, params[, options]])