VM (JavaScript 실행)
소스 코드: lib/vm.js
node:vm
모듈은 V8 가상 머신 컨텍스트 내에서 코드를 컴파일하고 실행할 수 있도록 합니다.
node:vm
모듈은 보안 메커니즘이 아닙니다. 신뢰할 수 없는 코드를 실행하는 데 사용하지 마십시오.
JavaScript 코드는 즉시 컴파일 및 실행하거나 컴파일, 저장 및 나중에 실행할 수 있습니다.
일반적인 사용 사례는 다른 V8 컨텍스트에서 코드를 실행하는 것입니다. 이는 호출된 코드가 호출하는 코드와 다른 전역 객체를 가진다는 것을 의미합니다.
개체를 컨텍스트화하여 컨텍스트를 제공할 수 있습니다. 호출된 코드는 컨텍스트의 모든 속성을 전역 변수처럼 취급합니다. 호출된 코드에 의해 발생한 전역 변수의 모든 변경 사항은 컨텍스트 객체에 반영됩니다.
const vm = require('node:vm')
const x = 1
const context = { x: 2 }
vm.createContext(context) // 객체를 컨텍스트화합니다.
const code = 'x += 40; var y = 17;'
// `x`와 `y`는 컨텍스트에서 전역 변수입니다.
// 처음에는 x가 context.x의 값인 2를 가집니다.
vm.runInContext(code, context)
console.log(context.x) // 42
console.log(context.y) // 17
console.log(x) // 1; y는 정의되지 않았습니다.
클래스: vm.Script
추가된 버전: v0.3.1
vm.Script
클래스의 인스턴스에는 특정 컨텍스트에서 실행할 수 있는 미리 컴파일된 스크립트가 포함되어 있습니다.
new vm.Script(code[, options])
[기록]
버전 | 변경 사항 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 지원이 추가되었습니다. |
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 대한 가져오기 속성 지원이 추가되었습니다. |
v10.6.0 | produceCachedData 가 script.createCachedData() 로 사용 중단되었습니다. |
v5.7.0 | cachedData 및 produceCachedData 옵션이 이제 지원됩니다. |
v0.3.1 | 추가된 버전: v0.3.1 |
code
<string> 컴파일할 JavaScript 코드입니다.options
<Object> | <string>filename
<string> 이 스크립트에서 생성된 스택 추적에서 사용되는 파일 이름을 지정합니다. 기본값:'evalmachine.\<anonymous\>'
.lineOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 줄 번호 오프셋을 지정합니다. 기본값:0
.columnOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 첫 번째 줄 열 번호 오프셋을 지정합니다. 기본값:0
.cachedData
<Buffer> | <TypedArray> | <DataView> 제공된 소스에 대한 V8의 코드 캐시 데이터가 있는 선택적Buffer
또는TypedArray
또는DataView
를 제공합니다. 제공된 경우cachedDataRejected
값은 V8에서 데이터의 수락 여부에 따라true
또는false
로 설정됩니다.produceCachedData
<boolean>true
이고cachedData
가 없는 경우 V8은code
에 대한 코드 캐시 데이터를 생성하려고 시도합니다. 성공하면 V8의 코드 캐시 데이터가 있는Buffer
가 생성되어 반환된vm.Script
인스턴스의cachedData
속성에 저장됩니다.cachedDataProduced
값은 코드 캐시 데이터가 성공적으로 생성되었는지 여부에 따라true
또는false
로 설정됩니다. 이 옵션은script.createCachedData()
를 사용하는 것이 사용 중단되었습니다. 기본값:false
.importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
가 호출될 때 이 스크립트의 평가 중에 모듈을 로드하는 방법을 지정하는 데 사용됩니다. 이 옵션은 실험적인 모듈 API의 일부입니다. 프로덕션 환경에서 사용하는 것을 권장하지 않습니다. 자세한 내용은 컴파일 API에서 동적import()
지원을 참조하세요.
options
가 문자열인 경우 파일 이름을 지정합니다.
새로운 vm.Script
객체를 만들면 code
가 컴파일되지만 실행되지는 않습니다. 컴파일된 vm.Script
는 나중에 여러 번 실행할 수 있습니다. code
는 어떤 전역 객체에도 바인딩되지 않습니다. 오히려 실행 직전에 각 실행에 대해서만 바인딩됩니다.
script.cachedDataRejected
Added in: v5.7.0
cachedData
가 vm.Script
를 생성하기 위해 제공되었을 때, 이 값은 V8에 의해 데이터 수락 여부에 따라 true
또는 false
로 설정됩니다. 그렇지 않으면 값은 undefined
입니다.
script.createCachedData()
Added in: v10.6.0
- 반환 값: <Buffer>
Script
생성자의 cachedData
옵션과 함께 사용할 수 있는 코드 캐시를 생성합니다. Buffer
를 반환합니다. 이 메서드는 언제든지 여러 번 호출할 수 있습니다.
Script
의 코드 캐시에는 JavaScript에서 관찰 가능한 상태가 포함되어 있지 않습니다. 코드 캐시는 스크립트 소스와 함께 안전하게 저장할 수 있으며 새로운 Script
인스턴스를 여러 번 구성하는 데 사용할 수 있습니다.
Script
소스의 함수는 지연 컴파일로 표시될 수 있으며 Script
생성 시 컴파일되지 않습니다. 이러한 함수는 처음 호출될 때 컴파일됩니다. 코드 캐시는 V8이 현재 Script
에 대해 알고 있는 메타데이터를 직렬화하여 향후 컴파일 속도를 높일 수 있습니다.
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`)
const cacheWithoutAdd = script.createCachedData()
// `cacheWithoutAdd`에서 함수 `add()`는 호출 시 전체 컴파일을 위해 표시됩니다.
script.runInThisContext()
const cacheWithAdd = script.createCachedData()
// `cacheWithAdd`에는 완전히 컴파일된 함수 `add()`가 포함되어 있습니다.
script.runInContext(contextifiedObject[, options])
[History]
버전 | 변경 사항 |
---|---|
v6.3.0 | breakOnSigint 옵션이 이제 지원됩니다. |
v0.3.1 | 추가됨: v0.3.1 |
contextifiedObject
<Object>vm.createContext()
메서드에서 반환된 컨텍스트화된 객체입니다.options
<Object>displayErrors
<boolean>true
이면code
를 컴파일하는 동안Error
가 발생할 경우 오류를 유발한 코드 줄이 스택 추적에 첨부됩니다. 기본값:true
.timeout
<integer> 실행을 종료하기 전에code
를 실행할 밀리초 수를 지정합니다. 실행이 종료되면Error
가 throw됩니다. 이 값은 엄격한 양의 정수여야 합니다.breakOnSigint
<boolean>true
이면SIGINT
(+)를 수신하면 실행이 종료되고Error
가 throw됩니다.process.on('SIGINT')
를 통해 첨부된 이벤트에 대한 기존 핸들러는 스크립트 실행 중에는 비활성화되지만 그 후에는 계속 작동합니다. 기본값:false
.
반환 값: <any> 스크립트에서 실행된 가장 마지막 문의 결과입니다.
주어진 contextifiedObject
내에서 vm.Script
객체에 포함된 컴파일된 코드를 실행하고 결과를 반환합니다. 코드를 실행하면 로컬 범위에 액세스할 수 없습니다.
다음 예제는 전역 변수를 증가시키는 코드를 컴파일하고, 다른 전역 변수의 값을 설정한 다음, 코드를 여러 번 실행합니다. 전역 변수는 context
객체에 포함되어 있습니다.
const vm = require('node:vm')
const context = {
animal: 'cat',
count: 2,
}
const script = new vm.Script('count += 1; name = "kitty";')
vm.createContext(context)
for (let i = 0; i < 10; ++i) {
script.runInContext(context)
}
console.log(context)
// 출력: { animal: 'cat', count: 12, name: 'kitty' }
timeout
또는 breakOnSigint
옵션을 사용하면 새로운 이벤트 루프와 해당 스레드가 시작되어 성능 오버헤드가 발생합니다.
script.runInNewContext([contextObject[, options]])
[History]
Version | Changes |
---|---|
v22.8.0, v20.18.0 | 이제 contextObject 인수가 vm.constants.DONT_CONTEXTIFY 를 허용합니다. |
v14.6.0 | 이제 microtaskMode 옵션이 지원됩니다. |
v10.0.0 | 이제 contextCodeGeneration 옵션이 지원됩니다. |
v6.3.0 | 이제 breakOnSigint 옵션이 지원됩니다. |
v0.3.1 | v0.3.1에서 추가됨 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
또는 컨텍스트화될 객체입니다.undefined
이면 이전 버전과의 호환성을 위해 빈 컨텍스트화된 객체가 생성됩니다.options
<Object>displayErrors
<boolean>true
인 경우,code
를 컴파일하는 동안Error
가 발생하면 오류를 발생시킨 코드 줄이 스택 추적에 첨부됩니다. 기본값:true
.timeout
<integer> 실행을 종료하기 전에code
를 실행할 밀리초 수를 지정합니다. 실행이 종료되면Error
가 발생합니다. 이 값은 엄격하게 양의 정수여야 합니다.breakOnSigint
<boolean>true
이면SIGINT
(+)를 수신하면 실행이 종료되고Error
가 발생합니다.process.on('SIGINT')
를 통해 첨부된 이벤트에 대한 기존 핸들러는 스크립트 실행 중에 비활성화되지만 그 후에도 계속 작동합니다. 기본값:false
.contextName
<string> 새로 생성된 컨텍스트의 사람이 읽을 수 있는 이름입니다. 기본값:'VM 컨텍스트 i'
, 여기서i
는 생성된 컨텍스트의 오름차순 숫자 인덱스입니다.contextOrigin
<string> 표시를 위해 새로 생성된 컨텍스트에 해당하는 Origin입니다. 원점은 URL처럼 포맷되어야 하지만,URL
객체의url.origin
속성 값과 마찬가지로 스킴, 호스트, 포트(필요한 경우)만 포함해야 합니다. 특히 이 문자열은 경로를 나타내는 후행 슬래시를 생략해야 합니다. 기본값:''
.contextCodeGeneration
<Object>strings
<boolean> false로 설정하면eval
또는 함수 생성자(Function
,GeneratorFunction
등)에 대한 모든 호출이EvalError
를 발생시킵니다. 기본값:true
.wasm
<boolean> false로 설정하면 WebAssembly 모듈을 컴파일하려는 모든 시도가WebAssembly.CompileError
를 발생시킵니다. 기본값:true
.microtaskMode
<string>afterEvaluate
로 설정하면 마이크로태스크(Promise
및async function
을 통해 예약된 작업)가 스크립트 실행 직후에 실행됩니다. 이 경우timeout
및breakOnSigint
범위에 포함됩니다.
반환값: <any> 스크립트에서 실행된 마지막 문의 결과입니다.
이 메서드는 script.runInContext(vm.createContext(options), options)
에 대한 바로 가기입니다. 여러 가지 작업을 한 번에 수행합니다.
다음 예제에서는 전역 변수를 설정하는 코드를 컴파일한 다음 서로 다른 컨텍스트에서 여러 번 코드를 실행합니다. 전역 변수는 각 개별 context
에 설정되고 포함됩니다.
const vm = require('node:vm')
const script = new vm.Script('globalVar = "set"')
const contexts = [{}, {}, {}]
contexts.forEach(context => {
script.runInNewContext(context)
})
console.log(contexts)
// 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.0 | breakOnSigint 옵션이 이제 지원됩니다. |
v0.3.1 | v0.3.1에 추가됨 |
options
<Object>displayErrors
<boolean>true
일 경우,code
를 컴파일하는 동안Error
가 발생하면 오류를 일으킨 코드 줄이 스택 추적에 첨부됩니다. 기본값:true
.timeout
<integer> 실행을 종료하기 전에code
를 실행할 밀리초 수를 지정합니다. 실행이 종료되면Error
가 throw됩니다. 이 값은 엄격하게 양의 정수여야 합니다.breakOnSigint
<boolean>true
인 경우,SIGINT
(+) 수신은 실행을 종료하고Error
를 throw합니다.process.on('SIGINT')
를 통해 첨부된 이벤트에 대한 기존 핸들러는 스크립트 실행 중에는 비활성화되지만 그 이후에는 계속 작동합니다. 기본값:false
.
- 반환값: <any> 스크립트에서 실행된 마지막 문의 결과입니다.
현재 global
객체 컨텍스트 내에서 vm.Script
에 포함된 컴파일된 코드를 실행합니다. 코드를 실행하면 로컬 범위에 액세스할 수 없지만 현재 global
객체에는 액세스할 수 있습니다.
다음 예시는 global
변수를 증가시키는 코드를 컴파일한 다음 해당 코드를 여러 번 실행합니다.
const vm = require('node:vm')
global.globalVar = 0
const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' })
for (let i = 0; i < 1000; ++i) {
script.runInThisContext()
}
console.log(globalVar)
// 1000
script.sourceMapURL
추가된 버전: v19.1.0, v18.13.0
스크립트가 소스 맵 매직 주석을 포함하는 소스에서 컴파일된 경우, 이 속성은 소스 맵의 URL로 설정됩니다.
import vm from 'node:vm'
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`)
console.log(script.sourceMapURL)
// Prints: sourcemap.json
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
이 기능은 --experimental-vm-modules
명령 플래그를 활성화해야만 사용할 수 있습니다.
vm.Module
클래스는 VM 컨텍스트에서 ECMAScript 모듈을 사용하기 위한 저수준 인터페이스를 제공합니다. 이는 ECMAScript 사양에 정의된 모듈 레코드를 밀접하게 반영하는 vm.Script
클래스의 상대 클래스입니다.
하지만 vm.Script
와 달리 모든 vm.Module
객체는 생성 시점에서 컨텍스트에 바인딩됩니다. vm.Module
객체에 대한 연산은 vm.Script
객체의 동기적 특성과는 대조적으로 본질적으로 비동기적입니다. 'async' 함수를 사용하면 vm.Module
객체를 조작하는 데 도움이 될 수 있습니다.
vm.Module
객체를 사용하려면 생성/파싱, 연결 및 평가라는 세 가지 개별 단계가 필요합니다. 이 세 가지 단계는 다음 예에 나와 있습니다.
이 구현은 ECMAScript 모듈 로더보다 낮은 수준에 있습니다. 아직 로더와 상호 작용할 방법도 없지만 지원이 계획되어 있습니다.
import vm from 'node:vm'
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
// 1단계
//
// 새 `vm.SourceTextModule` 객체를 생성하여 모듈을 만듭니다. 이렇게 하면 제공된 소스 텍스트를 파싱하여 오류가 발생하면 `SyntaxError`를 throw합니다. 기본적으로 모듈은 최상위 컨텍스트에서 생성됩니다. 하지만 여기에서는 이 모듈이 속한 컨텍스트로 `contextifiedObject`를 지정합니다.
//
// 여기서는 "foo" 모듈에서 기본 내보내기를 가져와서 로컬 바인딩 "secret"에 넣으려고 시도합니다.
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// 2단계
//
// 이 모듈의 가져온 종속성을 "연결"합니다.
//
// 제공된 연결 콜백("링커")은 두 개의 인수(이 경우 부모 모듈인 `bar`와 가져온 모듈의 지정자인 문자열)를 허용합니다. 콜백은 `module.link()`에 문서화된 특정 요구 사항과 함께 제공된 지정자에 해당하는 모듈을 반환해야 합니다.
//
// 반환된 모듈에 연결이 시작되지 않은 경우 반환된 모듈에서 동일한 링커 콜백이 호출됩니다.
//
// 종속성이 없는 최상위 모듈도 명시적으로 연결해야 합니다. 그러나 제공된 콜백은 호출되지 않습니다.
//
// link() 메서드는 링커에서 반환한 모든 프로미스가 해결될 때 해결될 프로미스를 반환합니다.
//
// 참고: 이것은 링커 함수가 호출될 때마다 새 "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() 메서드는 모듈이 평가를 완료한 후 해결될 프로미스를 반환합니다.
// 42를 출력합니다.
await bar.evaluate()
const vm = require('node:vm')
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
})
;(async () => {
// 1단계
//
// 새 `vm.SourceTextModule` 객체를 생성하여 모듈을 만듭니다. 이렇게 하면 제공된 소스 텍스트를 파싱하여 오류가 발생하면 `SyntaxError`를 throw합니다. 기본적으로 모듈은 최상위 컨텍스트에서 생성됩니다. 하지만 여기에서는 이 모듈이 속한 컨텍스트로 `contextifiedObject`를 지정합니다.
//
// 여기서는 "foo" 모듈에서 기본 내보내기를 가져와서 로컬 바인딩 "secret"에 넣으려고 시도합니다.
const bar = new vm.SourceTextModule(
`
import s from 'foo';
s;
print(s);
`,
{ context: contextifiedObject }
)
// 2단계
//
// 이 모듈의 가져온 종속성을 "연결"합니다.
//
// 제공된 연결 콜백("링커")은 두 개의 인수(이 경우 부모 모듈인 `bar`와 가져온 모듈의 지정자인 문자열)를 허용합니다. 콜백은 `module.link()`에 문서화된 특정 요구 사항과 함께 제공된 지정자에 해당하는 모듈을 반환해야 합니다.
//
// 반환된 모듈에 연결이 시작되지 않은 경우 반환된 모듈에서 동일한 링커 콜백이 호출됩니다.
//
// 종속성이 없는 최상위 모듈도 명시적으로 연결해야 합니다. 그러나 제공된 콜백은 호출되지 않습니다.
//
// link() 메서드는 링커에서 반환한 모든 프로미스가 해결될 때 해결될 프로미스를 반환합니다.
//
// 참고: 이것은 링커 함수가 호출될 때마다 새 "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() 메서드는 모듈이 평가를 완료한 후 해결될 프로미스를 반환합니다.
// 42를 출력합니다.
await bar.evaluate()
})()
module.dependencySpecifiers
이 모듈의 모든 종속성의 지정자입니다. 반환된 배열은 변경을 허용하지 않도록 고정됩니다.
ECMAScript 사양의 순환 모듈 레코드의 [[RequestedModules]]
필드에 해당합니다.
module.error
module.status
가 'errored'
인 경우 이 속성에는 모듈 평가 중에 발생한 예외가 포함됩니다. 상태가 다른 경우에는 이 속성에 접근하면 예외가 발생합니다.
throw undefined;
와의 모호성 가능성으로 인해 예외가 발생하지 않은 경우 undefined
값을 사용할 수 없습니다.
ECMAScript 사양의 순환 모듈 레코드의 [[EvaluationError]]
필드에 해당합니다.
module.evaluate([options])
모듈을 평가합니다.
모듈이 연결된 후에 호출해야 합니다. 그렇지 않으면 거부됩니다. 모듈이 이미 평가된 경우에도 호출할 수 있습니다. 이 경우 초기 평가가 성공적으로 종료된 경우 아무 것도 수행하지 않거나(module.status
가 'evaluated'
) 초기 평가로 인해 발생한 예외를 다시 throw합니다(module.status
가 'errored'
).
모듈을 평가하는 동안에는 이 메서드를 호출할 수 없습니다(module.status
가 'evaluating'
).
ECMAScript 사양의 순환 모듈 레코드의 Evaluate() 구체 메서드 필드에 해당합니다.
module.identifier
생성자에서 설정한 현재 모듈의 식별자입니다.
module.link(linker)
[기록]
버전 | 변경 사항 |
---|---|
v21.1.0, v20.10.0, v18.19.0 | extra.assert 옵션의 이름이 extra.attributes 로 변경되었습니다. 이전 이름은 이전 버전과의 호환성을 위해 계속 제공됩니다. |
linker
<Function>specifier
<string> 요청된 모듈의 지정자입니다:referencingModule
<vm.Module>link()
가 호출된Module
객체입니다.extra
<Object>attributes
<Object> 속성의 데이터입니다: ECMA-262에 따라 호스트는 지원되지 않는 속성이 있으면 오류를 트리거해야 합니다.assert
<Object>extra.attributes
의 별칭입니다.반환값: <vm.Module> | <Promise>
반환값: <Promise>
모듈 종속성을 연결합니다. 이 메서드는 평가 전에 호출해야 하며 모듈당 한 번만 호출할 수 있습니다.
이 함수는 Module
객체 또는 Module
객체로 귀결되는 Promise
를 반환해야 합니다. 반환된 Module
은 다음 두 가지 불변성을 충족해야 합니다.
- 부모
Module
과 동일한 컨텍스트에 속해야 합니다. status
는'errored'
가 아니어야 합니다.
반환된 Module
의 status
가 'unlinked'
인 경우 이 메서드는 제공된 동일한 linker
함수를 사용하여 반환된 Module
에서 재귀적으로 호출됩니다.
link()
는 모든 연결 인스턴스가 유효한 Module
로 귀결될 때 해결되거나, 링커 함수가 예외를 던지거나 유효하지 않은 Module
을 반환하는 경우 거부되는 Promise
를 반환합니다.
링커 함수는 ECMAScript 사양의 구현 정의 HostResolveImportedModule 추상 연산과 거의 일치하지만 몇 가지 주요 차이점이 있습니다.
- 링커 함수는 비동기적일 수 있지만 HostResolveImportedModule은 동기적입니다.
모듈 연결 중에 사용되는 실제 HostResolveImportedModule 구현은 연결 중에 연결된 모듈을 반환하는 것입니다. 이 시점에서 모든 모듈은 이미 완전히 연결되었으므로 HostResolveImportedModule 구현은 사양에 따라 완전히 동기적입니다.
ECMAScript 사양의 순환 모듈 레코드의 Link() 구체적인 메서드 필드에 해당합니다.
module.namespace
모듈의 네임스페이스 객체입니다. 이는 링크(module.link()
)가 완료된 후에만 사용할 수 있습니다.
ECMAScript 사양의 GetModuleNamespace 추상 연산에 해당합니다.
module.status
모듈의 현재 상태입니다. 다음 중 하나가 됩니다.
'unlinked'
:module.link()
가 아직 호출되지 않았습니다.'linking'
:module.link()
가 호출되었지만, 링커 함수에서 반환된 모든 Promise가 아직 확인되지 않았습니다.'linked'
: 모듈이 성공적으로 링크되었고, 모든 종속성이 링크되었지만,module.evaluate()
가 아직 호출되지 않았습니다.'evaluating'
: 모듈 자체 또는 부모 모듈에서module.evaluate()
를 통해 모듈을 평가 중입니다.'evaluated'
: 모듈이 성공적으로 평가되었습니다.'errored'
: 모듈이 평가되었지만, 예외가 발생했습니다.
'errored'
를 제외하고, 이 상태 문자열은 사양의 Cyclic Module Record의 [[Status]]
필드에 해당합니다. 'errored'
는 사양에서 'evaluated'
에 해당하지만, [[EvaluationError]]
가 undefined
가 아닌 값으로 설정되어 있습니다.
클래스: vm.SourceTextModule
v9.6.0에 추가됨
이 기능은 --experimental-vm-modules
명령 플래그가 활성화된 경우에만 사용할 수 있습니다.
- 확장: <vm.Module>
vm.SourceTextModule
클래스는 ECMAScript 사양에 정의된 Source Text Module Record를 제공합니다.
new vm.SourceTextModule(code[, options])
[기록]
버전 | 변경 사항 |
---|---|
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 대한 import 속성 지원이 추가되었습니다. |
code
<string> 구문 분석할 JavaScript 모듈 코드options
identifier
<string> 스택 추적에 사용되는 문자열입니다. 기본값:'vm:module(i)'
여기서i
는 컨텍스트 관련 오름차순 인덱스입니다.cachedData
<Buffer> | <TypedArray> | <DataView> 제공된 소스에 대한 V8의 코드 캐시 데이터가 포함된 선택적Buffer
또는TypedArray
또는DataView
를 제공합니다.code
는 이cachedData
가 생성된 모듈과 동일해야 합니다.context
<Object> 이Module
을 컴파일하고 평가할vm.createContext()
메서드에서 반환된 컨텍스트화된 객체입니다. 컨텍스트가 지정되지 않은 경우 모듈은 현재 실행 컨텍스트에서 평가됩니다.lineOffset
<integer> 이Module
에서 생성된 스택 추적에 표시되는 줄 번호 오프셋을 지정합니다. 기본값:0
.columnOffset
<integer> 이Module
에서 생성된 스택 추적에 표시되는 첫 번째 줄 열 번호 오프셋을 지정합니다. 기본값:0
.initializeImportMeta
<Function> 이Module
을 평가하는 동안import.meta
를 초기화하기 위해 호출됩니다.meta
<import.meta>module
<vm.SourceTextModule>importModuleDynamically
<Function>import()
가 호출될 때 이 모듈을 평가하는 동안 모듈을 로드하는 방법을 지정하는 데 사용됩니다. 이 옵션은 실험적 모듈 API의 일부입니다. 프로덕션 환경에서 사용하지 않는 것이 좋습니다. 자세한 내용은 컴파일 API에서 동적import()
지원을 참조하십시오.
새로운 SourceTextModule
인스턴스를 만듭니다.
객체인 import.meta
객체에 할당된 속성을 통해 모듈은 지정된 context
외부의 정보에 액세스할 수 있습니다. 특정 컨텍스트에서 객체를 만들려면 vm.runInContext()
를 사용하십시오.
import vm from 'node:vm'
const contextifiedObject = vm.createContext({ secret: 42 })
const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
initializeImportMeta(meta) {
// 참고: 이 객체는 최상위 컨텍스트에서 생성됩니다. 따라서
// Object.getPrototypeOf(import.meta.prop)는 컨텍스트화된 객체의
// Object.prototype가 아니라 최상위 컨텍스트의 Object.prototype를 가리킵니다.
meta.prop = {}
},
})
// 모듈에는 종속성이 없으므로 링커 함수는 절대 호출되지 않습니다.
await module.link(() => {})
await module.evaluate()
// 이제 Object.prototype.secret는 42와 같습니다.
//
// 이 문제를 해결하려면
// meta.prop = {};
// 위를 다음과 같이 대체하십시오.
// meta.prop = vm.runInContext('{}', contextifiedObject);
const vm = require('node:vm')
const contextifiedObject = vm.createContext({ secret: 42 })
;(async () => {
const module = new vm.SourceTextModule('Object.getPrototypeOf(import.meta.prop).secret = secret;', {
initializeImportMeta(meta) {
// 참고: 이 객체는 최상위 컨텍스트에서 생성됩니다. 따라서
// Object.getPrototypeOf(import.meta.prop)는 컨텍스트화된 객체의
// Object.prototype가 아니라 최상위 컨텍스트의 Object.prototype를 가리킵니다.
meta.prop = {}
},
})
// 모듈에는 종속성이 없으므로 링커 함수는 절대 호출되지 않습니다.
await module.link(() => {})
await module.evaluate()
// 이제 Object.prototype.secret는 42와 같습니다.
//
// 이 문제를 해결하려면
// meta.prop = {};
// 위를 다음과 같이 대체하십시오.
// meta.prop = vm.runInContext('{}', contextifiedObject);
})()
sourceTextModule.createCachedData()
추가된 버전: v13.7.0, v12.17.0
- 반환값: <Buffer>
SourceTextModule
생성자의 cachedData
옵션과 함께 사용할 수 있는 코드 캐시를 만듭니다. Buffer
를 반환합니다. 이 메서드는 모듈이 평가되기 전까지 여러 번 호출할 수 있습니다.
SourceTextModule
의 코드 캐시는 JavaScript에서 관찰 가능한 상태를 포함하지 않습니다. 코드 캐시는 스크립트 소스와 함께 저장하고 여러 번 새로운 SourceTextModule
인스턴스를 생성하는 데 안전하게 사용할 수 있습니다.
SourceTextModule
소스의 함수는 지연 컴파일로 표시될 수 있으며 SourceTextModule
생성 시 컴파일되지 않습니다. 이러한 함수는 처음 호출될 때 컴파일됩니다. 코드 캐시는 V8이 현재 SourceTextModule
에 대해 알고 있는 메타데이터를 직렬화하여 향후 컴파일 속도를 높이는 데 사용할 수 있습니다.
// 초기 모듈 생성
const module = new vm.SourceTextModule('const a = 1;')
// 이 모듈에서 캐시된 데이터 생성
const cachedData = module.createCachedData()
// 캐시된 데이터를 사용하여 새 모듈 생성. 코드는 동일해야 함.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData })
클래스: vm.SyntheticModule
추가된 버전: v13.0.0, v12.16.0
이 기능은 --experimental-vm-modules
명령 플래그가 활성화된 경우에만 사용할 수 있습니다.
- 확장: <vm.Module>
vm.SyntheticModule
클래스는 WebIDL 사양에 정의된 합성 모듈 레코드를 제공합니다. 합성 모듈의 목적은 비 JavaScript 소스를 ECMAScript 모듈 그래프에 노출하기 위한 일반 인터페이스를 제공하는 것입니다.
const vm = require('node:vm')
const source = '{ "a": 1 }'
const module = new vm.SyntheticModule(['default'], function () {
const obj = JSON.parse(source)
this.setExport('default', obj)
})
// 링크에 `module` 사용...
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
추가된 버전: v13.0.0, v12.16.0
exportNames
<string[]> 모듈에서 내보낼 이름들의 배열입니다.evaluateCallback
<Function> 모듈이 평가될 때 호출됩니다.options
새로운 SyntheticModule
인스턴스를 생성합니다.
이 인스턴스의 내보내기에 할당된 객체는 모듈의 임포터가 지정된 context
외부의 정보에 접근할 수 있도록 할 수 있습니다. 특정 컨텍스트에서 객체를 생성하려면 vm.runInContext()
를 사용하세요.
syntheticModule.setExport(name, value)
추가된 버전: v13.0.0, v12.16.0
이 메서드는 모듈이 링크된 후에 내보내기 값을 설정하는 데 사용됩니다. 모듈이 링크되기 전에 호출되면 ERR_VM_MODULE_STATUS
오류가 발생합니다.
import vm from 'node:vm'
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1)
})
await m.link(() => {})
await m.evaluate()
assert.strictEqual(m.namespace.x, 1)
const vm = require('node:vm')
;(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1)
})
await m.link(() => {})
await m.evaluate()
assert.strictEqual(m.namespace.x, 1)
})()
vm.compileFunction(code[, params[, options]])
[History]
버전 | 변경 사항 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 지원 추가. |
v19.6.0, v18.15.0 | cachedData 옵션이 전달된 경우 반환 값에 vm.Script 버전과 동일한 의미의 cachedDataRejected 가 포함됩니다. |
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 import 속성 지원 추가. |
v15.9.0 | importModuleDynamically 옵션 다시 추가. |
v14.3.0 | 호환성 문제로 importModuleDynamically 제거. |
v14.1.0, v13.14.0 | importModuleDynamically 옵션이 이제 지원됩니다. |
v10.10.0 | v10.10.0에 추가됨 |
code
<string> 컴파일할 함수의 본문입니다.params
<string[]> 함수의 모든 매개변수를 포함하는 문자열 배열입니다.options
<Object>filename
<string> 이 스크립트에서 생성된 스택 추적에서 사용되는 파일 이름을 지정합니다. 기본값:''
.lineOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 줄 번호 오프셋을 지정합니다. 기본값:0
.columnOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 첫 번째 줄의 열 번호 오프셋을 지정합니다. 기본값:0
.cachedData
<Buffer> | <TypedArray> | <DataView> 제공된 소스에 대한 V8의 코드 캐시 데이터가 포함된 선택적Buffer
또는TypedArray
또는DataView
를 제공합니다. 이것은 동일한code
및params
로vm.compileFunction()
에 대한 이전 호출에 의해 생성되어야 합니다.produceCachedData
<boolean> 새 캐시 데이터를 생성할지 여부를 지정합니다. 기본값:false
.parsingContext
<Object> 해당 함수를 컴파일해야 하는 컨텍스트화된 객체입니다.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
Node.js가 요청된 모듈을 로드하기 위해 메인 컨텍스트에서 기본 ESM 로더를 사용하도록 vm.Script
및 vm.compileFunction()
에 대한 importModuleDynamically
옵션으로 사용할 수 있는 상수입니다.
자세한 내용은 컴파일 API에서 동적 import()
지원을 참조하세요.
vm.createContext([contextObject[, options]])
[기록]
버전 | 변경 사항 |
---|---|
v22.8.0, v20.18.0 | contextObject 인수가 이제 vm.constants.DONT_CONTEXTIFY 를 허용합니다. |
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 에 대한 지원이 추가되었습니다. |
v21.2.0, v20.11.0 | 이제 importModuleDynamically 옵션이 지원됩니다. |
v14.6.0 | 이제 microtaskMode 옵션이 지원됩니다. |
v10.0.0 | 첫 번째 인수는 더 이상 함수일 수 없습니다. |
v10.0.0 | 이제 codeGeneration 옵션이 지원됩니다. |
v0.3.1 | 추가됨: v0.3.1 |
contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
또는 컨텍스트화될 객체입니다.undefined
인 경우 이전 버전과의 호환성을 위해 빈 컨텍스트화된 객체가 생성됩니다.options
<Object>name
<string> 새로 생성된 컨텍스트의 사람이 읽을 수 있는 이름입니다. 기본값:'VM 컨텍스트 i'
, 여기서i
는 생성된 컨텍스트의 오름차순 숫자 인덱스입니다.origin
<string> 표시 목적으로 새로 생성된 컨텍스트에 해당하는 출처입니다. 출처는 URL처럼 형식이 지정되어야 하지만,URL
객체의url.origin
속성 값처럼 스킴, 호스트, 포트(필요한 경우)만 포함해야 합니다. 특히 이 문자열은 경로를 나타내는 후행 슬래시를 생략해야 합니다. 기본값:''
.codeGeneration
<Object>strings
<boolean> false로 설정하면eval
또는 함수 생성자(Function
,GeneratorFunction
등)에 대한 호출이EvalError
를 발생시킵니다. 기본값:true
.wasm
<boolean> false로 설정하면 WebAssembly 모듈을 컴파일하려는 모든 시도가WebAssembly.CompileError
를 발생시킵니다. 기본값:true
.microtaskMode
<string>afterEvaluate
로 설정하면 마이크로태스크(Promise
및async function
을 통해 예약된 작업)는 스크립트가script.runInContext()
를 통해 실행된 직후에 실행됩니다. 이 경우 해당 태스크는timeout
및breakOnSigint
범위에 포함됩니다.importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> 리퍼러 스크립트 또는 모듈 없이 이 컨텍스트에서import()
가 호출될 때 모듈을 로드하는 방법을 지정하는 데 사용됩니다. 이 옵션은 실험적 모듈 API의 일부입니다. 프로덕션 환경에서 사용하는 것은 권장하지 않습니다. 자세한 내용은 컴파일 API에서 동적import()
지원을 참조하세요.
- 반환값: <Object> 컨텍스트화된 객체입니다.
지정된 contextObject
가 객체인 경우, vm.createContext()
메서드는 해당 객체를 준비하고 vm.runInContext()
또는 script.runInContext()
호출에서 사용할 수 있도록 해당 객체에 대한 참조를 반환합니다. 이러한 스크립트 내부에서 전역 객체는 contextObject
로 래핑되어 모든 기존 속성을 유지하지만 표준 전역 객체에 있는 내장 객체와 함수도 갖게 됩니다. vm 모듈에서 실행되는 스크립트 외부에서는 전역 변수가 변경되지 않은 상태로 유지됩니다.
const vm = require('node:vm')
global.globalVar = 3
const context = { globalVar: 1 }
vm.createContext(context)
vm.runInContext('globalVar *= 2;', context)
console.log(context)
// 출력: { globalVar: 2 }
console.log(global.globalVar)
// 출력: 3
contextObject
가 생략되거나 명시적으로 undefined
로 전달되면 비어 있는 새 컨텍스트화된 객체가 반환됩니다.
새로 생성된 컨텍스트의 전역 객체가 컨텍스트화되면 일반 전역 객체와 비교하여 몇 가지 특이점이 있습니다. 예를 들어, 고정할 수 없습니다. 컨텍스트화 특성이 없는 컨텍스트를 만들려면 contextObject
인수로 vm.constants.DONT_CONTEXTIFY
를 전달합니다. 자세한 내용은 vm.constants.DONT_CONTEXTIFY
문서를 참조하세요.
vm.createContext()
메서드는 주로 여러 스크립트를 실행하는 데 사용할 수 있는 단일 컨텍스트를 만드는 데 유용합니다. 예를 들어 웹 브라우저를 에뮬레이션하는 경우 이 메서드를 사용하여 창의 전역 객체를 나타내는 단일 컨텍스트를 만든 다음 해당 컨텍스트 내에서 모든 \<script\>
태그를 함께 실행할 수 있습니다.
컨텍스트의 제공된 name
과 origin
은 Inspector API를 통해 볼 수 있습니다.
vm.isContext(object)
추가된 버전: v0.11.7
주어진 object
객체가 vm.createContext()
를 사용하여 컨텍스트화되었거나, vm.constants.DONT_CONTEXTIFY
를 사용하여 생성된 컨텍스트의 전역 객체인 경우 true
를 반환합니다.
vm.measureMemory([options])
추가된 버전: v13.10.0
V8이 알고 있으며 현재 V8 격리 또는 주 컨텍스트에서 알고 있는 모든 컨텍스트에서 사용되는 메모리를 측정합니다.
options
<Object> 선택 사항입니다.mode
<string>'summary'
또는'detailed'
중 하나입니다. 요약 모드에서는 주 컨텍스트에 대해 측정된 메모리만 반환됩니다. 상세 모드에서는 현재 V8 격리에서 알고 있는 모든 컨텍스트에 대해 측정된 메모리가 반환됩니다. 기본값:'summary'
execution
<string>'default'
또는'eager'
중 하나입니다. 기본 실행에서는 다음 예약된 가비지 수집이 시작된 후까지 (다음 GC 전에 프로그램이 종료되면 시간이 오래 걸리거나 절대 해결되지 않을 수 있음) 프로미스가 해결되지 않습니다. 즉시 실행을 사용하면 메모리를 측정하기 위해 GC가 즉시 시작됩니다. 기본값:'default'
반환값: <Promise> 메모리 측정이 성공하면 프로미스는 메모리 사용량에 대한 정보가 포함된 객체로 해결됩니다. 그렇지 않으면
ERR_CONTEXT_NOT_INITIALIZED
오류와 함께 거부됩니다.
반환된 프로미스가 해결될 수 있는 객체의 형식은 V8 엔진에 따라 다르며 V8 버전마다 변경될 수 있습니다.
반환된 결과는 v8.getHeapSpaceStatistics()
에서 반환된 통계와는 다릅니다. vm.measureMemory()
는 현재 V8 엔진 인스턴스에서 각 V8 특정 컨텍스트에서 접근할 수 있는 메모리를 측정하는 반면, v8.getHeapSpaceStatistics()
의 결과는 현재 V8 인스턴스에서 각 힙 공간이 차지하는 메모리를 측정합니다.
const vm = require('node:vm')
// 주 컨텍스트에서 사용되는 메모리를 측정합니다.
vm.measureMemory({ mode: 'summary' })
// 이는 vm.measureMemory()와 같습니다.
.then(result => {
// 현재 형식은 다음과 같습니다.
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result)
})
const context = vm.createContext({ a: 1 })
vm.measureMemory({ mode: 'detailed', execution: 'eager' }).then(result => {
// 측정 완료될 때까지 GC되지 않도록 여기에서 컨텍스트를 참조합니다.
console.log(context.a)
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result)
})
vm.runInContext(code, contextifiedObject[, options])
[History]
버전 | 변경 사항 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 지원이 추가되었습니다. |
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 import 속성 지원이 추가되었습니다. |
v6.3.0 | breakOnSigint 옵션이 이제 지원됩니다. |
v0.3.1 | v0.3.1에서 추가되었습니다. |
code
<string> 컴파일 및 실행할 JavaScript 코드입니다.contextifiedObject
<Object>code
가 컴파일 및 실행될 때global
로 사용될 컨텍스트화된 객체입니다.options
<Object> | <string>filename
<string> 이 스크립트에서 생성된 스택 추적에 사용되는 파일 이름을 지정합니다. 기본값:'evalmachine.\<익명\>'
.lineOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 줄 번호 오프셋을 지정합니다. 기본값:0
.columnOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 첫 번째 줄 열 번호 오프셋을 지정합니다. 기본값:0
.displayErrors
<boolean>true
인 경우,code
를 컴파일하는 동안Error
가 발생하면 오류를 일으킨 코드 줄이 스택 추적에 첨부됩니다. 기본값:true
.timeout
<integer> 실행을 종료하기 전에code
를 실행할 밀리초 수를 지정합니다. 실행이 종료되면Error
가 throw됩니다. 이 값은 엄격하게 양의 정수여야 합니다.breakOnSigint
<boolean>true
인 경우,SIGINT
(+)를 받으면 실행이 종료되고Error
가 throw됩니다.process.on('SIGINT')
를 통해 첨부된 이벤트에 대한 기존 핸들러는 스크립트 실행 중에는 비활성화되지만 그 후에는 계속 작동합니다. 기본값:false
.cachedData
<Buffer> | <TypedArray> | <DataView> 제공된 소스에 대한 V8의 코드 캐시 데이터가 포함된 선택적Buffer
또는TypedArray
또는DataView
를 제공합니다.importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
가 호출될 때 이 스크립트 평가 중에 모듈을 로드해야 하는 방법을 지정하는 데 사용됩니다. 이 옵션은 실험적 모듈 API의 일부입니다. 프로덕션 환경에서는 사용하지 않는 것이 좋습니다. 자세한 내용은 컴파일 API에서 동적import()
지원을 참조하십시오.
vm.runInContext()
메서드는 code
를 컴파일하고 contextifiedObject
의 컨텍스트 내에서 실행한 다음 결과를 반환합니다. 실행 코드는 로컬 범위에 액세스할 수 없습니다. contextifiedObject
객체는 이전에 vm.createContext()
메서드를 사용하여 컨텍스트화되어 있어야 합니다.
options
가 문자열인 경우 파일 이름을 지정합니다.
다음 예제는 단일 컨텍스트화된 객체를 사용하여 다른 스크립트를 컴파일하고 실행합니다.
const vm = require('node:vm')
const contextObject = { globalVar: 1 }
vm.createContext(contextObject)
for (let i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', contextObject)
}
console.log(contextObject)
// 출력: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])
[History]
버전 | 변경 사항 |
---|---|
v22.8.0, v20.18.0 | contextObject 인수가 이제 vm.constants.DONT_CONTEXTIFY 를 허용합니다. |
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 지원이 추가되었습니다. |
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 import 속성 지원이 추가되었습니다. |
v14.6.0 | 이제 microtaskMode 옵션이 지원됩니다. |
v10.0.0 | 이제 contextCodeGeneration 옵션이 지원됩니다. |
v6.3.0 | 이제 breakOnSigint 옵션이 지원됩니다. |
v0.3.1 | v0.3.1에 추가되었습니다. |
code
<string> 컴파일 및 실행할 JavaScript 코드입니다.contextObject
<Object> | <vm.constants.DONT_CONTEXTIFY> | <undefined>vm.constants.DONT_CONTEXTIFY
또는 컨텍스트화될 객체입니다.undefined
인 경우 이전 버전과의 호환성을 위해 빈 컨텍스트화된 객체가 생성됩니다.options
<Object> | <string>filename
<string> 이 스크립트에서 생성된 스택 추적에 사용되는 파일 이름을 지정합니다. 기본값:'evalmachine.\<익명\>'
.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 컨텍스트 i'
, 여기서i
는 생성된 컨텍스트의 오름차순 숫자 인덱스입니다.contextOrigin
<string> 표시 목적으로 새로 생성된 컨텍스트에 해당하는 출처입니다. 출처는 URL처럼 형식이 지정되어야 하지만,URL
객체의url.origin
속성 값과 같이 스키마, 호스트 및 포트(필요한 경우)만 포함해야 합니다. 특히 이 문자열은 경로를 나타내는 후행 슬래시를 생략해야 합니다. 기본값:''
.contextCodeGeneration
<Object>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
에 포함됩니다.
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' }
// 컨텍스트화된 객체에서 컨텍스트가 생성되면 오류가 발생합니다.
// vm.constants.DONT_CONTEXTIFY를 사용하면 동결할 수 있는 일반적인 전역 객체를 사용하여 컨텍스트를 만들 수 있습니다.
const frozenContext = vm.runInNewContext('Object.freeze(globalThis); globalThis;', vm.constants.DONT_CONTEXTIFY)
vm.runInThisContext(code[, options])
[기록]
버전 | 변경 사항 |
---|---|
v21.7.0, v20.12.0 | vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER 에 대한 지원이 추가되었습니다. |
v17.0.0, v16.12.0 | importModuleDynamically 매개변수에 대한 import 속성 지원이 추가되었습니다. |
v6.3.0 | 이제 breakOnSigint 옵션이 지원됩니다. |
v0.3.1 | v0.3.1에 추가되었습니다. |
code
<string> 컴파일 및 실행할 JavaScript 코드입니다.filename
<string> 이 스크립트에서 생성된 스택 추적에 사용되는 파일 이름을 지정합니다. 기본값:'evalmachine.\<anonymous\>'
.lineOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 줄 번호 오프셋을 지정합니다. 기본값:0
.columnOffset
<number> 이 스크립트에서 생성된 스택 추적에 표시되는 첫 번째 줄 열 번호 오프셋을 지정합니다. 기본값:0
.displayErrors
<boolean>true
인 경우,code
를 컴파일하는 동안Error
가 발생하면 오류를 유발하는 코드 줄이 스택 추적에 첨부됩니다. 기본값:true
.timeout
<integer> 실행을 종료하기 전에code
를 실행할 밀리초 수를 지정합니다. 실행이 종료되면Error
가 발생합니다. 이 값은 엄격하게 양의 정수여야 합니다.breakOnSigint
<boolean>true
인 경우,SIGINT
(+)를 수신하면 실행이 종료되고Error
가 발생합니다. 스크립트 실행 중에process.on('SIGINT')
를 통해 첨부된 이벤트에 대한 기존 처리기는 비활성화되지만, 이후에는 계속 작동합니다. 기본값:false
.cachedData
<Buffer> | <TypedArray> | <DataView> 제공된 소스에 대한 V8의 코드 캐시 데이터가 포함된 선택적Buffer
또는TypedArray
또는DataView
를 제공합니다.importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
가 호출될 때 이 스크립트 평가 중에 모듈을 로드하는 방법을 지정하는 데 사용됩니다. 이 옵션은 실험적인 모듈 API의 일부입니다. 프로덕션 환경에서 사용하는 것은 권장하지 않습니다. 자세한 내용은 컴파일 API에서 동적import()
지원을 참조하십시오.
반환 값: <any> 스크립트에서 실행된 맨 마지막 문장의 결과입니다.
vm.runInThisContext()
는 code
를 컴파일하고 현재 global
컨텍스트 내에서 실행하며 결과를 반환합니다. 실행 코드는 로컬 범위에 접근할 수 없지만 현재 global
객체에 접근할 수 있습니다.
options
가 문자열이면 파일 이름을 지정합니다.
다음 예제에서는 vm.runInThisContext()
와 JavaScript eval()
함수를 모두 사용하여 동일한 코드를 실행하는 방법을 보여줍니다.
const vm = require('node:vm')
let localVar = 'initial value'
const vmResult = vm.runInThisContext('localVar = "vm";')
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`)
// 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
모듈을 사용하여 간단한 웹 서버를 실행하려면 컨텍스트로 전달된 코드가 자체적으로 require('node:http')
를 호출하거나 node:http
모듈에 대한 참조가 전달되어야 합니다. 예를 들어:
'use strict'
const vm = require('node:vm')
const code = `
((require) => {
const http = require('node:http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`
vm.runInThisContext(code)(require)
위의 경우에서 require()
는 전달된 컨텍스트와 상태를 공유합니다. 이는 신뢰할 수 없는 코드가 실행될 때 원치 않는 방식으로 컨텍스트의 객체를 변경하는 등 위험을 초래할 수 있습니다.
"컨텍스트화" 객체란 무엇을 의미합니까?
Node.js 내에서 실행되는 모든 JavaScript는 "컨텍스트" 범위 내에서 실행됩니다. V8 임베더 가이드에 따르면:
vm.createContext()
메서드가 객체와 함께 호출되면 contextObject
인수는 V8 컨텍스트의 새 인스턴스의 전역 객체를 래핑하는 데 사용됩니다(만약 contextObject
가 undefined
이면, 컨텍스트화되기 전에 현재 컨텍스트에서 새 객체가 생성됩니다). 이 V8 컨텍스트는 node:vm
모듈의 메서드를 사용하여 실행되는 code
에 작동할 수 있는 격리된 전역 환경을 제공합니다. V8 컨텍스트를 생성하고 외부 컨텍스트에서 contextObject
와 연결하는 프로세스를 이 문서에서는 객체를 "컨텍스트화"한다고 합니다.
컨텍스트화는 컨텍스트의 globalThis
값에 일부 특이한 동작을 도입합니다. 예를 들어, 동결할 수 없으며, 외부 컨텍스트의 contextObject
와 참조가 같지 않습니다.
const vm = require('node:vm')
// undefined `contextObject` 옵션은 전역 객체를 컨텍스트화합니다.
const context = vm.createContext()
console.log(vm.runInContext('globalThis', context) === context) // false
// 컨텍스트화된 전역 객체는 동결할 수 없습니다.
try {
vm.runInContext('Object.freeze(globalThis);', context)
} catch (e) {
console.log(e) // TypeError: 동결할 수 없음
}
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
값은 일반적인 값에 더 가깝게 동작합니다.
const vm = require('node:vm')
// vm.constants.DONT_CONTEXTIFY를 사용하여 글로벌 객체를 고정합니다.
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
vm.runInContext('Object.freeze(globalThis);', context)
try {
vm.runInContext('bar = 1; bar;', context)
} catch (e) {
console.log(e) // Uncaught ReferenceError: bar is not defined
}
vm.constants.DONT_CONTEXTIFY
가 vm.createContext()
에 대한 contextObject
인수로 사용되면 반환된 객체는 새롭게 생성된 컨텍스트에서 전역 객체에 대한 프록시와 유사한 객체이며 Node.js 특정 특성이 더 적습니다. 이것은 새로운 컨텍스트의 globalThis
값과 참조가 같고, 컨텍스트 외부에서 수정할 수 있으며, 새로운 컨텍스트에서 빌트인에 직접 접근하는 데 사용할 수 있습니다.
const vm = require('node:vm')
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY)
// 반환된 객체는 새 컨텍스트의 globalThis와 참조가 같습니다.
console.log(vm.runInContext('globalThis', context) === context) // true
// 새 컨텍스트에서 globals에 직접 접근하는 데 사용할 수 있습니다.
console.log(context.Array) // [Function: Array]
vm.runInContext('foo = 1;', context)
console.log(context.foo) // 1
context.bar = 1
console.log(vm.runInContext('bar;', context)) // 1
// 고정될 수 있으며 내부 컨텍스트에 영향을 미칩니다.
Object.freeze(context)
try {
vm.runInContext('baz = 1; baz;', context)
} catch (e) {
console.log(e) // Uncaught ReferenceError: baz is not defined
}
비동기 작업 및 Promise와의 타임아웃 상호 작용
Promise
와 async function
은 JavaScript 엔진에 의해 비동기적으로 실행되는 작업을 스케줄링할 수 있습니다. 기본적으로 이러한 작업은 현재 스택의 모든 JavaScript 함수가 실행을 완료한 후에 실행됩니다. 이를 통해 timeout
및 breakOnSigint
옵션의 기능을 벗어날 수 있습니다.
예를 들어, 5밀리초의 타임아웃으로 vm.runInNewContext()
에 의해 실행되는 다음 코드는 promise가 해결된 후 실행되도록 무한 루프를 스케줄링합니다. 스케줄링된 루프는 타임아웃에 의해 중단되지 않습니다.
const vm = require('node:vm')
function loop() {
console.log('entering loop')
while (1) console.log(Date.now())
}
vm.runInNewContext('Promise.resolve().then(() => loop());', { loop, console }, { timeout: 5 })
// 이것은 'entering loop' *이전*에 출력됩니다 (!)
console.log('done executing')
이것은 Context
를 생성하는 코드에 microtaskMode: 'afterEvaluate'
를 전달하여 해결할 수 있습니다.
const vm = require('node:vm')
function loop() {
while (1) console.log(Date.now())
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' }
)
이 경우, promise.then()
을 통해 스케줄링된 마이크로태스크는 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
으로 거부됩니다.
importModuleDynamically
가 vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
인 경우
이 옵션은 현재 vm.SourceTextModule
에서 지원되지 않습니다.
이 옵션을 사용하면 컴파일된 코드에서 import()
가 시작될 때 Node.js는 메인 컨텍스트의 기본 ESM 로더를 사용하여 요청된 모듈을 로드하고 실행 중인 코드로 반환합니다.
이를 통해 컴파일되는 코드에서 fs
또는 http
와 같은 Node.js 기본 제공 모듈에 액세스할 수 있습니다. 코드가 다른 컨텍스트에서 실행되는 경우 메인 컨텍스트에서 로드된 모듈에서 생성된 객체는 여전히 메인 컨텍스트의 객체이며 새 컨텍스트의 기본 제공 클래스의 instanceof
가 아님을 유의해야 합니다.
const { Script, constants } = require('node:vm')
const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})
// false: 메인 컨텍스트에서 로드된 URL은 새 컨텍스트의 Function 클래스의 인스턴스가 아닙니다.
script.runInNewContext().then(console.log)
import { Script, constants } from 'node:vm'
const script = new Script('import("node:fs").then(({readFile}) => readFile instanceof Function)', {
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
})
// false: 메인 컨텍스트에서 로드된 URL은 새 컨텍스트의 Function 클래스의 인스턴스가 아닙니다.
script.runInNewContext().then(console.log)
:::
이 옵션을 사용하면 스크립트 또는 함수에서 사용자 모듈을 로드할 수도 있습니다.
import { Script, constants } from 'node:vm'
import { resolve } from 'node:path'
import { writeFileSync } from 'node:fs'
// 현재 실행 중인 스크립트가 있는 디렉토리에 test.js 및 test.txt를 씁니다.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(import.meta.dirname, 'test.json'), '{"hello": "world"}')
// 스크립트가 동일한 디렉토리에 있는 것처럼 test.mjs를 로드한 다음 test.json을 로드하는 스크립트를 컴파일합니다.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(import.meta.dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
}
)
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)
const { Script, constants } = require('node:vm')
const { resolve } = require('node:path')
const { writeFileSync } = require('node:fs')
// 현재 실행 중인 스크립트가 있는 디렉토리에 test.js 및 test.txt를 씁니다.
writeFileSync(resolve(__dirname, 'test.mjs'), 'export const filename = "./test.json";')
writeFileSync(resolve(__dirname, 'test.json'), '{"hello": "world"}')
// 스크립트가 동일한 디렉토리에 있는 것처럼 test.mjs를 로드한 다음 test.json을 로드하는 스크립트를 컴파일합니다.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(__dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
}
)
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log)
:::
메인 컨텍스트의 기본 로더를 사용하여 사용자 모듈을 로드하는 데에는 몇 가지 주의 사항이 있습니다.
importModuleDynamically
가 함수일 때
importModuleDynamically
가 함수일 경우, 컴파일된 코드에서 import()
가 호출될 때 요청된 모듈이 컴파일 및 평가되는 방식을 사용자 정의하기 위해 호출됩니다. 현재 Node.js 인스턴스는 이 옵션이 작동하려면 --experimental-vm-modules
플래그로 실행되어야 합니다. 플래그가 설정되지 않은 경우, 이 콜백은 무시됩니다. 평가된 코드가 실제로 import()
를 호출하는 경우 결과는 ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG
와 함께 거부됩니다.
콜백 importModuleDynamically(specifier, referrer, importAttributes)
는 다음 서명을 가집니다.
specifier
<string>import()
에 전달된 지정자referrer
<vm.Script> | <Function> | <vm.SourceTextModule> | <Object> referrer는new vm.Script
,vm.runInThisContext
,vm.runInContext
및vm.runInNewContext
에 대해 컴파일된vm.Script
입니다.vm.compileFunction
에 대해 컴파일된Function
이고,new vm.SourceTextModule
에 대해 컴파일된vm.SourceTextModule
이며,vm.createContext()
에 대해 컨텍스트Object
입니다.importAttributes
<Object>optionsExpression
선택적 매개변수에 전달된"with"
값 또는 값이 제공되지 않은 경우 빈 객체입니다.- 반환 값: <Module Namespace Object> | <vm.Module> 오류 추적을 활용하고
then
함수 내보내기를 포함하는 네임스페이스 문제를 피하려면vm.Module
을 반환하는 것이 좋습니다.
// 이 스크립트는 --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' } }
// 이 스크립트는 --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' } }
})()