Skip to content

VM (JavaScript の実行)

[Stable: 2 - 安定]

Stable: 2 安定度: 2 - 安定

ソースコード: lib/vm.js

node:vm モジュールは、V8 仮想マシンのコンテキスト内でコードをコンパイルして実行することを可能にします。

node:vm モジュールはセキュリティ機構ではありません。信頼できないコードを実行するために使用しないでください。

JavaScript コードは、即座にコンパイルして実行することも、コンパイル、保存して後で実行することもできます。

一般的なユースケースは、異なる V8 コンテキストでコードを実行することです。これは、呼び出されたコードが、呼び出し元のコードとは異なるグローバルオブジェクトを持つことを意味します。

オブジェクトを コンテキスト化 することで、コンテキストを提供できます。呼び出されたコードは、コンテキスト内の任意のプロパティをグローバル変数のように扱います。呼び出されたコードによって引き起こされたグローバル変数への変更は、コンテキストオブジェクトに反映されます。

js
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.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加。
v17.0.0, v16.12.0importModuleDynamically パラメータへの import 属性のサポートを追加。
v10.6.0produceCachedDatascript.createCachedData() に賛成して非推奨になりました。
v5.7.0cachedDataproduceCachedData オプションがサポートされるようになりました。
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> truecachedData が存在しない場合、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

cachedDatavm.Script の作成に提供されると、この値は V8 によるデータの受け入れに応じて true または false に設定されます。 それ以外の場合、値は undefined です。

script.createCachedData()

追加: v10.6.0

Script コンストラクタの cachedData オプションで使用できるコードキャッシュを作成します。 Buffer を返します。 このメソッドはいつでも、何度でも呼び出すことができます。

Script のコードキャッシュには、JavaScript で観測可能な状態は含まれていません。 コードキャッシュは、スクリプトソースと一緒に保存し、新しい Script インスタンスを複数回構築するために使用しても安全です。

Script ソース内の関数は、遅延コンパイルとしてマークすることができ、Script の構築時にはコンパイルされません。 これらの関数は、最初に呼び出されたときにコンパイルされます。 コードキャッシュは、V8 が現在 Script について知っているメタデータをシリアライズし、将来のコンパイルを高速化するために使用できます。

js
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.0breakOnSigint オプションがサポートされるようになりました。
v0.3.1追加: v0.3.1
  • contextifiedObject <Object> vm.createContext() メソッドによって返される contextified オブジェクト。

  • 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 オブジェクトに含まれています。

js
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);
// Prints: { animal: 'cat', count: 12, name: 'kitty' }

timeout または breakOnSigint オプションを使用すると、新しいイベントループと対応するスレッドが開始され、パフォーマンスのオーバーヘッドが発生します。

script.runInNewContext([contextObject[, options]])

[履歴]

バージョン変更点
v22.8.0, v20.18.0contextObject 引数は vm.constants.DONT_CONTEXTIFY を受け入れるようになりました。
v14.6.0microtaskMode オプションがサポートされるようになりました。
v10.0.0contextCodeGeneration オプションがサポートされるようになりました。
v6.3.0breakOnSigint オプションがサポートされるようになりました。
v0.3.1Added in: 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> 表示を目的として新しく作成されたコンテキストに対応する オリジン。 オリジンは URL のようにフォーマットする必要がありますが、スキーム、ホスト、ポートのみを含みます(必要な場合)。URL オブジェクトの url.origin プロパティの値のように。 最も注目すべきことは、この文字列は末尾のスラッシュを省略する必要があるということです。それはパスを示すからです。 デフォルト: ''.

    • contextCodeGeneration <Object>

    • strings <boolean> false に設定すると、eval または関数コンストラクター (FunctionGeneratorFunction など) への呼び出しはすべて EvalError をスローします。 デフォルト: true.

    • wasm <boolean> false に設定すると、WebAssembly モジュールをコンパイルしようとすると WebAssembly.CompileError がスローされます。 デフォルト: true.

    • microtaskMode <string> afterEvaluate に設定すると、マイクロタスク (Promiseasync function を通じてスケジュールされたタスク) は、スクリプトの実行直後に実行されます。 その場合、それらは timeoutbreakOnSigint スコープに含まれます。

  • 戻り値: <any> スクリプトで実行された最後のステートメントの結果。

