UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

433 lines (387 loc) 12.9 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/vm.js import { ContextifyScript, makeContext, constants, measureMemory as _measureMemory, } from "nstdlib/stub/binding/contextify"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { validateArray, validateBoolean, validateBuffer, validateInt32, validateOneOf, validateObject, validateString, validateStringArray, validateUint32, kValidateObjectAllowArray, kValidateObjectAllowNullable, } from "nstdlib/lib/internal/validators"; import { emitExperimentalWarning, kEmptyObject, kVmBreakFirstLineSymbol, } from "nstdlib/lib/internal/util"; import { getHostDefinedOptionId, internalCompileFunction, isContext as _isContext, registerImportModuleDynamically, } from "nstdlib/lib/internal/vm"; import { vm_dynamic_import_main_context_default } from "nstdlib/stub/binding/symbols"; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. const { ERR_CONTEXT_NOT_INITIALIZED, ERR_INVALID_ARG_TYPE } = __codes__; const kParsingContext = Symbol("script parsing context"); /** * Check if object is a context object created by vm.createContext(). * @throws {TypeError} If object is not an object in the first place, throws TypeError. * @param {object} object Object to check. * @returns {boolean} */ function isContext(object) { validateObject(object, "object", kValidateObjectAllowArray); return _isContext(object); } class Script extends ContextifyScript { constructor(code, options = kEmptyObject) { code = `${code}`; if (typeof options === "string") { options = { filename: options }; } else { validateObject(options, "options"); } const { filename = "evalmachine.<anonymous>", lineOffset = 0, columnOffset = 0, cachedData, produceCachedData = false, importModuleDynamically, [kParsingContext]: parsingContext, } = options; validateString(filename, "options.filename"); validateInt32(lineOffset, "options.lineOffset"); validateInt32(columnOffset, "options.columnOffset"); if (cachedData !== undefined) { validateBuffer(cachedData, "options.cachedData"); } validateBoolean(produceCachedData, "options.produceCachedData"); const hostDefinedOptionId = getHostDefinedOptionId( importModuleDynamically, filename, ); // Calling `ReThrow()` on a native TryCatch does not generate a new // abort-on-uncaught-exception check. A dummy try/catch in JS land // protects against that. try { // eslint-disable-line no-useless-catch super( code, filename, lineOffset, columnOffset, cachedData, produceCachedData, parsingContext, hostDefinedOptionId, ); } catch (e) { throw e; /* node-do-not-add-exception-line */ } registerImportModuleDynamically(this, importModuleDynamically); } runInThisContext(options) { const { breakOnSigint, args } = getRunInContextArgs(null, options); if (breakOnSigint && process.listenerCount("SIGINT") > 0) { return sigintHandlersWrap(super.runInContext, this, args); } return ReflectApply(super.runInContext, this, args); } runInContext(contextifiedObject, options) { validateContext(contextifiedObject); const { breakOnSigint, args } = getRunInContextArgs( contextifiedObject, options, ); if (breakOnSigint && process.listenerCount("SIGINT") > 0) { return sigintHandlersWrap(super.runInContext, this, args); } return ReflectApply(super.runInContext, this, args); } runInNewContext(contextObject, options) { const context = createContext(contextObject, getContextOptions(options)); return this.runInContext(context, options); } } function validateContext(contextifiedObject) { if (!isContext(contextifiedObject)) { throw new ERR_INVALID_ARG_TYPE( "contextifiedObject", "vm.Context", contextifiedObject, ); } } function getRunInContextArgs(contextifiedObject, options = kEmptyObject) { validateObject(options, "options"); let timeout = options.timeout; if (timeout === undefined) { timeout = -1; } else { validateUint32(timeout, "options.timeout", true); } const { displayErrors = true, breakOnSigint = false, [kVmBreakFirstLineSymbol]: breakFirstLine = false, } = options; validateBoolean(displayErrors, "options.displayErrors"); validateBoolean(breakOnSigint, "options.breakOnSigint"); return { breakOnSigint, args: [ contextifiedObject, timeout, displayErrors, breakOnSigint, breakFirstLine, ], }; } function getContextOptions(options) { if (!options) return {}; const contextOptions = { name: options.contextName, origin: options.contextOrigin, codeGeneration: undefined, microtaskMode: options.microtaskMode, }; if (contextOptions.name !== undefined) validateString(contextOptions.name, "options.contextName"); if (contextOptions.origin !== undefined) validateString(contextOptions.origin, "options.contextOrigin"); if (options.contextCodeGeneration !== undefined) { validateObject( options.contextCodeGeneration, "options.contextCodeGeneration", ); const { strings, wasm } = options.contextCodeGeneration; if (strings !== undefined) validateBoolean(strings, "options.contextCodeGeneration.strings"); if (wasm !== undefined) validateBoolean(wasm, "options.contextCodeGeneration.wasm"); contextOptions.codeGeneration = { strings, wasm }; } if (options.microtaskMode !== undefined) validateString(options.microtaskMode, "options.microtaskMode"); return contextOptions; } let defaultContextNameIndex = 1; function createContext(contextObject = {}, options = kEmptyObject) { if (isContext(contextObject)) { return contextObject; } validateObject(options, "options"); const { name = `VM Context ${defaultContextNameIndex++}`, origin, codeGeneration, microtaskMode, importModuleDynamically, } = options; validateString(name, "options.name"); if (origin !== undefined) validateString(origin, "options.origin"); if (codeGeneration !== undefined) validateObject(codeGeneration, "options.codeGeneration"); let strings = true; let wasm = true; if (codeGeneration !== undefined) { ({ strings = true, wasm = true } = codeGeneration); validateBoolean(strings, "options.codeGeneration.strings"); validateBoolean(wasm, "options.codeGeneration.wasm"); } validateOneOf(microtaskMode, "options.microtaskMode", [ "afterEvaluate", undefined, ]); const microtaskQueue = microtaskMode === "afterEvaluate"; const hostDefinedOptionId = getHostDefinedOptionId( importModuleDynamically, name, ); makeContext( contextObject, name, origin, strings, wasm, microtaskQueue, hostDefinedOptionId, ); // Register the context scope callback after the context was initialized. registerImportModuleDynamically(contextObject, importModuleDynamically); return contextObject; } function createScript(code, options) { return new Script(code, options); } // Remove all SIGINT listeners and re-attach them after the wrapped function // has executed, so that caught SIGINT are handled by the listeners again. function sigintHandlersWrap(fn, thisArg, argsArray) { const sigintListeners = process.rawListeners("SIGINT"); process.removeAllListeners("SIGINT"); try { return ReflectApply(fn, thisArg, argsArray); } finally { // Add using the public methods so that the `newListener` handler of // process can re-attach the listeners. Array.prototype.forEach.call(sigintListeners, (listener) => { process.addListener("SIGINT", listener); }); } } function runInContext(code, contextifiedObject, options) { validateContext(contextifiedObject); if (typeof options === "string") { options = { filename: options, [kParsingContext]: contextifiedObject, }; } else { options = { ...options, [kParsingContext]: contextifiedObject }; } return createScript(code, options).runInContext(contextifiedObject, options); } function runInNewContext(code, contextObject, options) { if (typeof options === "string") { options = { filename: options }; } contextObject = createContext(contextObject, getContextOptions(options)); options = { ...options, [kParsingContext]: contextObject }; return createScript(code, options).runInNewContext(contextObject, options); } function runInThisContext(code, options) { if (typeof options === "string") { options = { filename: options }; } return createScript(code, options).runInThisContext(options); } function compileFunction(code, params, options = kEmptyObject) { validateString(code, "code"); if (params !== undefined) { validateStringArray(params, "params"); } const { filename = "", columnOffset = 0, lineOffset = 0, cachedData = undefined, produceCachedData = false, parsingContext = undefined, contextExtensions = [], importModuleDynamically, } = options; validateString(filename, "options.filename"); validateInt32(columnOffset, "options.columnOffset"); validateInt32(lineOffset, "options.lineOffset"); if (cachedData !== undefined) validateBuffer(cachedData, "options.cachedData"); validateBoolean(produceCachedData, "options.produceCachedData"); if (parsingContext !== undefined) { if ( typeof parsingContext !== "object" || parsingContext === null || !isContext(parsingContext) ) { throw new ERR_INVALID_ARG_TYPE( "options.parsingContext", "Context", parsingContext, ); } } validateArray(contextExtensions, "options.contextExtensions"); Array.prototype.forEach.call(contextExtensions, (extension, i) => { const name = `options.contextExtensions[${i}]`; validateObject(extension, name, kValidateObjectAllowNullable); }); const hostDefinedOptionId = getHostDefinedOptionId( importModuleDynamically, filename, ); return internalCompileFunction( code, filename, lineOffset, columnOffset, cachedData, produceCachedData, parsingContext, contextExtensions, params, hostDefinedOptionId, importModuleDynamically, ).function; } const measureMemoryModes = { summary: constants.measureMemory.mode.SUMMARY, detailed: constants.measureMemory.mode.DETAILED, }; const measureMemoryExecutions = { default: constants.measureMemory.execution.DEFAULT, eager: constants.measureMemory.execution.EAGER, }; function measureMemory(options = kEmptyObject) { emitExperimentalWarning("vm.measureMemory"); validateObject(options, "options"); const { mode = "summary", execution = "default" } = options; validateOneOf(mode, "options.mode", ["summary", "detailed"]); validateOneOf(execution, "options.execution", ["default", "eager"]); const result = _measureMemory( measureMemoryModes[mode], measureMemoryExecutions[execution], ); if (result === undefined) { return Promise.reject(new ERR_CONTEXT_NOT_INITIALIZED()); } return result; } const vmConstants = { __proto__: null, USE_MAIN_CONTEXT_DEFAULT_LOADER: vm_dynamic_import_main_context_default, }; Object.freeze(vmConstants); export { Script }; export { createContext }; export { createScript }; export { runInContext }; export { runInNewContext }; export { runInThisContext }; export { isContext }; export { compileFunction }; export { measureMemory }; export { vmConstants as constants }; // The vm module is patched to include vm.Module, vm.SourceTextModule // and vm.SyntheticModule in the pre-execution phase when // --experimental-vm-modules is on.