Browse Source

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>
v7.x
James M Snell 9 years ago
parent
commit
baeed8b3d9
  1. 397
      doc/api/vm.md

397
doc/api/vm.md

@ -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
The options when creating a script are: in stack traces produced by this script.
* `columnOffset` {number} Specifies the column number offset that is displayed
- `filename`: allows you to control the filename that shows up in any stack in stack traces produced by this script.
traces produced from this script. * `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
- `lineOffset`: allows you to add an offset to the line number that is while compiling the `code`, the line of code causing the error is attached
displayed in stack traces to the stack trace.
- `columnOffset`: allows you to add an offset to the column number that is * `timeout` {number} Specifies the number of milliseconds to execute `code`
displayed in stack traces before terminating execution. If execution is terminated, an [`Error`][]
- `displayErrors`: if `true`, on error, attach the line of code that caused will be thrown.
the error to the stack trace. Applies only to syntax errors compiling the * `cachedData` {Buffer} Provides an optional `Buffer` with V8's code cache
code; errors while running the code are controlled by the options to the data for the supplied source. When supplied, the `cachedDataRejected` value
script's methods. will be set to either `true` or `false` depending on acceptance of the data
- `timeout`: a number of milliseconds to execute `code` before terminating by V8.
execution. If execution is terminated, an [`Error`][] will be thrown. * `produceCachedData` {boolean} When `true` and no `cachedData` is present, V8
- `cachedData`: an optional `Buffer` with V8's code cache data for the supplied will attempt to produce code cache data for `code`. Upon success, a
source. When supplied `cachedDataRejected` value will be set to either `Buffer` with V8's code cache data will be produced and stored in the
`true` or `false` depending on acceptance of the data by V8. `cachedData` property of the returned `vm.Script` instance.
- `produceCachedData`: if `true` and no `cachedData` is present - V8 tries to The `cachedDataProduced` value will be set to either `true` or `false`
produce code cache data for `code`. Upon success, a `Buffer` with V8's code depending on whether code cache data is produced successfully.
cache data will be produced and stored in `cachedData` property of the
returned `vm.Script` instance. `cachedDataProduced` value will be set to Creating a new `vm.Script` object compiles `code` but does not run it. The
either `true` or `false` depending on whether code cache data is produced compiled `vm.Script` can be run later multiple times. It is important to note
successfully. that the `code` is not bound to any global object; rather, it is bound before
each run, just for that run.
### 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
`script.runInNewContext()` takes the same options as in stack traces produced by this script.
[`script.runInThisContext()`][]. * `columnOffset` {number} Specifies the column number offset that is displayed
in stack traces produced by this script.
Example: compile code that sets a global variable, then execute the code * `displayErrors` {boolean} When `true`, if an [`Error`][] error occurs
multiple times in different contexts. These globals are set on and contained in while compiling the `code`, the line of code causing the error is attached
the sandboxes. 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.
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 ```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.
Example of using `script.runInThisContext()` to compile code once and run it * `columnOffset` {number} Specifies the column number offset that is displayed
multiple times: 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` 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 ```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.
## vm.runInContext(code, contextifiedSandbox[, options])
`vm.runInContext()` compiles `code`, then runs it in `contextifiedSandbox` and Returns `true` if the given `sandbox` object has been [contextified][] using
returns the result. Running code does not have access to local scope. The [`vm.createContext()`][].
`contextifiedSandbox` object must have been previously contextified via
[`vm.createContext()`][]; it will be used as the global object for `code`.
`vm.runInContext()` takes the same options as [`vm.runInThisContext()`][]. ## vm.runInContext(code, contextifiedSandbox[, options])
Example: compile and execute different scripts in a single existing context. * `code` {string} The JavaScript code to compile and run.
* `contextifiedSandbox` {Object} The [contextified][] object that will be used
as the `global` when the `code` is compiled and run.
* `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.
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.
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`
`vm.runInNewContext()` takes the same options as [`vm.runInThisContext()`][]. * `filename` {string} Specifies the filename used in stack traces produced
by this script.
Example: compile and execute code that increments a global variable and sets a * `lineOffset` {number} Specifies the line number offset that is displayed
new one. These globals are contained in the sandbox. 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.
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.
The following example compiles and executes code that increments a global
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.
Example of using `vm.runInThisContext()` and [`eval()`][] to run the same code: * `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.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.
In this way `vm.runInThisContext()` is much like an [indirect `eval()` call][], `(0,eval)('code')`.
e.g. `(0,eval)('code')`. However, it also has the following additional options:
## Example: Running an HTTP Server within a VM
- `filename`: allows you to control the filename that shows up in any stack
traces produced. When using either `script.runInThisContext()` or `vm.runInThisContext()`, the
- `lineOffset`: allows you to add an offset to the line number that is code is executed within the current V8 global context. The code passed
displayed in stack traces to this VM context will have its own isolated scope.
- `columnOffset`: allows you to add an offset to the column number that is
displayed in stack traces In order to run a simple web server using the `http` module the code passed to
- `displayErrors`: if `true`, on error, attach the line of code that caused the context must either call `require('http')` on its own, or have a reference
the error to the stack trace. Will capture both syntax errors from compiling to the `http` module passed to it. For instance:
`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
The context of `.runInThisContext()` refers to the V8 context. The code passed
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

Loading…
Cancel
Save