このメソッドは script.runInContext(vm.createContext(options), options) へのショートカットです。 これは一度にいくつかのことを行います:

次の例は、グローバル変数を設定するコードをコンパイルし、次に異なるコンテキストでコードを複数回実行します。 グローバル変数は、個々の context 上に設定され、その中に含まれます。

js
const vm = require('node:vm');

const script = new vm.Script('globalVar = "set"');

const contexts = [{}, {}, {}];
contexts.forEach((context) => {
  script.runInNewContext(context);
});

console.log(contexts);
// Prints: [{ 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.0breakOnSigint オプションがサポートされるようになりました。
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 変数をインクリメントするコードをコンパイルし、そのコードを複数回実行します。

js
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に設定されます。

js
import vm from 'node:vm';

const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);

console.log(script.sourceMapURL);
// Prints: sourcemap.json
js
const vm = require('node:vm');

const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);

console.log(script.sourceMapURL);
// Prints: sourcemap.json

クラス: vm.Module

追加: v13.0.0, v12.16.0

[Stable: 1 - 試験的]

Stable: 1 安定度: 1 - 試験的

この機能は、--experimental-vm-modules コマンドフラグが有効になっている場合にのみ使用できます。

vm.Module クラスは、VMコンテキストでECMAScriptモジュールを使用するための低レベルのインターフェースを提供します。これは、ECMAScript仕様で定義されているモジュールレコードを厳密に反映する vm.Script クラスの対応物です。

ただし、vm.Script とは異なり、すべての vm.Module オブジェクトは作成時からコンテキストにバインドされています。 vm.Module オブジェクトに対する操作は本質的に非同期ですが、vm.Script オブジェクトは同期的な性質を持っています。「async」関数を使用すると、vm.Module オブジェクトの操作に役立ちます。

vm.Module オブジェクトを使用するには、作成/パース、リンク、評価という3つの異なるステップが必要です。次の例は、これら3つのステップを示しています。

この実装は、ECMAScriptモジュールローダーよりも低いレベルにあります。また、ローダーとのインタラクションはまだできませんが、サポートが計画されています。

js
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
//
// このモジュールのインポートされた依存関係を "リンク" します。
//
// 提供されたリンクコールバック(「リンカー」)は、2つの引数を受け取ります。
// 親モジュール(この場合は `bar` )と、インポートされたモジュールの指定子である文字列です。
// コールバックは、指定された指定子に対応するモジュールを返すことが期待されています。
// `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(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);

// ステップ 3
//
// モジュールを評価します。 evaluate() メソッドは、モジュールの評価が完了した後に
// 解決されるPromiseを返します。

// 42 が出力されます。
await bar.evaluate();
js
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
  //
  // このモジュールのインポートされた依存関係を "リンク" します。
  //
  // 提供されたリンクコールバック(「リンカー」)は、2つの引数を受け取ります。
  // 親モジュール(この場合は `bar` )と、インポートされたモジュールの指定子である文字列です。
  // コールバックは、指定された指定子に対応するモジュールを返すことが期待されています。
  // `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(`Unable to resolve dependency: ${specifier}`);
  }
  await bar.link(linker);

  // ステップ 3
  //
  // モジュールを評価します。 evaluate() メソッドは、モジュールの評価が完了した後に
  // 解決されるPromiseを返します。

  // 42 が出力されます。
  await bar.evaluate();
})();

module.dependencySpecifiers

このモジュールのすべての依存関係の指定子。返される配列は、変更できないように凍結されます。

ECMAScript仕様の巡回モジュールレコード[[RequestedModules]]フィールドに対応します。

module.error

