VM (JavaScript の実行)
ソースコード: lib/vm.js
node:vm
モジュールは、V8 仮想マシンコンテキスト内でコードをコンパイルおよび実行できるようにします。
node:vm
モジュールはセキュリティメカニズムではありません。信頼できないコードの実行には使用しないでください。
JavaScript コードは、即座にコンパイルして実行することも、コンパイルして保存し、後で実行することもできます。
一般的なユースケースは、異なる V8 コンテキストでコードを実行することです。これは、呼び出されたコードが呼び出し元コードとは異なるグローバルオブジェクトを持つことを意味します。
オブジェクトをコンテキスト化 することで、コンテキストを提供できます。呼び出されたコードは、コンテキスト内のプロパティをグローバル変数のように扱います。呼び出されたコードによって発生したグローバル変数の変更は、コンテキストオブジェクトに反映されます。
const vm = require('node:vm')
const x = 1
const context = { x: 2 }
vm.createContext(context) // オブジェクトをコンテキスト化します。
const code = 'x += 40; var y = 17;'
// `x`と`y`はコンテキスト内のグローバル変数です。
// 最初に、xの値はcontext.xの値である2になります。
vm.runInContext(code, context)
console.log(context.x) // 42
console.log(context.y) // 17
console.log(x) // 1; yは定義されていません。
クラス: vm.Script
追加されたバージョン: v0.3.1
vm.Script
クラスのインスタンスには、特定のコンテキストで実行できる事前にコンパイルされたスクリプトが含まれています。
new vm.Script(code[, options])
[履歴]
バージョン | 変更 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加 |
v17.0.0, v16.12.0 | importModuleDynamically パラメーターへの import 属性のサポートを追加 |
v10.6.0 | produceCachedData はscript.createCachedData() に非推奨となりました。 |
v5.7.0 | cachedData とproduceCachedData オプションがサポートされるようになりました。 |
v0.3.1 | 追加されたバージョン: v0.3.1 |
code
<string> コンパイルする JavaScript コード。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。デフォルト:'evalmachine.\<anonymous\>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号のオフセットを指定します。デフォルト:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。デフォルト:0
。cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。提供された場合、cachedDataRejected
の値は、V8 によるデータの受け入れに応じてtrue
またはfalse
に設定されます。produceCachedData
<boolean>true
の場合、cachedData
が存在しない場合、V8 はcode
のコードキャッシュデータの生成を試みます。成功すると、V8 のコードキャッシュデータを含むBuffer
が生成され、返されたvm.Script
インスタンスのcachedData
プロパティに格納されます。cachedDataProduced
の値は、コードキャッシュデータが正常に生成されたかどうかによってtrue
またはfalse
に設定されます。このオプションは、script.createCachedData()
に代わり非推奨です。デフォルト:false
。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API での動的なimport()
のサポートを参照してください。
options
が文字列の場合、ファイル名を指定します。
新しいvm.Script
オブジェクトを作成するとcode
がコンパイルされますが、実行されません。コンパイルされたvm.Script
は後で複数回実行できます。code
はグローバルオブジェクトにバインドされていません。代わりに、各実行の前に、その実行のためにのみバインドされます。
script.cachedDataRejected
追加日: v5.7.0
cachedData
がvm.Script
の作成に使用された場合、この値は V8 によるデータの受け入れに応じてtrue
またはfalse
に設定されます。それ以外の場合はundefined
です。
script.createCachedData()
追加日: v10.6.0
- 戻り値: <Buffer>
Script
コンストラクタのcachedData
オプションで使用できるコードキャッシュを作成します。Buffer
を返します。このメソッドはいつでも何回でも呼び出すことができます。
Script
のコードキャッシュには、JavaScript で観測可能な状態は含まれていません。コードキャッシュは、スクリプトソースと共に保存し、新しいScript
インスタンスを複数回構築するために安全に使用できます。
Script
ソース内の関数は、遅延コンパイルとしてマークでき、Script
の構築時にはコンパイルされません。これらの関数は、初めて呼び出されたときにコンパイルされます。コードキャッシュは、V8 が現在Script
について知っているメタデータをシリアライズし、将来のコンパイルを高速化するために使用できます。
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`)
const cacheWithoutAdd = script.createCachedData()
// `cacheWithoutAdd`では、関数`add()`は呼び出し時に完全コンパイルされるようにマークされています。
script.runInThisContext()
const cacheWithAdd = script.createCachedData()
// `cacheWithAdd`には、完全にコンパイルされた関数`add()`が含まれています。
script.runInContext(contextifiedObject[, options])
[履歴]
バージョン | 変更 |
---|---|
v6.3.0 | breakOnSigint オプションがサポートされました。 |
v0.3.1 | 追加日: v0.3.1 |
contextifiedObject
<Object>vm.createContext()
メソッドによって返されるコンテキスト化されたオブジェクト。options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行ミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は厳密に正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(+)を受信すると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラは、スクリプト実行中は無効になりますが、その後は動作し続けます。デフォルト:false
。
戻り値: <any> スクリプトで実行された最後のステートメントの結果。
vm.Script
オブジェクトに含まれるコンパイル済みコードを指定されたcontextifiedObject
内で実行し、結果を返します。実行中のコードはローカルスコープにアクセスできません。
次の例では、グローバル変数をインクリメントするコードをコンパイルし、別のグローバル変数の値を設定してから、コードを複数回実行します。グローバルはcontext
オブジェクトに含まれています。
const vm = require('node:vm')
const context = {
animal: 'cat',
count: 2,
}
const script = new vm.Script('count += 1; name = "kitty";')
vm.createContext(context)
for (let i = 0; i < 10; ++i) {
script.runInContext(context)
}
console.log(context)
// 出力: { animal: 'cat', count: 12, name: 'kitty' }
timeout
またはbreakOnSigint
オプションを使用すると、新しいイベントループとそれに対応するスレッドが開始され、ゼロ以外の性能オーバーヘッドが発生します。
script.runInNewContext([contextObject[, options]])
[履歴]
バージョン | 変更 |
---|---|
v22.8.0, v20.18.0 | contextObject 引数がvm.constants.DONT_CONTEXTIFY を受け取るようになりました。 |
v14.6.0 | microtaskMode オプションがサポートされました。 |
v10.0.0 | contextCodeGeneration オプションがサポートされました。 |
v6.3.0 | breakOnSigint オプションがサポートされました。 |
v0.3.1 | 追加:v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
または、コンテキスト化されるオブジェクトのいずれか。undefined
の場合、後方互換性のために空のコンテキスト化されたオブジェクトが作成されます。options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行ミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は厳密に正の整数でなければなりません。breakOnSigint
<boolean>true
の場合、SIGINT
(+)を受信すると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントに対する既存のハンドラーは、スクリプト実行中は無効になりますが、その後は機能し続けます。デフォルト:false
。contextName
<string> 新しく作成されたコンテキストの人間が判読可能な名前。デフォルト:'VM Context i'
(ここでi
は作成されたコンテキストの上昇数値インデックスです)。contextOrigin
<string> 表示目的のために新しく作成されたコンテキストに対応するOrigin。オリジンは URL のようにフォーマットする必要がありますが、スキーム、ホスト、ポート(必要な場合)のみを使用し、url.origin
プロパティの値のようになりますURL
オブジェクト。特に、この文字列は末尾のスラッシュを省略する必要があります。これはパスを示しているためです。デフォルト:''
。contextCodeGeneration
<Object>strings
<boolean> false に設定されている場合、eval
または関数コンストラクター(Function
、GeneratorFunction
など)への呼び出しはすべてEvalError
をスローします。デフォルト:true
。wasm
<boolean> false に設定されている場合、WebAssembly モジュールのコンパイルの試みはすべてWebAssembly.CompileError
をスローします。デフォルト:true
。microtaskMode
<string>afterEvaluate
に設定されている場合、マイクロタスク(Promise
とasync function
を介してスケジュールされたタスク)は、スクリプトの実行直後に実行されます。その場合、それらはtimeout
とbreakOnSigint
のスコープに含まれます。
戻り値: <any> スクリプトで実行された最後のステートメントの結果。
このメソッドはscript.runInContext(vm.createContext(options), options)
へのショートカットです。一度にいくつかのことを行います。
次の例では、グローバル変数を設定するコードをコンパイルし、異なるコンテキストでそのコードを複数回実行します。グローバル変数は、各個々のcontext
に設定され、含まれています。
const vm = require('node:vm')
const script = new vm.Script('globalVar = "set"')
const contexts = [{}, {}, {}]
contexts.forEach(context => {
script.runInNewContext(context)
})
console.log(contexts)
// 出力: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
// コンテキストがコンテキスト化されたオブジェクトから作成された場合、これはスローされます。
// vm.constants.DONT_CONTEXTIFYを使用すると、凍結できる通常のグローバルオブジェクトを使用してコンテキストを作成できます。
const freezeScript = new vm.Script('Object.freeze(globalThis); globalThis;')
const frozenContext = freezeScript.runInNewContext(vm.constants.DONT_CONTEXTIFY)
script.runInThisContext([options])
[履歴]
バージョン | 変更点 |
---|---|
v6.3.0 | breakOnSigint オプションがサポートされました。 |
v0.3.1 | 追加: v0.3.1 |
options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行ミリ秒数を指定します。実行が終了されると、Error
がスローされます。この値は厳密に正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(+) を受信すると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプト実行中は無効になりますが、その後は機能し続けます。デフォルト:false
。
戻り値: <any> スクリプトで実行された最後のステートメントの結果。
vm.Script
に含まれるコンパイル済みコードを現在の global
オブジェクトのコンテキスト内で実行します。実行中のコードはローカルスコープにアクセスできませんが、現在の global
オブジェクトにはアクセスできます。
次の例では、global
変数をインクリメントするコードをコンパイルし、そのコードを複数回実行します。
const vm = require('node: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
script.sourceMapURL
追加日時: v19.1.0, v18.13.0
スクリプトがソースマップマジックコメントを含むソースからコンパイルされた場合、このプロパティはソースマップの URL に設定されます。
import vm from 'node:vm'
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// sourcemap.jsonを出力します
const vm = require('node:vm')
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// sourcemap.jsonを出力します
クラス: vm.Module
追加日時: v13.0.0, v12.16.0
この機能は、--experimental-vm-modules
コマンドフラグを有効にした場合のみ使用できます。
vm.Module
クラスは、VM コンテキストで ECMAScript モジュールを使用するための低レベルインターフェースを提供します。これは、ECMAScript 仕様で定義されているModule Recordを綿密に反映したvm.Script
クラスの対応物です。
ただし、vm.Script
とは異なり、すべてのvm.Module
オブジェクトは、作成時からコンテキストにバインドされます。vm.Module
オブジェクトに対する操作は、vm.Script
オブジェクトの同期的な性質とは対照的に、本質的に非同期です。「async」関数の使用は、vm.Module
オブジェクトの操作に役立ちます。
vm.Module
オブジェクトを使用するには、作成/解析、リンク、評価の 3 つの異なるステップが必要です。これらの 3 つのステップは、次の例で説明されています。
この実装は、ECMAScript モジュールローダーよりも低レベルにあります。ローダーとやり取りする方法はまだありませんが、サポートは計画されています。
import vm from 'node:vm'
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
// ステップ1
//
// 新しい`vm.SourceTextModule`オブジェクトを構築することにより、モジュールを作成します。
// これは提供されたソーステキストを解析し、何か問題が発生した場合は`SyntaxError`をスローします。
// デフォルトでは、モジュールはトップコンテキストで作成されます。しかしここでは、`contextifiedObject`をこのモジュールが属するコンテキストとして指定します。
//
// ここでは、モジュール "foo" からデフォルトエクスポートを取得し、それをローカルバインディング "secret" に入れることを試みます。
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// ステップ2
//
// このモジュールのインポートされた依存関係をそれにリンクします。
//
// 提供されたリンキングコールバック(「リンカー」)は、親モジュール(この場合は`bar`)とインポートされたモジュールの指定子である文字列の2つの引数を受け取ります。
// コールバックは、`module.link()`に記載されている特定の要件を満たした、提供された指定子に対応するモジュールを返すことが期待されます。
//
// 返されたモジュールに対してリンクが開始されていない場合、同じリンカーコールバックが返されたモジュールで呼び出されます。
//
// 依存関係のないトップレベルモジュールでも、明示的にリンクする必要があります。ただし、提供されたコールバックは呼び出されません。
//
// link()メソッドは、リンカーによって返されたすべてのPromiseが解決されたときに解決されるPromiseを返します。
//
// 注:これは、リンカー関数が呼び出されるたびに新しい「foo」モジュールを作成するという点で、作り付けの例です。本格的なモジュールシステムでは、重複したモジュールを避けるためにキャッシュが使用される可能性があります。
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(
`
// "secret"変数は、コンテキストを作成するときに"contextifiedObject"に追加したグローバル変数を参照します。
export default secret;
`,
{ context: referencingModule.context }
)
// ここでは`referencingModule.context`ではなく`contextifiedObject`を使用しても機能します。
}
throw new Error(`依存関係を解決できません: ${specifier}`)
}
await bar.link(linker)
// ステップ3
//
// モジュールを評価します。evaluate()メソッドは、モジュールの評価が完了した後に解決されるPromiseを返します。
// 42を出力します。
await bar.evaluate()
const vm = require('node:vm')
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
;(async () => {
// ステップ1
//
// 新しい`vm.SourceTextModule`オブジェクトを構築することにより、モジュールを作成します。
// これは提供されたソーステキストを解析し、何か問題が発生した場合は`SyntaxError`をスローします。
// デフォルトでは、モジュールはトップコンテキストで作成されます。しかしここでは、`contextifiedObject`をこのモジュールが属するコンテキストとして指定します。
//
// ここでは、モジュール "foo" からデフォルトエクスポートを取得し、それをローカルバインディング "secret" に入れることを試みます。
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// ステップ2
//
// このモジュールのインポートされた依存関係をそれにリンクします。
//
// 提供されたリンキングコールバック(「リンカー」)は、親モジュール(この場合は`bar`)とインポートされたモジュールの指定子である文字列の2つの引数を受け取ります。
// コールバックは、`module.link()`に記載されている特定の要件を満たした、提供された指定子に対応するモジュールを返すことが期待されます。
//
// 返されたモジュールに対してリンクが開始されていない場合、同じリンカーコールバックが返されたモジュールで呼び出されます。
//
// 依存関係のないトップレベルモジュールでも、明示的にリンクする必要があります。ただし、提供されたコールバックは呼び出されません。
//
// link()メソッドは、リンカーによって返されたすべてのPromiseが解決されたときに解決されるPromiseを返します。
//
// 注:これは、リンカー関数が呼び出されるたびに新しい「foo」モジュールを作成するという点で、作り付けの例です。本格的なモジュールシステムでは、重複したモジュールを避けるためにキャッシュが使用される可能性があります。
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(
`
// "secret"変数は、コンテキストを作成するときに"contextifiedObject"に追加したグローバル変数を参照します。
export default secret;
`,
{ context: referencingModule.context }
)
// ここでは`referencingModule.context`ではなく`contextifiedObject`を使用しても機能します。
}
throw new Error(`依存関係を解決できません: ${specifier}`)
}
await bar.link(linker)
// ステップ3
//
// モジュールを評価します。evaluate()メソッドは、モジュールの評価が完了した後に解決されるPromiseを返します。
// 42を出力します。
await bar.evaluate()
})()
module.dependencySpecifiers
このモジュールのすべての依存関係の指定子。返される配列は変更できないように凍結されています。
ECMAScript 仕様の循環モジュールレコードの[[RequestedModules]]
フィールドに対応します。
module.error
module.status
が'errored'
の場合、このプロパティには評価中にモジュールによってスローされた例外が含まれます。ステータスが他の場合は、このプロパティにアクセスすると例外がスローされます。
throw undefined;
とのあいまいさを避けるため、例外がスローされない場合にundefined
を使用することはできません。
ECMAScript 仕様の循環モジュールレコードの[[EvaluationError]]
フィールドに対応します。
module.evaluate([options])
モジュールを評価します。
これは、モジュールがリンクされた後に呼び出す必要があります。そうでない場合は拒否されます。モジュールが既に評価されている場合にも呼び出すことができます。その場合、最初の評価が成功した場合(module.status
が'evaluated'
の場合)は何も行わなかったり、最初の評価の結果例外を再スローします(module.status
が'errored'
の場合)。
このメソッドは、モジュールが評価中である間(module.status
が'evaluating'
の場合)は呼び出すことができません。
ECMAScript 仕様の循環モジュールレコードのEvaluate() concrete methodフィールドに対応します。
module.identifier
コンストラクタで設定された、現在のモジュールの識別子。
module.link(linker)
[履歴]
バージョン | 変更 |
---|---|
v21.1.0, v20.10.0, v18.19.0 | オプションextra.assert はextra.attributes に名前変更されました。旧名は後方互換性のために引き続き提供されます。 |
linker
<Function>specifier
<string> 要求されたモジュールの指定子:referencingModule
<vm.Module>link()
が呼び出されたModule
オブジェクト。extra
<Object>attributes
<Object> 属性からのデータ: ECMA-262 に従い、サポートされていない属性が存在する場合、ホストはエラーを発生させる必要があります。assert
<Object>extra.attributes
のエイリアス。戻り値: <vm.Module> | <Promise>
戻り値: <Promise>
モジュール依存関係をリンクします。このメソッドは評価の前に呼び出す必要があり、モジュールごとに一度だけ呼び出すことができます。
この関数は、最終的にModule
オブジェクトに解決されるModule
オブジェクトまたはPromise
を返すことが期待されます。返されたModule
は、次の 2 つの不変式を満たす必要があります。
- 親
Module
と同じコンテキストに属している必要があります。 - その
status
は'errored'
であってはなりません。
返されたModule
のstatus
が'unlinked'
の場合、このメソッドは、同じlinker
関数を使用して、返されたModule
に対して再帰的に呼び出されます。
link()
は、すべてのリンクインスタンスが有効なModule
に解決された場合に解決されるか、リンカー関数が例外をスローするか無効なModule
を返す場合に拒否されるPromise
を返します。
リンカー関数は、実装定義のHostResolveImportedModule抽象操作とほぼ対応しますが、いくつかの重要な違いがあります。
- リンカー関数は非同期になることが許されますが、HostResolveImportedModuleは同期です。
モジュールリンク中に使用される実際のHostResolveImportedModule実装は、リンク中にリンクされたモジュールを返す実装です。その時点ですべてのモジュールは既に完全にリンクされているため、HostResolveImportedModule実装は仕様に従って完全に同期しています。
ECMAScript 仕様のCyclic Module RecordのLink() concrete methodフィールドに対応します。
module.namespace
モジュールの名前空間オブジェクト。これは、リンク(module.link()
)が完了した後にのみ使用できます。
ECMAScript 仕様のGetModuleNamespace抽象操作に対応します。
module.status
モジュールの現在の状態。以下のいずれかになります。
'unlinked'
:module.link()
はまだ呼び出されていません。'linking'
:module.link()
は呼び出されましたが、リンカー関数によって返されたすべての Promise はまだ解決されていません。'linked'
:モジュールは正常にリンクされ、そのすべての依存関係もリンクされていますが、module.evaluate()
はまだ呼び出されていません。'evaluating'
:モジュールは、自身または親モジュールに対するmodule.evaluate()
によって評価されています。'evaluated'
:モジュールは正常に評価されました。'errored'
:モジュールは評価されましたが、例外がスローされました。
'errored'
以外、このステータス文字列は仕様のCyclic Module Recordの[[Status]]
フィールドに対応します。'errored'
は仕様では'evaluated'
に対応しますが、[[EvaluationError]]
がundefined
ではない値に設定されています。
クラス: vm.SourceTextModule
追加されたバージョン: v9.6.0
この機能は、--experimental-vm-modules
コマンドフラグを有効にして使用できます。
- 拡張元: <vm.Module>
vm.SourceTextModule
クラスは、ECMAScript 仕様で定義されているSource Text Module Recordを提供します。
new vm.SourceTextModule(code[, options])
[履歴]
バージョン | 変更 |
---|---|
v17.0.0, v16.12.0 | importModuleDynamically パラメータへの import 属性のサポートを追加しました。 |
code
<string> パースする JavaScript モジュールコードoptions
identifier
<string> スタックトレースで使用される文字列。デフォルト:'vm:module(i)'
ここでi
はコンテキスト固有の昇順インデックスです。cachedData
<Buffer> | <TypedArray> | <DataView> 指定されたソースの V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。code
は、このcachedData
が作成されたモジュールと同じでなければなりません。context
<Object>vm.createContext()
メソッドによって返されるコンテキスト化されたオブジェクトで、このModule
をコンパイルして評価します。コンテキストが指定されていない場合、モジュールは現在の実行コンテキストで評価されます。lineOffset
<integer> このModule
によって生成されたスタックトレースに表示される行番号のオフセットを指定します。デフォルト:0
。columnOffset
<integer> このModule
によって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。デフォルト:0
。initializeImportMeta
<Function> このModule
の評価中にimport.meta
を初期化するために呼び出されます。meta
<import.meta>module
<vm.SourceTextModule>importModuleDynamically
<Function>import()
が呼び出されたときに、このモジュールの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポートを参照してください。
新しいSourceTextModule
インスタンスを作成します。
import.meta
オブジェクトに割り当てられたオブジェクトのプロパティにより、モジュールは指定されたcontext
の外側の情報にアクセスできる可能性があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext()
を使用してください。
import vm from 'node:vm'
const contextifiedObject = vm.createContext({ secret: 42 })
const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {}
},
})
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {})
await module.evaluate()
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
const vm = require('node:vm')
const contextifiedObject = vm.createContext({ secret: 42 })
;(async () => {
const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {}
},
})
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {})
await module.evaluate()
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
})()
sourceTextModule.createCachedData()
追加バージョン: v13.7.0, v12.17.0
- 戻り値: <Buffer>
SourceTextModule
コンストラクターの cachedData
オプションで使用できるコードキャッシュを作成します。Buffer
を返します。このメソッドは、モジュールが評価される前に何度でも呼び出すことができます。
SourceTextModule
のコードキャッシュには、JavaScript のオブザーバブルな状態は含まれていません。コードキャッシュは、スクリプトソースと一緒に保存して、新しい SourceTextModule
インスタンスを複数回構築するために安全に使用できます。
SourceTextModule
ソース内の関数は、遅延コンパイルとしてマークでき、SourceTextModule
の構築時にはコンパイルされません。これらの関数は、初めて呼び出されたときにコンパイルされます。コードキャッシュは、V8 が現在 SourceTextModule
について知っているメタデータをシリアル化し、将来のコンパイルを高速化するために使用できます。
// 初期モジュールを作成する
const module = new vm.SourceTextModule('const a = 1;')
// このモジュールからキャッシュデータを作成する
const cachedData = module.createCachedData()
// キャッシュデータを使用して新しいモジュールを作成する。コードは同じでなければならない。
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })
クラス: vm.SyntheticModule
追加バージョン: v13.0.0, v12.16.0
この機能は、--experimental-vm-modules
コマンドフラグを有効にしている場合のみ使用できます。
- 拡張: <vm.Module>
vm.SyntheticModule
クラスは、WebIDL 仕様で定義されている合成モジュールレコードを提供します。合成モジュールの目的は、非 JavaScript ソースを ECMAScript モジュールグラフに公開するための汎用インターフェースを提供することです。
const vm = require('node:vm')
const source = '{ "a": 1 }'
const module = new vm.SyntheticModule(['default'], function () {
const obj = JSON.parse(source)
this.setExport('default', obj)
})
// リンク処理で `module` を使用する…
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
追加: v13.0.0, v12.16.0
exportNames
<string[]> モジュールからエクスポートされる名前の配列。evaluateCallback
<Function> モジュールが評価されるときに呼び出されます。options
identifier
<string> スタックトレースで使用される文字列。デフォルト:'vm:module(i)'
(i
はコンテキスト固有の昇順インデックス)。context
<Object>vm.createContext()
メソッドによって返される、コンテキスト化されたオブジェクト。このModule
をコンパイルして評価するために使用されます。
新しい SyntheticModule
インスタンスを作成します。
このインスタンスのエクスポートに割り当てられたオブジェクトにより、モジュールのインポーターは、指定された context
の外部の情報にアクセスできる可能性があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext()
を使用してください。
syntheticModule.setExport(name, value)
追加: v13.0.0, v12.16.0
このメソッドは、モジュールがリンクされた後に、エクスポートの値を設定するために使用されます。モジュールがリンクされる前に呼び出されると、ERR_VM_MODULE_STATUS
エラーがスローされます。
import vm from 'node:vm'
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1)
})
await m.link(() => {})
await m.evaluate()
assert.strictEqual(m.namespace.x, 1)
const vm = require('node:vm')
;(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1)
})
await m.link(() => {})
await m.evaluate()
assert.strictEqual(m.namespace.x, 1)
})()
vm.compileFunction(code[, params[, options]])
[履歴]
バージョン | 変更点 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER へのサポートを追加 |
v19.6.0, v18.15.0 | cachedData オプションが渡された場合、vm.Script バージョンと同じセマンティクスを持つcachedDataRejected が戻り値に含まれるようになりました。 |
v17.0.0, v16.12.0 | importModuleDynamically パラメータへの import 属性のサポートを追加 |
v15.9.0 | importModuleDynamically オプションを再び追加 |
v14.3.0 | 互換性の問題のため、importModuleDynamically を削除 |
v14.1.0, v13.14.0 | importModuleDynamically オプションがサポートされるようになりました。 |
v10.10.0 | 追加: v10.10.0 |
code
<string> コンパイルする関数の本体。params
<string[]> 関数のすべてのパラメータを含む文字列の配列。options
<Object>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。 デフォルト:''
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号のオフセットを指定します。 デフォルト:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。 デフォルト:0
。cachedData
<Buffer> | <TypedArray> | <DataView> 指定されたソースの V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。これは、同じcode
とparams
で以前のvm.compileFunction()
呼び出しによって生成されている必要があります。produceCachedData
<boolean> 新しいキャッシュデータを作成するかどうかを指定します。 デフォルト:false
。parsingContext
<Object> 前述の関数をコンパイルするコンテキスト化された[/api/vm#what-does-it-mean-to-contextify-an-object]オブジェクト。contextExtensions
<Object[]> コンパイル中に適用されるコンテキスト拡張(現在のスコープをラップするオブジェクト)のコレクションを含む配列。 デフォルト:[]
。
importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、この関数の評価中にモジュールをロードする方法を指定するために使用されます。このオプションは実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API での動的import()
のサポートを参照してください。戻り値: <Function>
指定されたコードを指定されたコンテキストにコンパイルし(コンテキストが提供されない場合は現在のコンテキストを使用)、指定されたparams
でラップされた関数内で返します。
vm.constants
追加: v21.7.0, v20.12.0
VM 操作で一般的に使用される定数を含むオブジェクトを返します。
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
追加: v21.7.0, v20.12.0
vm.Script
とvm.compileFunction()
のimportModuleDynamically
オプションとして使用できる定数です。これにより、Node.js はメインコンテキストからデフォルトの ESM ローダーを使用して、要求されたモジュールをロードします。
詳細については、コンパイル API における動的import()
のサポートを参照してください。
vm.createContext([contextObject[, options]])
[履歴]
バージョン | 変更 |
---|---|
v22.8.0, v20.18.0 | contextObject 引数がvm.constants.DONT_CONTEXTIFY を受け入れるようになりました。 |
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加しました。 |
v21.2.0, v20.11.0 | importModuleDynamically オプションがサポートされるようになりました。 |
v14.6.0 | microtaskMode オプションがサポートされるようになりました。 |
v10.0.0 | 第 1 引数は関数ではなくなりました。 |
v10.0.0 | codeGeneration オプションがサポートされるようになりました。 |
v0.3.1 | 追加: v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
か、コンテキスト化されるオブジェクトのいずれか。undefined
の場合、後方互換性のために空のコンテキスト化されたオブジェクトが作成されます。options
<Object>name
<string> 新しく作成されたコンテキストの人間が読める名前。デフォルト:'VM Context i'
(i
は作成されたコンテキストの昇順の数字インデックス)。origin
<string> 表示目的のために新しく作成されたコンテキストに対応するOrigin。origin は URL のようにフォーマットする必要がありますが、スキーム、ホスト、ポート(必要な場合)のみを使用します。url.origin
プロパティの値のようにURL
オブジェクト。特に、この文字列は末尾のスラッシュを省略する必要があります。これはパスを表すためです。デフォルト:''
。codeGeneration
<Object>strings
<boolean>false
に設定されている場合、eval
または関数コンストラクタ(Function
、GeneratorFunction
など)への呼び出しはEvalError
をスローします。デフォルト:true
。wasm
<boolean>false
に設定されている場合、WebAssembly モジュールのコンパイルの試みはWebAssembly.CompileError
をスローします。デフォルト:true
。microtaskMode
<string>afterEvaluate
に設定されている場合、マイクロタスク(Promise
とasync function
でスケジュールされたタスク)は、スクリプトがscript.runInContext()
で実行された直後に実行されます。その場合、timeout
とbreakOnSigint
のスコープに含まれます。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> リファラースクリプトまたはモジュールなしでこのコンテキストでimport()
が呼び出されたときに、モジュールをロードする方法を指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポートを参照してください。
戻り値: <Object> コンテキスト化されたオブジェクト。
指定されたcontextObject
がオブジェクトの場合、vm.createContext()
メソッドは、そのオブジェクトを準備し、それに参照を返します。これにより、vm.runInContext()
またはscript.runInContext()
の呼び出しで使用できます。このようなスクリプト内では、グローバルオブジェクトはcontextObject
によってラップされ、既存のプロパティはすべて保持されますが、標準的なグローバルオブジェクトが持つすべての組み込みオブジェクトと関数が含まれます。vm モジュールによって実行されるスクリプトの外部では、グローバル変数は変更されません。
const vm = require('node:vm')
global.globalVar = 3
const context = { globalVar: 1 }
vm.createContext(context)
vm.runInContext('globalVar *= 2;', context)
console.log(context)
// 出力: { globalVar: 2 }
console.log(global.globalVar)
// 出力: 3
contextObject
を省略した場合(または明示的にundefined
として渡した場合)、新しく空のコンテキスト化されたオブジェクトが返されます。
新しく作成されたコンテキストのグローバルオブジェクトがコンテキスト化されると、通常のグローバルオブジェクトとは異なる点がいくつかあります。たとえば、フリーズできません。コンテキスト化の癖のないコンテキストを作成するには、contextObject
引数としてvm.constants.DONT_CONTEXTIFY
を渡します。詳細については、vm.constants.DONT_CONTEXTIFY
のドキュメントを参照してください。
vm.createContext()
メソッドは、複数のスクリプトを実行するために使用できる単一のコンテキストを作成する場合に主に役立ちます。たとえば、Web ブラウザをエミュレートする場合、このメソッドを使用してウィンドウのグローバルオブジェクトを表す単一のコンテキストを作成し、そのコンテキスト内ですべての\<script\>
タグをまとめて実行できます。
コンテキストの指定されたname
とorigin
は、Inspector API を介して表示されます。
vm.isContext(object)
追加: v0.11.7
与えられたobject
オブジェクトがvm.createContext()
を使用してコンテキスト化されているか、またはvm.constants.DONT_CONTEXTIFY
を使用して作成されたコンテキストのグローバルオブジェクトである場合、true
を返します。
vm.measureMemory([options])
追加: v13.10.0
現在の V8 isolate、またはメインコンテキストで認識されているすべてのコンテキストで使用され、V8 に認識されているメモリを測定します。
options
<Object> オプション。mode
<string>'summary'
または'detailed'
のいずれか。サマリーモードでは、メインコンテキストで測定されたメモリのみが返されます。詳細モードでは、現在の V8 isolate で認識されているすべてのコンテキストで測定されたメモリが返されます。デフォルト:'summary'
execution
<string>'default'
または'eager'
のいずれか。デフォルトの実行では、次のスケジュールされたガベージコレクションが開始されるまでプロミスは解決されません。これは時間がかかる場合があります(次の GC の前にプログラムが終了した場合は、決して解決されない可能性があります)。eager 実行では、メモリを測定するために GC がすぐに開始されます。デフォルト:'default'
戻り値: <Promise> メモリが正常に測定された場合、プロミスはメモリ使用量に関する情報を含むオブジェクトで解決されます。そうでない場合は、
ERR_CONTEXT_NOT_INITIALIZED
エラーで拒否されます。
返された Promise が解決する可能性のあるオブジェクトの形式は、V8 エンジンに固有であり、V8 のバージョンごとに変更される可能性があります。
返された結果は、v8.getHeapSpaceStatistics()
によって返される統計とは異なります。vm.measureMemory()
は現在の V8 エンジンのインスタンス内の各 V8 固有のコンテキストからアクセス可能なメモリを測定する一方、v8.getHeapSpaceStatistics()
の結果は現在の V8 インスタンス内の各ヒープスペースによって占有されているメモリを測定します。
const vm = require('node:vm')
// メインコンテキストで使用されるメモリを測定します。
vm.measureMemory({ mode: 'summary' })
// これは vm.measureMemory() と同じです
.then(result => {
// 現在の形式は次のとおりです。
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result)
})
const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
// 測定が完了するまでGCされないように、ここでコンテキストを参照します。
console.log(context.a)
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result)
})
vm.runInContext(code, contextifiedObject[, options])
[履歴]
バージョン | 変更内容 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加 |
v17.0.0, v16.12.0 | importModuleDynamically パラメータへのインポート属性のサポートを追加 |
v6.3.0 | breakOnSigint オプションがサポートされました |
v0.3.1 | 追加: v0.3.1 |
code
<string> コンパイルして実行する JavaScript コード。contextifiedObject
<Object>code
のコンパイルと実行時にglobal
として使用される コンテキスト化された オブジェクト。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。デフォルト:'evalmachine.\<anonymous\>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号のオフセットを指定します。デフォルト:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。デフォルト:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行時間をミリ秒単位で指定します。実行が終了されると、Error
がスローされます。この値は厳密に正の整数でなければなりません。breakOnSigint
<boolean>true
の場合、SIGINT
(+) を受信すると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後は機能し続けます。デフォルト:false
。cachedData
<Buffer> | <TypedArray> | <DataView> 指定されたソースの V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポート を参照してください。
vm.runInContext()
メソッドは code
をコンパイルし、contextifiedObject
のコンテキスト内で実行してから、結果を返します。実行中のコードはローカルスコープにアクセスできません。contextifiedObject
オブジェクトは、事前に vm.createContext()
メソッドを使用して コンテキスト化 されている 必要 があります。
options
が文字列の場合、ファイル名を指定します。
次の例では、単一の コンテキスト化された オブジェクトを使用して、さまざまなスクリプトをコンパイルして実行します。
const vm = require('node:vm')
const contextObject = { globalVar: 1 }
vm.createContext(contextObject)
for (let i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', contextObject)
}
console.log(contextObject)
// 出力: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])
[履歴]
バージョン | 変更点 |
---|---|
v22.8.0, v20.18.0 | contextObject 引数がvm.constants.DONT_CONTEXTIFY を受け入れるようになりました。 |
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加しました。 |
v17.0.0, v16.12.0 | importModuleDynamically パラメーターへの import 属性のサポートを追加しました。 |
v14.6.0 | microtaskMode オプションがサポートされるようになりました。 |
v10.0.0 | contextCodeGeneration オプションがサポートされるようになりました。 |
v6.3.0 | breakOnSigint オプションがサポートされるようになりました。 |
v0.3.1 | 追加されました: v0.3.1 |
code
<string> コンパイルして実行する JavaScript コード。contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
またはコンテキスト化されるオブジェクト。undefined
の場合は、後方互換性のために空のコンテキスト化されたオブジェクトが作成されます。filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。デフォルト:'evalmachine.<anonymous>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号のオフセットを指定します。デフォルト:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。デフォルト:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行時間をミリ秒単位で指定します。実行が終了すると、Error
がスローされます。この値は厳密に正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(+)を受け取ると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントに対する既存のハンドラーは、スクリプトの実行中は無効になりますが、その後は機能し続けます。デフォルト:false
。contextName
<string> 新しく作成されたコンテキストの分かりやすい名前。デフォルト:'VM Context i'
、ここでi
は作成されたコンテキストの昇順の数字インデックスです。contextOrigin
<string> 表示目的のために新しく作成されたコンテキストに対応するOrigin。origin は URL のようにフォーマットする必要がありますが、スキーム、ホスト、およびポート(必要な場合)のみを含みます。url.origin
プロパティの値のように、URL
オブジェクト。特に、この文字列は末尾のスラッシュを含めるべきではありません。デフォルト:''
。contextCodeGeneration
<Object>strings
<boolean>false
に設定されている場合、eval
または関数コンストラクター(Function
、GeneratorFunction
など)への呼び出しはEvalError
をスローします。デフォルト:true
。wasm
<boolean>false
に設定されている場合、WebAssembly モジュールのコンパイルの試みはWebAssembly.CompileError
をスローします。デフォルト:true
。cachedData
<Buffer> | <TypedArray> | <DataView> V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を指定します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API での動的なimport()
のサポートを参照してください。microtaskMode
<string>afterEvaluate
に設定されている場合、microtask(Promise
およびasync function
を介してスケジュールされたタスク)は、スクリプトの実行直後に実行されます。その場合、timeout
およびbreakOnSigint
スコープに含まれます。
戻り値: <any> スクリプトで実行された最後のステートメントの結果。
このメソッドは (new vm.Script(code, options)).runInContext(vm.createContext(options), options)
へのショートカットです。options
が文字列の場合、ファイル名を指定します。
一度にいくつかのことを行います。
次の例では、グローバル変数をインクリメントし、新しいグローバル変数を設定するコードをコンパイルして実行します。これらのグローバル変数はcontextObject
に含まれています。
const vm = require('node:vm')
const contextObject = {
animal: 'cat',
count: 2,
}
vm.runInNewContext('count += 1; name = "kitty"', contextObject)
console.log(contextObject)
// 出力: { animal: 'cat', count: 3, name: 'kitty' }
// コンテキストがコンテキスト化されたオブジェクトから作成された場合、これはスローされます。
// vm.constants.DONT_CONTEXTIFYを使用すると、凍結できる通常のグローバルオブジェクトを使用してコンテキストを作成できます。
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)
vm.runInThisContext(code[, options])
[履歴]
バージョン | 変更点 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加 |
v17.0.0, v16.12.0 | importModuleDynamically パラメータへの import 属性のサポートを追加 |
v6.3.0 | breakOnSigint オプションをサポート |
v0.3.1 | 追加: v0.3.1 |
code
<string> コンパイルして実行する JavaScript コード。filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。デフォルト:'evalmachine.\<anonymous\>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号のオフセットを指定します。デフォルト:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される最初の行の列番号のオフセットを指定します。デフォルト:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生すると、エラーの原因となったコード行がスタックトレースに添付されます。デフォルト:true
。timeout
<integer> 実行を終了するまでのcode
の実行時間をミリ秒単位で指定します。実行が終了すると、Error
がスローされます。この値は厳密に正の整数でなければなりません。breakOnSigint
<boolean>true
の場合、SIGINT
(+) を受信すると実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントに対する既存のハンドラは、スクリプト実行中は無効になりますが、その後は機能し続けます。デフォルト:false
。cachedData
<Buffer> | <TypedArray> | <DataView> V8 のコードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポートを参照してください。
戻り値: <any> スクリプトで実行された最後のステートメントの結果。
vm.runInThisContext()
は code
をコンパイルし、現在の global
のコンテキスト内で実行し、結果を返します。実行中のコードはローカルスコープにアクセスできませんが、現在の global
オブジェクトにアクセスできます。
options
が文字列の場合、ファイル名を指定します。
次の例は、vm.runInThisContext()
と JavaScript の eval()
関数の両方を使用して同じコードを実行する方法を示しています。
const vm = require('node:vm')
let localVar = 'initial value'
const vmResult = vm.runInThisContext('localVar = "vm";')
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`)
// 出力: vmResult: 'vm', localVar: 'initial value'
const evalResult = eval('localVar = "eval";')
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`)
// 出力: evalResult: 'eval', localVar: 'eval'
vm.runInThisContext()
はローカルスコープにアクセスできないため、localVar
は変更されません。対照的に、eval()
はローカルスコープにアクセスできるため、localVar
の値は変更されます。このように、vm.runInThisContext()
は 間接的な eval()
呼び出し 、例えば (0,eval)('code')
と非常によく似ています。
例:VM 内での HTTP サーバーの実行
script.runInThisContext()
またはvm.runInThisContext()
を使用する場合、コードは現在の V8 グローバルコンテキスト内で実行されます。この VM コンテキストに渡されるコードは、独自の分離されたスコープを持ちます。
node:http
モジュールを使用して単純なウェブサーバーを実行するには、コンテキストに渡されるコードが独自にrequire('node:http')
を呼び出すか、node:http
モジュールへの参照を渡す必要があります。例えば:
'use strict'
const vm = require('node:vm')
const code = `
((require) => {
const http = require('node:http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`
vm.runInThisContext(code)(require)
上記の例では、require()
は渡されたコンテキストと状態を共有します。これは、信頼できないコードが実行される場合、例えばコンテキスト内のオブジェクトを意図しない方法で変更するなど、リスクをもたらす可能性があります。
オブジェクトを「コンテキスト化」することの意味
Node.js 内で実行されるすべての JavaScript は、「コンテキスト」のスコープ内で実行されます。V8 Embedder's Guideによると:
vm.createContext()
メソッドがオブジェクトを伴って呼び出されると、contextObject
引数は、V8 コンテキストの新しいインスタンスのグローバルオブジェクトをラップするために使用されます(contextObject
がundefined
の場合、コンテキスト化される前に現在のコンテキストから新しいオブジェクトが作成されます)。この V8 コンテキストは、node:vm
モジュールのメソッドを使用して実行されるcode
に、動作できる分離されたグローバル環境を提供します。V8 コンテキストを作成し、それを外部コンテキストのcontextObject
に関連付けるプロセスが、このドキュメントで「オブジェクトのコンテキスト化」として参照されているものです。
コンテキスト化は、コンテキスト内のglobalThis
値にいくつかの癖を導入します。例えば、凍結できず、外部コンテキストのcontextObject
と参照的に等しくありません。
const vm = require('node:vm')
// undefined の `contextObject` オプションはグローバルオブジェクトをコンテキスト化します。
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// コンテキスト化されたグローバルオブジェクトは凍結できません。
try {
vm.runInContext('Object.freeze(globalThis);', context)
} catch (e) {
console.log(e) // TypeError: Cannot freeze
}
console.log(vm.runInContext('globalThis.foo = 1; foo;', context)) // 1
通常のグローバルオブジェクトを持つコンテキストを作成し、癖の少ない外部コンテキストのグローバルプロキシにアクセスするには、vm.constants.DONT_CONTEXTIFY
をcontextObject
引数として指定します。
vm.constants.DONT_CONTEXTIFY
この定数は、vm API の contextObject
引数として使用されると、Node.js に対して、グローバルオブジェクトを Node.js 固有の方法で別のオブジェクトでラップせずにコンテキストを作成するよう指示します。その結果、新しいコンテキスト内の globalThis
の値は、より普通の値に近い動作になります。
const vm = require('node:vm')
// vm.constants.DONT_CONTEXTIFY を使用してグローバルオブジェクトを凍結します。
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
vm.runInContext('Object.freeze(globalThis);', context)
try {
vm.runInContext('bar = 1; bar;', context)
} catch (e) {
console.log(e) // Uncaught ReferenceError: bar is not defined
}
vm.constants.DONT_CONTEXTIFY
を vm.createContext()
の contextObject
引数として使用すると、返されるオブジェクトは、新しく作成されたコンテキスト内のグローバルオブジェクトへのプロキシのようなオブジェクトになり、Node.js 固有の癖が少なくなります。それは新しいコンテキスト内の globalThis
値と参照が等しく、コンテキストの外から変更でき、新しいコンテキスト内の組み込み関数に直接アクセスするために使用できます。
const vm = require('node:vm')
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
// 返されたオブジェクトは、新しいコンテキスト内の globalThis と参照が等しいです。
console.log(vm.runInContext('globalThis', context) === context) // true
// 新しいコンテキスト内のグローバル変数に直接アクセスするために使用できます。
console.log(context.Array) // [Function: Array]
vm.runInContext('foo = 1;', context)
console.log(context.foo) // 1
context.bar = 1
console.log(vm.runInContext('bar;', context)) // 1
// 凍結でき、内部コンテキストにも影響します。
Object.freeze(context)
try {
vm.runInContext('baz = 1; baz;', context)
} catch (e) {
console.log(e) // Uncaught ReferenceError: baz is not defined
}
非同期タスクと Promise とのタイムアウトの相互作用
Promise
とasync function
は、JavaScript エンジンによって非同期的に実行されるタスクをスケジュールできます。デフォルトでは、これらのタスクは、現在のスタック上のすべての JavaScript 関数の実行が完了した後に実行されます。これにより、timeout
とbreakOnSigint
オプションの機能を回避できます。
たとえば、タイムアウトを 5 ミリ秒に設定したvm.runInNewContext()
によって実行される次のコードは、Promise が解決された後に無限ループを実行するようにスケジュールします。スケジュールされたループは、タイムアウトによって中断されることはありません。
const vm = require('node:vm')
function loop() {
console.log('entering loop')
while (1) console.log(Date.now())
}
vm.runInNewContext('Promise.resolve().then(() => loop());', { loop, console }, { timeout: 5 })
// これは 'entering loop' よりも前に出力されます (!)
console.log('done executing')
これは、Context
を作成するコードにmicrotaskMode: 'afterEvaluate'
を渡すことで対処できます。
const vm = require('node:vm')
function loop() {
while (1) console.log(Date.now())
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' }
)
この場合、promise.then()
によってスケジュールされた microtask は、vm.runInNewContext()
から戻る前に実行され、timeout
機能によって中断されます。これはvm.Context
で実行されるコードのみに適用されるため、たとえばvm.runInThisContext()
はこのオプションを受け取りません。
Promise コールバックは、それらが作成されたコンテキストの microtask キューに入力されます。たとえば、上記の例で() => loop()
がloop
に置き換えられた場合、loop
はグローバル microtask キューにプッシュされます。なぜなら、それは外部(メイン)コンテキストからの関数であり、したがってタイムアウトも回避できるからです。
process.nextTick()
、queueMicrotask()
、setTimeout()
、setImmediate()
などの非同期スケジューリング関数がvm.Context
内で利用可能になった場合、それらに渡される関数は、すべてのコンテキストで共有されるグローバルキューに追加されます。したがって、これらの関数に渡されたコールバックも、タイムアウトによって制御できません。
コンパイル API における動的import()
のサポート
以下の API は、vm
モジュールによってコンパイルされたコードで動的import()
を有効にするimportModuleDynamically
オプションをサポートしています。
new vm.Script
vm.compileFunction()
new vm.SourceTextModule
vm.runInThisContext()
vm.runInContext()
vm.runInNewContext()
vm.createContext()
このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。
importModuleDynamically
オプションが指定されていない場合、または未定義の場合
このオプションが指定されていない場合、またはundefined
の場合、import()
を含むコードはvm
API によってコンパイルできますが、コンパイルされたコードが実行され、実際にimport()
を呼び出すと、結果はERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING
で拒否されます。
importModuleDynamically
がvm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
の場合
このオプションは、現在vm.SourceTextModule
ではサポートされていません。
このオプションを使用すると、コンパイルされたコードでimport()
が開始された場合、Node.js はメインコンテキストのデフォルトの ESM ローダーを使用して要求されたモジュールを読み込み、実行中のコードに返します。
これにより、fs
やhttp
などの Node.js 組み込みモジュールをコンパイルされたコードからアクセスできます。コードが別のコンテキストで実行される場合は、メインコンテキストから読み込まれたモジュールによって作成されたオブジェクトは、依然としてメインコンテキストのものであり、新しいコンテキストの組み込みクラスのinstanceof
ではないことに注意してください。
const { Script, constants } = require('node:vm')
const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})
// false: メインコンテキストから読み込まれたURLは、新しいコンテキストのFunctionクラスのインスタンスではありません。
script.runInNewContext().then(console.log)
import { Script, constants } from 'node:vm'
const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})
// false: メインコンテキストから読み込まれたURLは、新しいコンテキストのFunctionクラスのインスタンスではありません。
script.runInNewContext().then(console.log)
このオプションを使用すると、スクリプトまたは関数はユーザーモジュールを読み込むこともできます。
import { Script, constants } from 'node:vm'
import { resolve } from 'node:path'
import { writeFileSync } from 'node:fs'
// 現在実行されているスクリプトがあるディレクトリにtest.jsとtest.txtを書き込みます。
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')
// test.mjsを読み込み、その後test.jsonをスクリプトが同じディレクトリにあるかのように読み込むスクリプトをコンパイルします。
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(import.meta.dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
}
)
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)
const { Script, constants } = require('node:vm')
const { resolve } = require('node:path')
const { writeFileSync } = require('node:fs')
// 現在実行されているスクリプトがあるディレクトリにtest.jsとtest.txtを書き込みます。
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')
// test.mjsを読み込み、その後test.jsonをスクリプトが同じディレクトリにあるかのように読み込むスクリプトをコンパイルします。
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(__dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
}
)
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)
メインコンテキストからデフォルトローダーを使用してユーザーモジュールを読み込むには、いくつかの注意点があります。
importModuleDynamically
が関数の場合
importModuleDynamically
が関数の時、コンパイル済みコードでimport()
が呼び出されると、要求されたモジュールをどのようにコンパイルして評価するかをユーザーがカスタマイズするために呼び出されます。現在、このオプションを動作させるには、Node.js インスタンスを--experimental-vm-modules
フラグを付けて起動する必要があります。フラグが設定されていない場合、このコールバックは無視されます。評価されたコードが実際にimport()
を呼び出す場合、結果はERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG
で拒否されます。
コールバックimportModuleDynamically(specifier, referrer, importAttributes)
は以下のシグネチャを持ちます。
specifier
<string>import()
に渡される指定子referrer
<vm.Script> | <Function> | <vm.SourceTextModule> | <Object> referrer は、new vm.Script
、vm.runInThisContext
、vm.runInContext
、vm.runInNewContext
のコンパイル済みvm.Script
です。vm.compileFunction
の場合はコンパイル済みFunction
、new vm.SourceTextModule
の場合はコンパイル済みvm.SourceTextModule
、vm.createContext()
の場合はコンテキストObject
です。importAttributes
<Object>optionsExpression
オプションパラメーターに渡された"with"
値、または値が提供されなかった場合は空のオブジェクト。- 戻り値: <Module Namespace Object> | <vm.Module> エラー追跡の利点を活用し、
then
関数のエクスポートを含む名前空間の問題を回避するために、vm.Module
を返すことをお勧めします。
// This script must be run with --experimental-vm-modules.
import { Script, SyntheticModule } from 'node:vm'
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier) // 'foo.json'
console.log(referrer) // The compiled script
console.log(importAttributes) // { type: 'json' }
const m = new SyntheticModule(['bar'], () => {})
await m.link(() => {})
m.setExport('bar', { hello: 'world' })
return m
},
})
const result = await script.runInThisContext()
console.log(result) // { bar: { hello: 'world' } }
// This script must be run with --experimental-vm-modules.
const { Script, SyntheticModule } = require('node:vm')
;(async function main() {
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier) // 'foo.json'
console.log(referrer) // The compiled script
console.log(importAttributes) // { type: 'json' }
const m = new SyntheticModule(['bar'], () => {})
await m.link(() => {})
m.setExport('bar', { hello: 'world' })
return m
},
})
const result = await script.runInThisContext()
console.log(result) // { bar: { hello: 'world' } }
})()