doc: improve vm.md copy

General improvements to vm module documentation

PR-URL: https://github.com/nodejs/node/pull/6827
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
James M Snell 2016-05-17 21:56:01 -07:00
parent 395cc885f4
commit baeed8b3d9

View File

@ -4,7 +4,8 @@
<!--name=vm--> <!--name=vm-->
You can access this module with: The `vm` module provides APIs for compiling and running code within V8 Virtual
Machine contexts. It can be accessed using:
```js ```js
const vm = require('vm'); const vm = require('vm');
@ -13,66 +14,81 @@ const vm = require('vm');
JavaScript code can be compiled and run immediately or compiled, saved, and run JavaScript code can be compiled and run immediately or compiled, saved, and run
later. later.
## Class: Script ## Class: vm.Script
A class for holding precompiled scripts, and running them in specific sandboxes. Instances of the `vm.Script` class contain precompiled scripts that can be
executed in specific sandboxes (or "contexts").
### new vm.Script(code, options) ### new vm.Script(code, options)
Creating a new `Script` compiles `code` but does not run it. Instead, the * `code` {string} The JavaScript code to compile.
created `vm.Script` object represents this compiled code. This script can be run * `options`
later many times using methods below. The returned script is not bound to any * `filename` {string} Specifies the filename used in stack traces produced
global object. It is bound before each run, just for that run. by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
* `cachedData` {Buffer} Provides an optional `Buffer` 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.
The options when creating a script are: Creating a new `vm.Script` object compiles `code` but does not run it. The
compiled `vm.Script` can be run later multiple times. It is important to note
- `filename`: allows you to control the filename that shows up in any stack that the `code` is not bound to any global object; rather, it is bound before
traces produced from this script. each run, just for that run.
- `lineOffset`: allows you to add an offset to the line number that is
displayed in stack traces
- `columnOffset`: allows you to add an offset to the column number that is
displayed in stack traces
- `displayErrors`: if `true`, on error, attach the line of code that caused
the error to the stack trace. Applies only to syntax errors compiling the
code; errors while running the code are controlled by the options to the
script's methods.
- `timeout`: a number of milliseconds to execute `code` before terminating
execution. If execution is terminated, an [`Error`][] will be thrown.
- `cachedData`: an optional `Buffer` with V8's code cache data for the supplied
source. When supplied `cachedDataRejected` value will be set to either
`true` or `false` depending on acceptance of the data by V8.
- `produceCachedData`: if `true` and no `cachedData` is present - V8 tries to
produce code cache data for `code`. Upon success, a `Buffer` with V8's code
cache data will be produced and stored in `cachedData` property of the
returned `vm.Script` instance. `cachedDataProduced` value will be set to
either `true` or `false` depending on whether code cache data is produced
successfully.
### script.runInContext(contextifiedSandbox[, options]) ### script.runInContext(contextifiedSandbox[, options])
Similar to [`vm.runInContext()`][] but a method of a precompiled `Script` * `contextifiedSandbox` {Object} A [contextified][] object as returned by the
object. `script.runInContext()` runs `script`'s compiled code in `vm.createContext()` method.
* `options` {Object}
* `filename` {string} Specifies the filename used in stack traces produced
by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
Runs the compiled code contained by the `vm.Script` object within the given
`contextifiedSandbox` and returns the result. Running code does not have access `contextifiedSandbox` and returns the result. Running code does not have access
to local scope. to local scope.
`script.runInContext()` takes the same options as The following example compiles code that increments a global variable, sets
[`script.runInThisContext()`][]. the value of another global variable, then execute the code multiple times.
The globals are contained in the `sandbox` object.
Example: compile code that increments a global variable and sets one, then
execute the code multiple times. These globals are contained in the sandbox.
```js ```js
const util = require('util'); const util = require('util');
const vm = require('vm'); const vm = require('vm');
var sandbox = { const sandbox = {
animal: 'cat', animal: 'cat',
count: 2 count: 2
}; };
var context = new vm.createContext(sandbox); const script = new vm.Script('count += 1; name = "kitty";');
var script = new vm.Script('count += 1; name = "kitty"');
const context = new vm.createContext(sandbox);
for (var i = 0; i < 10; ++i) { for (var i = 0; i < 10; ++i) {
script.runInContext(context); script.runInContext(context);
} }
@ -82,33 +98,39 @@ console.log(util.inspect(sandbox));
// { animal: 'cat', count: 12, name: 'kitty' } // { animal: 'cat', count: 12, name: 'kitty' }
``` ```
Note that running untrusted code is a tricky business requiring great care.
`script.runInContext()` is quite useful, but safely running untrusted code
requires a separate process.
### script.runInNewContext([sandbox][, options]) ### script.runInNewContext([sandbox][, options])
Similar to [`vm.runInNewContext()`][] but a method of a precompiled `Script` * `sandbox` {Object} An object that will be [contextified][]. If `undefined`, a
object. `script.runInNewContext()` contextifies `sandbox` if passed or creates a new object will be created.
new contextified sandbox if it's omitted, and then runs `script`'s compiled code * `options` {Object}
with the sandbox as the global object and returns the result. Running code does * `filename` {string} Specifies the filename used in stack traces produced
not have access to local scope. by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
`script.runInNewContext()` takes the same options as First contextifies the given `sandbox`, runs the compiled code contained by
[`script.runInThisContext()`][]. the `vm.Script` object within the created sandbox, and returns the result.
Running code does not have access to local scope.
Example: compile code that sets a global variable, then execute the code The following example compiles code that sets a global variable, then executes
multiple times in different contexts. These globals are set on and contained in the code multiple times in different contexts. The globals are set on and
the sandboxes. contained within each individual `sandbox`.
```js ```js
const util = require('util'); const util = require('util');
const vm = require('vm'); const vm = require('vm');
const sandboxes = [{}, {}, {}];
const script = new vm.Script('globalVar = "set"'); const script = new vm.Script('globalVar = "set"');
const sandboxes = [{}, {}, {}];
sandboxes.forEach((sandbox) => { sandboxes.forEach((sandbox) => {
script.runInNewContext(sandbox); script.runInNewContext(sandbox);
}); });
@ -118,19 +140,28 @@ console.log(util.inspect(sandboxes));
// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] // [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
``` ```
Note that running untrusted code is a tricky business requiring great care.
`script.runInNewContext()` is quite useful, but safely running untrusted code
requires a separate process.
### script.runInThisContext([options]) ### script.runInThisContext([options])
Similar to [`vm.runInThisContext()`][] but a method of a precompiled `Script` * `options` {Object}
object. `script.runInThisContext()` runs `script`'s compiled code and returns * `filename` {string} Specifies the filename used in stack traces produced
the result. Running code does not have access to local scope, but does have by this script.
access to the current `global` object. * `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
Example of using `script.runInThisContext()` to compile code once and run it Runs the compiled code contained by the `vm.Script` within the context of the
multiple times: 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 ```js
const vm = require('vm'); const vm = require('vm');
@ -148,53 +179,60 @@ console.log(globalVar);
// 1000 // 1000
``` ```
The options for running a script are:
- `filename`: allows you to control the filename that shows up in any stack
traces produced.
- `lineOffset`: allows you to add an offset to the line number that is
displayed in stack traces
- `columnOffset`: allows you to add an offset to the column number that is
displayed in stack traces
- `displayErrors`: if `true`, on error, attach the line of code that caused
the error to the stack trace. Applies only to runtime errors executing the
code; it is impossible to create a `Script` instance with syntax errors, as
the constructor will throw.
- `timeout`: a number of milliseconds to execute the script before terminating
execution. If execution is terminated, an [`Error`][] will be thrown.
## vm.createContext([sandbox]) ## vm.createContext([sandbox])
If given a `sandbox` object, will "contextify" that sandbox so that it can be * `sandbox` {Object}
If given a `sandbox` object, the `vm.createContext()` method will [prepare
that sandbox][#vm_what_does_it_mean_to_contextify_an_object] so that it can be
used in calls to [`vm.runInContext()`][] or [`script.runInContext()`][]. Inside used in calls to [`vm.runInContext()`][] or [`script.runInContext()`][]. Inside
scripts run as such, `sandbox` will be the global object, retaining all its such scripts, the `sandbox` object will be the global object, retaining all of
existing properties but also having the built-in objects and functions any 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, standard [global object][] has. Outside of scripts run by the vm module,
`sandbox` will be unchanged. `sandbox` will remain unchanged.
If not given a sandbox object, returns a new, empty contextified sandbox object If `sandbox` is omitted (or passed explicitly as `undefined`), a new, empty
you can use. [contextified][] sandbox object will be returned.
This function is useful for creating a sandbox that can be used to run multiple The `vm.createContext()` method is primarily useful for creating a single
scripts, e.g. if you were emulating a web browser it could be used to create a sandbox that can be used to run multiple scripts. For instance, if emulating a
single sandbox representing a window's global object, then run all `<script>` web browser, the method can be used to create a single sandbox representing a
tags together inside that sandbox. window's global object, then run all `<script>` tags together within the context
of that sandbox.
## vm.isContext(sandbox) ## vm.isContext(sandbox)
Returns whether or not a sandbox object has been contextified by calling * `sandbox` {Object}
[`vm.createContext()`][] on it.
Returns `true` if the given `sandbox` object has been [contextified][] using
[`vm.createContext()`][].
## vm.runInContext(code, contextifiedSandbox[, options]) ## vm.runInContext(code, contextifiedSandbox[, options])
`vm.runInContext()` compiles `code`, then runs it in `contextifiedSandbox` and * `code` {string} The JavaScript code to compile and run.
returns the result. Running code does not have access to local scope. The * `contextifiedSandbox` {Object} The [contextified][] object that will be used
`contextifiedSandbox` object must have been previously contextified via as the `global` when the `code` is compiled and run.
[`vm.createContext()`][]; it will be used as the global object for `code`. * `options`
* `filename` {string} Specifies the filename used in stack traces produced
by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
`vm.runInContext()` takes the same options as [`vm.runInThisContext()`][]. The `vm.runInContext()` method compiles `code`, runs it within the context of
the `contextifiedSandbox`, then returns the result. Running code does not have
access to the local scope. The `contextifiedSandbox` object *must* have been
previously [contextified][] using the [`vm.createContext()`][] method.
Example: compile and execute different scripts in a single existing context. The following example compiles and executes different scripts using a single
[contextified][] object:
```js ```js
const util = require('util'); const util = require('util');
@ -204,21 +242,19 @@ const sandbox = { globalVar: 1 };
vm.createContext(sandbox); vm.createContext(sandbox);
for (var i = 0; i < 10; ++i) { for (var i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', sandbox); vm.runInContext('globalVar *= 2;', sandbox);
} }
console.log(util.inspect(sandbox)); console.log(util.inspect(sandbox));
// { globalVar: 1024 } // { globalVar: 1024 }
``` ```
Note that running untrusted code is a tricky business requiring great care.
`vm.runInContext()` is quite useful, but safely running untrusted code requires
a separate process.
## vm.runInDebugContext(code) ## vm.runInDebugContext(code)
`vm.runInDebugContext()` compiles and executes `code` inside the V8 debug * `code` {string} The JavaScript code to compile and run.
context. The primary use case is to get access to the V8 debug object:
The `vm.runInDebugContext()` method compiles and executes `code` inside the V8
debug context. The primary use case is to gain access to the V8 `Debug` object:
```js ```js
const vm = require('vm'); const vm = require('vm');
@ -227,21 +263,38 @@ console.log(Debug.findScript(process.emit).name); // 'events.js'
console.log(Debug.findScript(process.exit).name); // 'internal/process.js' console.log(Debug.findScript(process.exit).name); // 'internal/process.js'
``` ```
Note that the debug context and object are intrinsically tied to V8's debugger *Note*: The debug context and object are intrinsically tied to V8's debugger
implementation and may change (or even get removed) without prior warning. implementation and may change (or even be removed) without prior warning.
The debug object can also be exposed with the `--expose_debug_as=` switch. The `Debug` object can also be made available using the V8-specific
`--expose_debug_as=` [command line option][cli.md].
## vm.runInNewContext(code[, sandbox][, options]) ## vm.runInNewContext(code[, sandbox][, options])
`vm.runInNewContext()` compiles `code`, contextifies `sandbox` if passed or * `code` {string} The JavaScript code to compile and run.
creates a new contextified sandbox if it's omitted, and then runs the code with * `sandbox` {Object} An object that will be [contextified][]. If `undefined`, a
the sandbox as the global object and returns the result. new object will be created.
* `options`
* `filename` {string} Specifies the filename used in stack traces produced
by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
`vm.runInNewContext()` takes the same options as [`vm.runInThisContext()`][]. The `vm.runInContext()` first contextifies the given `sandbox` object (or
creates a new `sandbox` if passed as `undefined`), compiles the `code`, runs it
within the context of the created context, then returns the result. Running code
does not have access to the local scope.
Example: compile and execute code that increments a global variable and sets a The following example compiles and executes code that increments a global
new one. These globals are contained in the sandbox. variable and sets a new one. These globals are contained in the `sandbox`.
```js ```js
const util = require('util'); const util = require('util');
@ -258,17 +311,29 @@ console.log(util.inspect(sandbox));
// { animal: 'cat', count: 3, name: 'kitty' } // { animal: 'cat', count: 3, name: 'kitty' }
``` ```
Note that running untrusted code is a tricky business requiring great care.
`vm.runInNewContext()` is quite useful, but safely running untrusted code requires
a separate process.
## vm.runInThisContext(code[, options]) ## vm.runInThisContext(code[, options])
`vm.runInThisContext()` compiles `code`, runs it and returns the result. Running * `code` {string} The JavaScript code to compile and run.
code does not have access to local scope, but does have access to the current * `options`
`global` object. * `filename` {string} Specifies the filename used in stack traces produced
by this script.
* `lineOffset` {number} Specifies the line number offset that is displayed
in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
* `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
while compiling the `code`, the line of code causing the error is attached
to the stack trace.
* `timeout` {number} Specifies the number of milliseconds to execute `code`
before terminating execution. If execution is terminated, an [`Error`][]
will be thrown.
Example of using `vm.runInThisContext()` and [`eval()`][] to run the same code: `vm.runInThisContext()` compiles `code`, runs it within the context of the
current `global` and returns the result. Running code does not have access to
local scope, but does have access to the current `global` object.
The following example illustrates using both `vm.runInThisContext()` and
the JavaScript [`eval()`][] function to run the same code:
```js ```js
const vm = require('vm'); const vm = require('vm');
@ -286,33 +351,21 @@ console.log('localVar: ', localVar);
// evalResult: 'eval', localVar: 'eval' // evalResult: 'eval', localVar: 'eval'
``` ```
`vm.runInThisContext()` does not have access to the local scope, so `localVar` Because `vm.runInThisContext()` does not have access to the local scope,
is unchanged. [`eval()`][] does have access to the local scope, so `localVar` is `localVar` is unchanged. In contrast, [`eval()`][] *does* have access to the
changed. local scope, so the value `localVar` is changed. In this way
`vm.runInThisContext()` is much like an [indirect `eval()` call][], e.g.
`(0,eval)('code')`.
In this way `vm.runInThisContext()` is much like an [indirect `eval()` call][], ## Example: Running an HTTP Server within a VM
e.g. `(0,eval)('code')`. However, it also has the following additional options:
- `filename`: allows you to control the filename that shows up in any stack When using either `script.runInThisContext()` or `vm.runInThisContext()`, the
traces produced. code is executed within the current V8 global context. The code passed
- `lineOffset`: allows you to add an offset to the line number that is to this VM context will have its own isolated scope.
displayed in stack traces
- `columnOffset`: allows you to add an offset to the column number that is
displayed in stack traces
- `displayErrors`: if `true`, on error, attach the line of code that caused
the error to the stack trace. Will capture both syntax errors from compiling
`code` and runtime errors thrown by executing the compiled code. Defaults to
`true`.
- `timeout`: a number of milliseconds to execute `code` before terminating
execution. If execution is terminated, an [`Error`][] will be thrown.
## Example: Run a Server within a VM In order to run a simple web server using the `http` module the code passed to
the context must either call `require('http')` on its own, or have a reference
The context of `.runInThisContext()` refers to the V8 context. The code passed to the `http` module passed to it. For instance:
to this VM context will have it's own isolated scope. To run a simple web server
using the `http` module, for instance, the code passed to the context must either
call `require('http')` on its own, or have a reference to the `http` module passed
to it. For instance:
```js ```js
'use strict'; 'use strict';
@ -334,10 +387,26 @@ let code =
vm.runInThisContext(code)(require); vm.runInThisContext(code)(require);
``` ```
_Note: `require()` in the above case shares the state with context it is passed *Note*: The `require()` in the above case shares the state with context it is
from. This might introduce risks when unknown code is executed, e.g. altering passed from. This may introduce risks when untrusted code is executed, e.g.
objects from the calling thread's context in unwanted ways. It is advisable to altering objects from the calling thread's context in unwanted ways.
run `vm` code in a separate process._
## What does it mean to "contextify" an object?
All JavaScript executed within Node.js runs within the scope of a "context".
According to the [V8 Embedder's Guide][]:
> In V8, a context is an execution environment that allows separate, unrelated,
> 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 `sandbox` object that is
passed in (or a newly created object if `sandbox` is `undefined`) is associated
internally with a new instance of a V8 Context. This V8 Context provides the
`code` run using the `vm` modules methods with an isolated global environment
within which it can operate. The process of creating the V8 Context and
associating it with the `sandbox` object is what this document refers to as
"contextifying" the `sandbox`.
[indirect `eval()` call]: https://es5.github.io/#x10.4.2 [indirect `eval()` call]: https://es5.github.io/#x10.4.2
[global object]: https://es5.github.io/#x15.1 [global object]: https://es5.github.io/#x15.1
@ -349,3 +418,5 @@ run `vm` code in a separate process._
[`vm.runInNewContext()`]: #vm_vm_runinnewcontext_code_sandbox_options [`vm.runInNewContext()`]: #vm_vm_runinnewcontext_code_sandbox_options
[`vm.runInThisContext()`]: #vm_vm_runinthiscontext_code_options [`vm.runInThisContext()`]: #vm_vm_runinthiscontext_code_options
[`eval()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval [`eval()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
[V8 Embedder's Guide]: https://developers.google.com/v8/embed#contexts
[contextified]: #vm_what_does_it_mean_to_contextify_an_object