module.status'errored'の場合、このプロパティにはモジュールの評価中にスローされた例外が含まれます。ステータスがそれ以外の場合、このプロパティにアクセスすると例外がスローされます。

undefinedという値は、throw undefined;との曖昧さの可能性があるため、スローされた例外がない場合には使用できません。

ECMAScript仕様の巡回モジュールレコード[[EvaluationError]]フィールドに対応します。

module.evaluate([options])

  • options <Object>

    • timeout <integer> 実行を終了するまでに評価するミリ秒数を指定します。実行が中断された場合、Errorがスローされます。この値は厳密に正の整数でなければなりません。
    • breakOnSigint <boolean> trueの場合、SIGINT (+)を受信すると実行を終了し、Errorをスローします。process.on('SIGINT')を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。デフォルト: false
  • 戻り値: <Promise> 成功時にundefinedで履行します。

モジュールを評価します。

これは、モジュールがリンクされた後に呼び出す必要があります。そうでない場合は拒否されます。また、モジュールがすでに評価されている場合にも呼び出すことができます。その場合、最初の評価が成功で終了した場合は何もしないか(module.status'evaluated'の場合)、最初の評価の結果として発生した例外を再度スローします(module.status'errored'の場合)。

このメソッドは、モジュールの評価中(module.status'evaluating'の場合)には呼び出すことはできません。

ECMAScript仕様の巡回モジュールレコードEvaluate() 抽象メソッドフィールドに対応します。

module.identifier

コンストラクタで設定された、現在のモジュールの識別子。

module.link(linker)

[履歴]

バージョン変更点
v21.1.0, v20.10.0, v18.19.0オプション extra.assertextra.attributes に名前が変更されました。以前の名前は下位互換性のためにまだ提供されています。
  • linker <Function>

    • specifier <string> リクエストされたモジュールの指定子:

    • referencingModule <vm.Module> link() が呼び出される Module オブジェクト。

    • extra <Object>

    • attributes <Object> 属性からのデータ: ECMA-262 に従って、サポートされていない属性が存在する場合、ホストはエラーをトリガーすることが期待されます。

    • assert <Object> extra.attributes のエイリアス。

    • 戻り値: <vm.Module> | <Promise>

  • 戻り値: <Promise>

モジュールの依存関係をリンクします。 このメソッドは評価前に呼び出す必要があり、モジュールごとに 1 回だけ呼び出すことができます。

この関数は、Module オブジェクト、または最終的に Module オブジェクトに解決される Promise を返すことが期待されます。 返される Module は、次の 2 つの不変条件を満たす必要があります。

  • Module と同じコンテキストに属している必要があります。
  • その status'errored' であってはなりません。

返された Modulestatus'unlinked' の場合、このメソッドは、提供された同じ linker 関数を使用して、返された Module に対して再帰的に呼び出されます。

link() は、すべてのリンクインスタンスが有効な Module に解決されたときに解決されるか、リンカー関数が例外をスローするか、無効な Module を返した場合に拒否される Promise を返します。

リンカー関数は、ECMAScript 仕様の HostResolveImportedModule 抽象操作の実装定義にほぼ対応していますが、いくつかの重要な違いがあります。

モジュールのリンク中に使用される実際の HostResolveImportedModule 実装は、リンク中にリンクされたモジュールを返すものです。 その時点で、すべてのモジュールはすでに完全にリンクされているため、HostResolveImportedModule 実装は仕様に従って完全に同期しています。

ECMAScript 仕様の Cyclic Module RecordLink() 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 ではない値に設定されています。

Class: vm.SourceTextModule

Added in: v9.6.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1 - Experimental

この機能は、--experimental-vm-modules コマンドフラグが有効になっている場合にのみ利用可能です。

vm.SourceTextModule クラスは、ECMAScript 仕様で定義されている Source Text Module Record を提供します。

new vm.SourceTextModule(code[, options])

[History]

