Skip to content

VM (JavaScript の実行)

[安定版: 2 - 安定版]

安定版: 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 のコードキャッシュデータを含むオプションのBufferTypedArray、または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

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()メソッドによって返されるコンテキスト化されたオブジェクト。

  • 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)
// 出力: { 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.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または関数コンストラクター(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)
// 出力: [{ 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)
// sourcemap.jsonを出力します
js
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

[安定版: 1 - 実験的]

安定版: 1 安定性: 1 - 実験的

この機能は、--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 モジュールローダーよりも低レベルにあります。ローダーとやり取りする方法はまだありませんが、サポートは計画されています。

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
//
// このモジュールのインポートされた依存関係をそれにリンクします。
//
// 提供されたリンキングコールバック(「リンカー」)は、親モジュール(この場合は`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()
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
  //
  // このモジュールのインポートされた依存関係をそれにリンクします。
  //
  // 提供されたリンキングコールバック(「リンカー」)は、親モジュール(この場合は`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])

  • 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() concrete methodフィールドに対応します。

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>

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

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

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

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

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

リンカー関数は、実装定義の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ではない値に設定されています。

クラス: vm.SourceTextModule

追加されたバージョン: v9.6.0

[安定版: 1 - 試験段階]

安定版: 1 安定性: 1 - 試験段階

この機能は、--experimental-vm-modulesコマンドフラグを有効にして使用できます。

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

new vm.SourceTextModule(code[, options])

[履歴]

バージョン変更
v17.0.0, v16.12.0importModuleDynamicallyパラメータへの import 属性のサポートを追加しました。
  • code <string> パースする JavaScript モジュールコード

  • options

    • identifier <string> スタックトレースで使用される文字列。デフォルト: 'vm:module(i)' ここでiはコンテキスト固有の昇順インデックスです。

    • cachedData <Buffer> | <TypedArray> | <DataView> 指定されたソースの V8 のコードキャッシュデータを含むオプションのBufferTypedArray、または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()を使用してください。

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 })

クラス: vm.SyntheticModule

追加バージョン: v13.0.0, v12.16.0

[安定版: 1 - 試験的]

安定版: 1 安定性: 1 - 試験的

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

vm.SyntheticModule クラスは、WebIDL 仕様で定義されている合成モジュールレコードを提供します。合成モジュールの目的は、非 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> vm.createContext() メソッドによって返される、コンテキスト化されたオブジェクト。この Module をコンパイルして評価するために使用されます。

新しい 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 のコードキャッシュデータを含むオプションのBufferTypedArray、またはDataViewを提供します。これは、同じcodeparamsで以前の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

[安定版: 1 - 試験版]

安定版: 1 安定性: 1.1 - アクティブ開発中

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

詳細については、コンパイル 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第 1 引数は関数ではなくなりました。
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> 表示目的のために新しく作成されたコンテキストに対応するOrigin。origin は URL のようにフォーマットする必要がありますが、スキーム、ホスト、ポート(必要な場合)のみを使用します。url.originプロパティの値のようにURLオブジェクト。特に、この文字列は末尾のスラッシュを省略する必要があります。これはパスを表すためです。デフォルト: ''

    • 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)
// 出力: { globalVar: 2 }

console.log(global.globalVar)
// 出力: 3

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

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

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

コンテキストの指定されたnameoriginは、Inspector API を介して表示されます。

vm.isContext(object)

追加: v0.11.7

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

vm.measureMemory([options])

追加: v13.10.0

[安定版: 1 - 試験段階]

安定版: 1 安定性: 1 - 試験段階

現在の 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 インスタンス内の各ヒープスペースによって占有されているメモリを測定します。

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 パラメータへのインポート属性のサポートを追加
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 のコードキャッシュデータを含むオプションの BufferTypedArray、または 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)
// 出力: { 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またはコンテキスト化されるオブジェクトundefinedの場合は、後方互換性のために空のコンテキスト化されたオブジェクトが作成されます。

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

    • contextCodeGeneration <Object>

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

    • wasm <boolean> falseに設定されている場合、WebAssembly モジュールのコンパイルの試みはWebAssembly.CompileErrorをスローします。デフォルト: true

    • cachedData <Buffer> | <TypedArray> | <DataView> V8 のコードキャッシュデータを含むオプションのBufferTypedArray、または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に含まれています。

js
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.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 コード。

  • 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 のコードキャッシュデータを含むオプションの BufferTypedArray、または 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}'`)
// 出力: 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モジュールへの参照を渡す必要があります。例えば:

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

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

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 関数の実行が完了した後に実行されます。これにより、timeoutbreakOnSigintオプションの機能を回避できます。

たとえば、タイムアウトを 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()によってスケジュールされた 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で拒否されます。

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.runInContextvm.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
// 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' } }
js
// 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' } }
})()