set, cachedData, produceCachedData, parsingContext); } catch (e) { throw e; /* node-do-not-add-exception-line */ } if (importModuleDynamically !== undefined) { validateFunction(importModuleDynamically, 'options.importModuleDynamically'); const { importModuleDynamicallyWrap } = require('internal/vm/module'); const { callbackMap } = internalBinding('module_wrap'); callbackMap.set(this, { importModuleDynamically: importModuleDynamicallyWrap(importModuleDynamically), }); } } runInThisContext(options) { const { breakOnSigint, args } = getRunInContextArgs(options); if (breakOnSigint && process.listenerCount('SIGINT') > 0) { return sigintHandlersWrap(super.runInThisContext, this, args); } return ReflectApply(super.runInThisContext, this, args); } runInContext(contextifiedObject, options) { validateContext(contextifiedObject); const { breakOnSigint, args } = getRunInContextArgs(options); ArrayPrototypeUnshift(args, contextifiedObject); 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(options = {}) { 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: [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; } function isContext(object) { validateObject(object, 'object', { allowArray: true }); return _isContext(object); } let defaultContextNameIndex = 1; function createContext(contextObject = {}, options = {}) { if (isContext(contextObject)) { return contextObject; } validateObject(options, 'options'); const { name = `VM Context ${defaultContextNameIndex++}`, origin, codeGeneration, microtaskMode } = 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'); } let microtaskQueue = null; if (microtaskMode !== undefined) { validateOneOf(microtaskMode, 'options.microtaskMode', ['afterEvaluate', undefined]); if (microtaskMode === 'afterEvaluate') microtaskQueue = new MicrotaskQueue(); } makeContext(contextObject, name, origin, strings, wasm, microtaskQueue); 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. ArrayPrototypeForEach(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 = {}) { validateString(code, 'code'); if (params !== undefined) { validateArray(params, 'params'); ArrayPrototypeForEach(params, (param, i) => validateString(param, `params[${i}]`)); } const { filename = '', columnOffset = 0, lineOffset = 0, cachedData = undefined, produceCachedData = false, parsingContext = undefined, contextExtensions = [], importModuleDynamically, } = options; validateString(filename, 'options.filename'); validateUint32(columnOffset, 'options.columnOffset'); validateUint32(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'); ArrayPrototypeForEach(contextExtensions, (extension, i) => { const name = `options.contextExtensions[${i}]`; validateObject(extension, name, { nullable: true }); }); const result = _compileFunction( code, filename, lineOffset, columnOffset, cachedData, produceCachedData, parsingContext, contextExtensions, params ); if (produceCachedData) { result.function.cachedDataProduced = result.cachedDataProduced; } if (result.cachedData) { result.function.cachedData = result.cachedData; } if (importModuleDynamically !== undefined) { validateFunction(importModuleDynamically, 'options.importModuleDynamically'); const { importModuleDynamicallyWrap } = require('internal/vm/module'); const { callbackMap } = internalBinding('module_wrap'); const wrapped = importModuleDynamicallyWrap(importModuleDynamically); const func = result.function; callbackMap.set(result.cacheKey, { importModuleDynamically: (s, _k, i) => wrapped(s, func, i), }); } return result.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 = {}) { 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 PromiseReject(new ERR_CONTEXT_NOT_INITIALIZED()); } return result; } module.exports = { Script, createContext, createScript, runInContext, runInNewContext, runInThisContext, isContext, compileFunction, measureMemory, }; // 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. // Copyright (c) 2014, StrongLoop Inc. // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 'use strict'; const { Array, ArrayBuffer, ArrayPrototypeForEach, ArrayPrototypePush, DataView, Error, Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, ObjectPrototypeToString, SafeMap, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, } = primordials; const { Buffer } = require('buffer'); const { validateString } = require('internal/validators'); const { Serializer, Deserializer } = internalBinding('serdes'); let profiler = {}; if (internalBinding('config').hasInspector) { profiler = internalBinding('profiler'); } const assert = require('internal/assert'); const { copy } = internalBinding('buffer'); const { inspect } = require('internal/util/inspect'); const { FastBuffer } = require('internal/buffer'); const { getValidatedPath } = require('internal/fs/utils'); const { toNamespacedPath } = require('path'); const { createHeapSnapshotStream, triggerHeapSnapshot } = internalBinding('heap_utils'); const { HeapSnapshotStream } = require('internal/heap_utils'); const promiseHooks = require('internal/promise_hooks'); /** * Generates a snapshot of the current V8 heap * and writes it to a JSON file. * @param {string} [filename] * @returns {string} */ function writeHeapSnapshot(filename) { if (filename !== undefined) { filename = getValidatedPath(filename); filename = toNamespacedPath(filename); } return triggerHeapSnapshot(filename); } /** * Generates a snapshot of the current V8 heap * and returns a Readable Stream. * @returns {import('./stream.js').Readable} */ function getHeapSnapshot() { const handle = createHeapSnapshotStream(); assert(handle); return new HeapSnapshotStream(handle); } // We need to get the buffer from the binding at the callsite since // it's re-initialized after deserialization. const binding = internalBinding('v8'); const { cachedDataVersionTag, setFlagsFromString: _setFlagsFromString, updateHeapStatisticsBuffer, updateHeapSpaceStatisticsBuffer, updateHeapCodeStatisticsBuffer, // Properties for heap statistics buffer extraction. kTotalHeapSizeIndex, kTotalHeapSizeExecutableIndex, kTotalPhysicalSizeIndex, kTotalAvailableSize, kUsedHeapSizeIndex, kHeapSizeLimitIndex, kDoesZapGarbageIndex, kMallocedMemoryIndex, kPeakMallocedMemoryIndex, kNumberOfNativeContextsIndex, kNumberOfDetachedContextsIndex, // Properties for heap spaces statistics buffer extraction. kHeapSpaces, kSpaceSizeIndex, kSpaceUsedSizeIndex, kSpaceAvailableSizeIndex, kPhysicalSpaceSizeIndex, // Properties for heap code statistics buffer extraction. kCodeAndMetadataSizeIndex, kBytecodeAndMetadataSizeIndex, kExternalScriptSourceSizeIndex } = binding; const kNumberOfHeapSpaces = kHeapSpaces.length; /** * Sets V8 command-line flags. * @param {string} flags * @returns {void} */ function setFlagsFromString(flags) { validateString(flags, 'flags'); _setFlagsFromString(flags); } /** * Gets the current V8 heap statistics. * @returns {{ * total_heap_size: number; * total_heap_size_executable: number; * total_physical_size: number; * total_available_size: number; * used_heap_size: number; * heap_size_limit: number; * malloced_memory: number; * peak_malloced_memory: number; * does_zap_garbage: number; * number_of_native_contexts: number; * number_of_detached_contexts: number; * }} */ function getHeapStatistics() { const buffer = binding.heapStatisticsBuffer; updateHeapStatisticsBuffer(); return { total_heap_size: buffer[kTotalHeapSizeIndex], total_heap_size_executable: buffer[kTotalHeapSizeExecutableIndex], total_physical_size: buffer[kTotalPhysicalSizeIndex], total_available_size: buffer[kTotalAvailableSize], used_heap_size: buffer[kUsedHeapSizeIndex], heap_size_limit: buffer[kHeapSizeLimitIndex], malloced_memory: buffer[kMallocedMemoryIndex], peak_malloced_memory: buffer[kPeakMallocedMemoryIndex], does_zap_garbage: buffer[kDoesZapGarbageIndex], number_of_native_contexts: buffer[kNumberOfNativeContextsIndex], number_of_detached_contexts: buffer[kNumberOfDetachedContextsIndex] }; } /** * Gets the current V8 heap space statistics. * @returns {{ * space_name: string; * space_size: number; * space_used_size: number; * space_available_size: number; * physical_space_size: number; * }[]} */ function getHeapSpaceStatistics() { const heapSpaceStatistics = new Array(kNumberOfHeapSpaces); const buffer = binding.heapSpaceStatisticsBuffer; for (let i = 0; i < kNumberOfHeapSpaces; i++) { updateHeapSpaceStatisticsBuffer(i); heapSpaceStatistics[i] = { space_name: kHeapSpaces[i], space_size: buffer[kSpaceSizeIndex], space_used_size: buffer[kSpaceUsedSizeIndex], space_available_size: buffer[kSpaceAvailableSizeIndex], physical_space_size: buffer[kPhysicalSpaceSizeIndex] }; } return heapSpaceStatistics; } /** * Gets the current V8 heap code statistics. * @returns {{ * code_and_metadata_size: number; * bytecode_and_metadata_size: number; * external_script_source_size: number; * }} */ function getHeapCodeStatistics() { const buffer = binding.heapCodeStatisticsBuffer; updateHeapCodeStatisticsBuffer(); return { code_and_metadata_size: buffer[kCodeAndMetadataSizeIndex], bytecode_and_metadata_size: buffer[kBytecodeAndMetadataSizeIndex], external_script_source_size: buffer[kExternalScriptSourceSizeIndex] }; } /* V8 serialization API */ /* JS methods for the base objects */ Serializer.prototype._getDataCloneError = Error; /** * Reads raw bytes from the deserializer's internal buffer. * @param {number} length * @returns {Buffer} */ Deserializer.prototype.readRawBytes = function readRawBytes(length) { const offset = this._readRawBytes(length); // `this.buffer` can be a Buffer or a plain Uint8Array, so just calling // `.slice()` doesn't work. return new FastBuffer(this.buffer.buffer, this.buffer.byteOffset + offset, length); }; /* Keep track of how to handle different ArrayBufferViews. * The default Serializer for Node does not use the V8 methods for serializing * those objects because Node's `Buffer` objects use pooled allocation in many * cases, and their underlying `ArrayBuffer`s would show up in the * serialization. Because a) those may contain sensitive data and the user * may not be aware of that and b) they are often much larger than the `Buffer` * itself, custom serialization is applied. */ const arrayBufferViewTypes = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, DataView]; const arrayBufferViewTypeToIndex = new SafeMap(); { const dummy = new ArrayBuffer(); ArrayPrototypeForEach(arrayBufferViewTypes, (ctor, i) => { const tag = ObjectPrototypeToString(new ctor(dummy)); arrayBufferViewTypeToIndex.set(tag, i); }); } const bufferConstructorIndex = ArrayPrototypePush(arrayBufferViewTypes, FastBuffer) - 1; class DefaultSerializer extends Serializer { constructor() { super(); this._setTreatArrayBufferViewsAsHostObjects(true); } /** * Used to write some kind of host object, i.e. an * object that is created by native C++ bindings. * @param {object} abView * @returns {void} */ _writeHostObject(abView) { let i = 0; if (abView.constructor === Buffer) { i = bufferConstructorIndex; } else { const tag = ObjectPrototypeToString(abView); i = arrayBufferViewTypeToIndex.get(tag); if (i === undefined) { throw new this._getDataCloneError( `Unserializable host object: ${inspect(abView)}`); } } this.writeUint32(i); this.writeUint32(abView.byteLength); this.writeRawBytes(new Uint8Array(abView.buffer, abView.byteOffset, abView.byteLength)); } } class DefaultDeserializer extends Deserializer { /** * Used to read some kind of host object, i.e. an * object that is created by native C++ bindings. * @returns {any} */ _readHostObject() { const typeIndex = this.readUint32(); const ctor = arrayBufferViewTypes[typeIndex]; const byteLength = this.readUint32(); const byteOffset = this._readRawBytes(byteLength); const BYTES_PER_ELEMENT = ctor.BYTES_PER_ELEMENT || 1; const offset = this.buffer.byteOffset + byteOffset; if (offset % BYTES_PER_ELEMENT === 0) { return new ctor(this.buffer.buffer, offset, byteLength / BYTES_PER_ELEMENT); } // Copy to an aligned buffer first. const buffer_copy = Buffer.allocUnsafe(byteLength); copy(this.buffer, buffer_copy, 0, byteOffset, byteOffset + byteLength); return new ctor(buffer_copy.buffer, buffer_copy.byteOffset, byteLength / BYTES_PER_ELEMENT); } } /** * Uses a `DefaultSerializer` to serialize `value` * into a buffer. * @param {any} value * @returns {Buffer} */ function serialize(value) { const ser = new DefaultSerializer(); ser.writeHeader(); ser.writeValue(value); return ser.releaseBuffer(); } /** * Uses a `DefaultDeserializer` with default options * to read a JavaScript value from a buffer. * @param {Buffer | TypedArray | DataView} buffer * @returns {any} */ function deserialize(buffer) { const der = new DefaultDeserializer(buffer); der.readHeader(); return der.readValue(); } module.exports = { cachedDataVersionTag, getHeapSnapshot, getHeapStatistics, getHeapSpaceStatistics, getHeapCodeStatistics, setFlagsFromString, Serializer, Deserializer, DefaultSerializer, DefaultDeserializer, deserialize, takeCoverage: profiler.takeCoverage, stopCoverage: profiler.stopCoverage, serialize, writeHeapSnapshot, promiseHooks, };