VersionChanges
v17.0.0, v16.12.0importModuleDynamically パラメーターへの import 属性のサポートを追加しました。
  • code <string> 解析する JavaScript モジュールコード
  • options
    • identifier <string> スタックトレースで使用される文字列。デフォルト: 'vm:module(i)'。ここで i はコンテキスト固有の昇順インデックスです。

    • cachedData <Buffer> | <TypedArray> | <DataView> 提供されるソースに対する V8 のコードキャッシュデータを含むオプションの Buffer または TypedArray、または DataView を提供します。code は、この cachedData が作成されたモジュールと同じである必要があります。

    • context <Object> この Module をコンパイルおよび評価するための、vm.createContext() メソッドによって返される contextified オブジェクト。コンテキストが指定されていない場合、モジュールは現在の実行コンテキストで評価されます。

    • lineOffset <integer> この Module によって生成されるスタックトレースに表示される行番号オフセットを指定します。デフォルト: 0

    • columnOffset <integer> この Module によって生成されるスタックトレースに表示される最初の行の列番号オフセットを指定します。デフォルト: 0

    • initializeImportMeta <Function> import.meta を初期化するために、この Module の評価中に呼び出されます。

    • meta <import.meta>

    • module <vm.SourceTextModule>

    • importModuleDynamically <Function> import() が呼び出されたときに、このモジュールの評価中にモジュールをどのようにロードするかを指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API での動的 import() のサポート を参照してください。

新しい SourceTextModule インスタンスを作成します。

オブジェクトである import.meta オブジェクトに割り当てられたプロパティは、モジュールが指定された context の外部の情報にアクセスすることを許可する場合があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext() を使用します。

js
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);
js
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

SourceTextModule コンストラクターの cachedData オプションで使用できるコードキャッシュを作成します。Buffer を返します。このメソッドは、モジュールが評価される前に何度でも呼び出すことができます。

SourceTextModule のコードキャッシュには、JavaScript から観測可能な状態は含まれていません。コードキャッシュは、スクリプトソースと一緒に保存し、新しい SourceTextModule インスタンスを複数回構築するために安全に使用できます。

SourceTextModule ソース内の関数は、遅延コンパイルとしてマークでき、SourceTextModule の構築時にはコンパイルされません。これらの関数は、最初に呼び出されたときにコンパイルされます。コードキャッシュは、V8 が現在 SourceTextModule について把握しているメタデータをシリアライズし、将来のコンパイルを高速化するために使用できます。

js
// 最初のモジュールを作成
const module = new vm.SourceTextModule('const a = 1;');

// このモジュールからキャッシュされたデータを作成
const cachedData = module.createCachedData();

// キャッシュされたデータを使用して新しいモジュールを作成します。コードは同じである必要があります。
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData });

Class: vm.SyntheticModule

追加: v13.0.0, v12.16.0

[安定: 1 - 試験的]

安定: 1 安定度: 1 - 試験的

この機能は、--experimental-vm-modules コマンドフラグが有効になっている場合にのみ使用できます。

vm.SyntheticModule クラスは、WebIDL 仕様で定義されている Synthetic Module Record を提供します。シンセティックモジュールの目的は、非 JavaScript ソースを ECMAScript モジュールグラフに公開するための汎用インターフェースを提供することです。

js
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> この Module をコンパイルおよび評価するために、vm.createContext() メソッドによって返される、コンテキスト化されたオブジェクト。

新しい SyntheticModule インスタンスを作成します。

このインスタンスのエクスポートに割り当てられたオブジェクトにより、モジュールのインポーターは指定された context の外部にある情報にアクセスできるようになる場合があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext() を使用します。

syntheticModule.setExport(name, value)

追加: v13.0.0, v12.16.0

  • name <string> 設定するエクスポートの名前。
  • value <any> エクスポートに設定する値。

このメソッドは、モジュールがリンクされた後、エクスポートの値を設定するために使用されます。モジュールがリンクされる前に呼び出された場合、ERR_VM_MODULE_STATUS エラーがスローされます。

js
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);
js
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.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加。
v19.6.0, v18.15.0cachedData オプションが渡された場合、戻り値に vm.Script バージョンと同じセマンティクスを持つ cachedDataRejected が含まれるようになりました。
v17.0.0, v16.12.0importModuleDynamically パラメーターへの import 属性のサポートを追加。
v15.9.0importModuleDynamically オプションを再度追加。
v14.3.0互換性の問題のため、importModuleDynamically を削除。
v14.1.0, v13.14.0importModuleDynamically オプションがサポートされるようになりました。
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> 問題の関数をコンパイルする コンテキスト化 オブジェクト。
    • 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

[安定版: 1 - 試験的]

安定版: 1 安定度: 1.1 - アクティブな開発

Node.jsがリクエストされたモジュールをロードするために、メインコンテキストからデフォルトのESMローダーを使用するように、vm.Scriptvm.compileFunction()importModuleDynamicallyオプションとして使用できる定数です。

詳細については、コンパイルAPIにおける動的import()のサポートを参照してください。

vm.createContext([contextObject[, options]])

[履歴]

バージョン変更点
v22.8.0, v20.18.0contextObject引数はvm.constants.DONT_CONTEXTIFYを受け入れるようになりました。
v21.7.0, v20.12.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADERのサポートが追加されました。
v21.2.0, v20.11.0importModuleDynamicallyオプションがサポートされるようになりました。
v14.6.0microtaskModeオプションがサポートされるようになりました。
v10.0.0最初の引数は関数ではなくなりました。
v10.0.0codeGenerationオプションがサポートされるようになりました。
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> 表示を目的として、新しく作成されたコンテキストに対応するオリジン。 オリジンはURLのようにフォーマットする必要がありますが、URLオブジェクトのurl.originプロパティの値のように、スキーム、ホスト、およびポート(必要な場合)のみを含みます。 特に、この文字列は末尾のスラッシュを省略する必要があります。これはパスを示すためです。 デフォルト: ''

    • codeGeneration <Object>

    • strings <boolean> falseに設定すると、evalまたは関数コンストラクタ(FunctionGeneratorFunctionなど)への呼び出しはEvalErrorをスローします。 デフォルト: true

    • wasm <boolean> falseに設定すると、WebAssemblyモジュールをコンパイルしようとするとWebAssembly.CompileErrorがスローされます。 デフォルト: true

    • microtaskMode <string> afterEvaluateに設定すると、マイクロタスク(Promiseasync functionを介してスケジュールされたタスク)は、スクリプトがscript.runInContext()を介して実行された直後に実行されます。 この場合、それらはtimeoutbreakOnSigintのスコープに含まれます。

    • importModuleDynamically <Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> リファラースクリプトまたはモジュールなしでこのコンテキストでimport()が呼び出されたときに、モジュールをロードする方法を指定するために使用されます。 このオプションは、試験的なモジュールAPIの一部です。 本番環境での使用はお勧めしません。 詳細については、コンパイルAPIにおける動的import()のサポートを参照してください。

  • 戻り値: <Object> コンテキスト化されたオブジェクト。

指定されたcontextObjectがオブジェクトの場合、vm.createContext()メソッドはそのオブジェクトを準備しvm.runInContext()またはscript.runInContext()の呼び出しで使用できるように、そのオブジェクトへの参照を返します。 このようなスクリプト内では、グローバルオブジェクトはcontextObjectによってラップされ、既存のすべてのプロパティを保持しますが、標準的なグローバルオブジェクトが持つ組み込みオブジェクトと関数も持ちます。 vmモジュールによって実行されるスクリプトの外部では、グローバル変数は変更されません。

js
const vm = require('node:vm');

global.globalVar = 3;

const context = { globalVar: 1 };
vm.createContext(context);

vm.runInContext('globalVar *= 2;', context);

console.log(context);
// Prints: { globalVar: 2 }

console.log(global.globalVar);
// Prints: 3

contextObjectが省略された場合(または明示的にundefinedとして渡された場合)、新しく空のコンテキスト化されたオブジェクトが返されます。

新しく作成されたコンテキストのグローバルオブジェクトがコンテキスト化されると、通常のグローバルオブジェクトと比較して、いくつかの癖があります。 たとえば、凍結できません。 コンテキスト化の癖のないコンテキストを作成するには、contextObject引数としてvm.constants.DONT_CONTEXTIFYを渡します。 詳細については、vm.constants.DONT_CONTEXTIFYのドキュメントを参照してください。

vm.createContext()メソッドは、主に複数のスクリプトを実行するために使用できる単一のコンテキストを作成するのに役立ちます。 たとえば、Webブラウザーをエミュレートする場合、このメソッドを使用して、ウィンドウのグローバルオブジェクトを表す単一のコンテキストを作成し、そのコンテキスト内で一緒にすべての\<script\>タグを実行できます。

コンテキストに提供されたnameoriginは、Inspector APIを通じて表示されます。

vm.isContext(object)

Added in: v0.11.7

与えられた object オブジェクトが vm.createContext() を用いて contextified されているか、または vm.constants.DONT_CONTEXTIFY を用いて生成されたコンテキストのグローバルオブジェクトである場合に、true を返します。

vm.measureMemory([options])

Added in: v13.10.0

[Stable: 1 - 実験的]

Stable: 1 Stability: 1 - 実験的

V8 が把握しており、現在の V8 アイソレートが把握しているすべてのコンテキスト、またはメインコンテキストで使用されているメモリを計測します。

  • options <Object> Optional.

    • mode <string> 'summary' または 'detailed' のいずれか。 summary モードでは、メインコンテキストのために計測されたメモリのみが返されます。 detailed モードでは、現在の V8 アイソレートが把握しているすべてのコンテキストのために計測されたメモリが返されます。 Default: 'summary'
    • execution <string> 'default' または 'eager' のいずれか。 デフォルトの実行では、次のスケジュールされたガベージコレクションが開始されるまで (時間がかかる場合や、次の GC の前にプログラムが終了する場合は決して行われない場合があります)、promise は解決されません。 eager 実行では、メモリを計測するために GC がすぐに開始されます。 Default: 'default'
  • 戻り値: <Promise> メモリの計測に成功すると、promise はメモリ使用量に関する情報を含むオブジェクトで解決されます。 それ以外の場合は、ERR_CONTEXT_NOT_INITIALIZED エラーで拒否されます。

返される Promise が解決する可能性のあるオブジェクトの形式は V8 エンジンに固有であり、V8 のバージョンによって変更される可能性があります。

返される結果は v8.getHeapSpaceStatistics() によって返される統計情報とは異なります。vm.measureMemory() は V8 エンジンの現在のインスタンスの各 V8 固有のコンテキストから到達可能なメモリを計測しますが、v8.getHeapSpaceStatistics() の結果は現在の V8 インスタンスの各ヒープ領域によって占有されているメモリを計測します。

js
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.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加。
v17.0.0, v16.12.0importModuleDynamically パラメーターへの import 属性のサポートを追加。
v6.3.0breakOnSigint オプションがサポートされるようになりました。
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 が文字列の場合、ファイル名を指定します。

次の例は、単一の コンテキスト化されたオブジェクトを使用して、異なるスクリプトをコンパイルして実行します。

js
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);
// Prints: { globalVar: 1024 }

vm.runInNewContext(code[, contextObject[, options]])

[履歴]

バージョン変更点
v22.8.0, v20.18.0contextObject 引数は vm.constants.DONT_CONTEXTIFY を受け入れるようになりました。
v21.7.0, v20.12.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートが追加されました。
v17.0.0, v16.12.0importModuleDynamically パラメータへの import 属性のサポートが追加されました。
v14.6.0microtaskMode オプションがサポートされるようになりました。
v10.0.0contextCodeGeneration オプションがサポートされるようになりました。
v6.3.0breakOnSigint オプションがサポートされるようになりました。
v0.3.1追加: v0.3.1
  • code <string> コンパイルおよび実行する JavaScript コード。

  • contextObject <Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined> vm.constants.DONT_CONTEXTIFY または contextified されるオブジェクト。undefined の場合、下位互換性のために空の contextified オブジェクトが作成されます。

  • 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

    • contextName <string> 新しく作成されたコンテキストの人間が読める名前。デフォルト: 'VM Context i'。ここで i は、作成されたコンテキストの昇順の数値インデックスです。

    • contextOrigin <string> 表示の目的で、新しく作成されたコンテキストに対応する オリジン。オリジンは、URL のようにフォーマットする必要がありますが、スキーム、ホスト、およびポート (必要な場合) のみを含みます。これは、URL オブジェクトの url.origin プロパティの値のようです。最も注目すべき点は、この文字列は末尾のスラッシュを省略する必要があることです。これはパスを示すためです。デフォルト: ''

    • contextCodeGeneration <Object>

    • strings <boolean> false に設定すると、eval または関数コンストラクター (FunctionGeneratorFunction など) の呼び出しは 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 に設定すると、マイクロタスク (Promise および async function を介してスケジュールされたタスク) は、スクリプトの実行直後に実行されます。その場合、それらは timeout および breakOnSigint スコープに含まれます。

  • 戻り値: <any> スクリプトで実行された最後のステートメントの結果。

このメソッドは、(new vm.Script(code, options)).runInContext(vm.createContext(options), options) へのショートカットです。options が文字列の場合、ファイル名を指定します。

これは、いくつかのことを同時に行います。

次の例は、グローバル変数をインクリメントし、新しい変数を設定するコードをコンパイルして実行します。これらのグローバル変数は contextObject に含まれています。

js
const vm = require('node:vm');

const contextObject = {
  animal: 'cat',
  count: 2,
};

vm.runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' }

// これは、コンテキストが contextified オブジェクトから作成された場合、例外をスローします。
// 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.0vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER のサポートを追加しました。
v17.0.0, v16.12.0importModuleDynamically パラメーターへのインポート属性のサポートを追加しました。
v6.3.0breakOnSigint オプションがサポートされるようになりました。
v0.3.1v0.3.1 で追加されました
  • code <string> コンパイルおよび実行する JavaScript コード。

  • 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() のサポートを参照してください。
  • 戻り値: <any> スクリプトで実行された最後のステートメントの結果。

vm.runInThisContext()code をコンパイルし、現在の global のコンテキスト内で実行し、結果を返します。 実行中のコードはローカルスコープにアクセスできませんが、現在の global オブジェクトにはアクセスできます。

options が文字列の場合、ファイル名を指定します。

次の例は、vm.runInThisContext() と JavaScript の eval() 関数の両方を使用して同じコードを実行する方法を示しています。

js
const vm = require('node:vm');
let localVar = 'initial value';

const vmResult = vm.runInThisContext('localVar = "vm";');
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`);
// Prints: vmResult: 'vm', localVar: 'initial value'

const evalResult = eval('localVar = "eval";');
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`);
// Prints: 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モジュールを使用して簡単なWebサーバーを実行するには、コンテキストに渡されるコードがrequire('node:http')を独自に呼び出すか、node:httpモジュールへの参照が渡される必要があります。例えば:

js
'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 Contextの新しいインスタンスのグローバルオブジェクトをラップするために使用されます(contextObjectundefinedの場合、コンテキスト化される前に現在のコンテキストから新しいオブジェクトが作成されます)。このV8 Contextは、node:vmモジュールのメソッドを使用して実行されるcodeに、操作できる隔離されたグローバル環境を提供します。V8 Contextを作成し、それを外部コンテキストのcontextObjectに関連付けるプロセスは、このドキュメントでオブジェクトを「コンテキスト化」すると呼ばれるものです。

コンテキスト化すると、コンテキスト内のglobalThis値にいくつかの癖が生じます。たとえば、フリーズできず、外部コンテキストのcontextObjectと参照が等しくありません。

js
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

通常のグローバルオブジェクトを持つコンテキストを作成し、癖の少ない外部コンテキストでグローバルプロキシにアクセスするには、contextObject引数としてvm.constants.DONT_CONTEXTIFYを指定します。

vm.constants.DONT_CONTEXTIFY

この定数は、vm API で contextObject 引数として使用される場合、Node.js 固有の方法でグローバルオブジェクトを別のオブジェクトでラップせずにコンテキストを作成するように Node.js に指示します。その結果、新しいコンテキスト内の globalThis の値は、通常のコンテキストにより近い動作をします。

js
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_CONTEXTIFYvm.createContext()contextObject 引数として使用される場合、返されるオブジェクトは、Node.js 固有の癖が少ない、新しく作成されたコンテキストのグローバルオブジェクトへのプロキシのようなオブジェクトです。これは、新しいコンテキスト内の globalThis の値と参照が等しく、コンテキストの外部から変更でき、新しいコンテキストの組み込み関数に直接アクセスするために使用できます。

js
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 のタイムアウトの相互作用

Promiseasync function は、JavaScript エンジンによって非同期的に実行されるタスクをスケジュールできます。デフォルトでは、これらのタスクは、現在のスタック上のすべての JavaScript 関数の実行が完了した後に実行されます。これにより、timeout および breakOnSigint オプションの機能を回避できます。

たとえば、タイムアウトが 5 ミリ秒の vm.runInNewContext() によって実行される次のコードは、Promise が解決された後に実行される無限ループをスケジュールします。スケジュールされたループは、タイムアウトによって中断されることはありません。

js
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' を渡すことで対処できます。

js
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() を介してスケジュールされたマイクロタスクは、vm.runInNewContext() から戻る前に実行され、timeout 機能によって中断されます。これは vm.Context で実行されているコードにのみ適用されるため、たとえば、vm.runInThisContext() はこのオプションを受け入れません。

Promise コールバックは、作成されたコンテキストのマイクロタスクキューに入力されます。たとえば、上記の例で () =\> loop()loop だけで置き換えられた場合、loop はグローバルマイクロタスクキューにプッシュされます。これは、それが外部 (メイン) コンテキストからの関数であり、したがってタイムアウトを回避することもできるためです。

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 で拒否されます。

importModuleDynamicallyvm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER の場合

このオプションは、現在 vm.SourceTextModule ではサポートされていません。

このオプションを使用すると、コンパイルされたコードで import() が開始されたときに、Node.js はメインコンテキストからのデフォルトの ESM ローダーを使用して、要求されたモジュールをロードし、実行されているコードに返します。

これにより、コンパイルされているコードは fshttp などの Node.js 組み込みモジュールにアクセスできるようになります。コードが異なるコンテキストで実行されている場合、メインコンテキストからロードされたモジュールによって作成されたオブジェクトは、新しいコンテキストの組み込みクラスの instanceof ではなく、メインコンテキストのオブジェクトであることに注意してください。

js
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);
js
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);

このオプションを使用すると、スクリプトまたは関数はユーザーモジュールをロードすることもできます。

js
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);
js
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.Scriptvm.runInThisContextvm.runInContext および vm.runInNewContext に対してコンパイルされた vm.Script です。vm.compileFunction に対してコンパイルされた Functionnew vm.SourceTextModule に対してコンパイルされた vm.SourceTextModulevm.createContext() に対してコンテキスト Object です。
  • importAttributes <Object> optionsExpression オプションのパラメータに渡される "with" の値。値が指定されていない場合は空のオブジェクト。
  • 戻り値: <Module Namespace Object> | <vm.Module> エラー追跡を利用し、then 関数エクスポートを含む名前空間の問題を回避するために、vm.Module を返すことをお勧めします。
js
// このスクリプトは --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);   // コンパイルされたスクリプト
    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' } }
js
// このスクリプトは --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);   // コンパイルされたスクリプト
      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' } }
})();