/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/v8.ts
838 строк
25 KB
Bartek Iwańczuk
fix(ext/node): implement v8.promiseHooks API (#36281)
27 июл 2026, 15:51
Не верифицирован
27 июл 2026, 15:51
861c8c9
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. /// <reference path="../../core/internal.d.ts" /> (function () { const { core, primordials } = __bootstrap; const { Array, ArrayPrototypeIndexOf, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSplice, BigInt64Array, BigUint64Array, DataView, DataViewPrototypeGetBuffer, DataViewPrototypeGetByteLength, DataViewPrototypeGetByteOffset, Date, DateNow, DatePrototypeGetDate, DatePrototypeGetFullYear, DatePrototypeGetHours, DatePrototypeGetMinutes, DatePrototypeGetMonth, DatePrototypeGetSeconds, Error, Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, ObjectFreeze, ObjectPrototypeToString, String, StringPrototypePadStart, Symbol, SymbolDispose, SymbolSpecies, TypeError, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, } = primordials; const { op_v8_cached_data_version_tag, op_v8_get_heap_code_statistics, op_v8_get_heap_statistics, op_v8_get_wire_format_version, op_v8_new_deserializer, op_v8_new_serializer, op_v8_number_of_heap_spaces, op_v8_read_double, op_v8_read_header, op_v8_read_raw_bytes, op_v8_read_uint32, op_v8_read_uint64, op_v8_read_value, op_v8_release_buffer, op_v8_set_flags_from_string, op_v8_set_heap_snapshot_near_heap_limit, op_v8_set_treat_array_buffer_views_as_host_objects, op_v8_query_objects_count, op_v8_take_heap_snapshot, op_v8_transfer_array_buffer, op_v8_transfer_array_buffer_de, op_v8_update_heap_space_statistics, op_v8_write_double, op_v8_write_header, op_v8_write_raw_bytes, op_v8_write_uint32, op_v8_write_uint64, op_v8_write_value, op_v8_gc_profiler_new, op_v8_gc_profiler_start, op_v8_gc_profiler_stop, } = core.ops; const { Buffer } = core.loadExtScript("ext:deno_node/internal/buffer.mjs"); const lazyFs = core.createLazyLoader("node:fs"); const lazyStream = core.createLazyLoader("node:stream"); const { notImplemented } = core.loadExtScript("ext:deno_node/_utils.ts"); const { isArrayBufferView, isDataView } = core.loadExtScript( "ext:deno_node/internal/util/types.ts", ); function getViewBuffer(view: ArrayBufferView): ArrayBufferLike { return isDataView(view) ? DataViewPrototypeGetBuffer(view as DataView) : TypedArrayPrototypeGetBuffer(view as Uint8Array); } function getViewByteOffset(view: ArrayBufferView): number { return isDataView(view) ? DataViewPrototypeGetByteOffset(view as DataView) : TypedArrayPrototypeGetByteOffset(view as Uint8Array); } function getViewByteLength(view: ArrayBufferView): number { return isDataView(view) ? DataViewPrototypeGetByteLength(view as DataView) : TypedArrayPrototypeGetByteLength(view as Uint8Array); } const lazyFsUtils = core.createLazyLoader( "ext:deno_node/internal/fs/utils.mjs", ); const { validateFunction, validateObject, validateOneOf, validateString, validateUint32, } = core .loadExtScript( "ext:deno_node/internal/validators.mjs", ); function cachedDataVersionTag() { return op_v8_cached_data_version_tag(); } const heapCodeStatisticsBuffer = new Float64Array(4); function getHeapCodeStatistics() { op_v8_get_heap_code_statistics(heapCodeStatisticsBuffer); return { code_and_metadata_size: heapCodeStatisticsBuffer[0], bytecode_and_metadata_size: heapCodeStatisticsBuffer[1], external_script_source_size: heapCodeStatisticsBuffer[2], cpu_profiler_metadata_size: heapCodeStatisticsBuffer[3], }; } function getHeapSnapshot(options?: Record<string, unknown>) { if (options !== undefined) { validateObject(options, "options"); } const data = op_v8_take_heap_snapshot(); return lazyStream().Readable.from(Buffer.from(data)); } const heapSpaceStatisticsBuffer = new Float64Array(4); function getHeapSpaceStatistics() { const numberOfHeapSpaces = op_v8_number_of_heap_spaces(); const heapSpaceStatistics = new Array(numberOfHeapSpaces); for (let i = 0; i < numberOfHeapSpaces; i++) { const spaceName = op_v8_update_heap_space_statistics( heapSpaceStatisticsBuffer, i, ); heapSpaceStatistics[i] = { space_name: spaceName, space_size: heapSpaceStatisticsBuffer[0], space_used_size: heapSpaceStatisticsBuffer[1], space_available_size: heapSpaceStatisticsBuffer[2], physical_space_size: heapSpaceStatisticsBuffer[3], }; } return heapSpaceStatistics; } const buffer = new Float64Array(15); function getHeapStatistics() { op_v8_get_heap_statistics(buffer); return { total_heap_size: buffer[0], total_heap_size_executable: buffer[1], total_physical_size: buffer[2], total_available_size: buffer[3], used_heap_size: buffer[4], heap_size_limit: buffer[5], malloced_memory: buffer[6], peak_malloced_memory: buffer[7], does_zap_garbage: buffer[8], number_of_native_contexts: buffer[9], number_of_detached_contexts: buffer[10], total_global_handles_size: buffer[11], used_global_handles_size: buffer[12], external_memory: buffer[13], total_allocated_bytes: buffer[14], }; } function setFlagsFromString(flags: string) { // NOTE(bartlomieju): From Node.js docs: // The v8.setFlagsFromString() method can be used to programmatically set V8 // command-line flags. This method should be used with care. Changing settings // after the VM has started may result in unpredictable behavior, including // crashes and data loss; or it may simply do nothing. validateString(flags, "flags"); op_v8_set_flags_from_string(flags); } function stopCoverage() { notImplemented("v8.stopCoverage"); } function takeCoverage() { notImplemented("v8.takeCoverage"); } let heapSnapshotCounter = 0; function writeHeapSnapshot( filename?: string, options?: Record<string, unknown>, ) { if (filename !== undefined) { filename = lazyFsUtils().getValidatedPath(filename) as string; } else { const now = new Date(); const year = DatePrototypeGetFullYear(now); const month = StringPrototypePadStart( String(DatePrototypeGetMonth(now) + 1), 2, "0", ); const day = StringPrototypePadStart( String(DatePrototypeGetDate(now)), 2, "0", ); const hours = StringPrototypePadStart( String(DatePrototypeGetHours(now)), 2, "0", ); const minutes = StringPrototypePadStart( String(DatePrototypeGetMinutes(now)), 2, "0", ); const seconds = StringPrototypePadStart( String(DatePrototypeGetSeconds(now)), 2, "0", ); const pid = globalThis.process?.pid ?? 0; const thread = 0; const seq = ++heapSnapshotCounter; filename = `Heap.${year}${month}${day}.${hours}${minutes}${seconds}.${pid}.${thread}.${ StringPrototypePadStart(String(seq), 3, "0") }.heapsnapshot`; } if (options !== undefined) { validateObject(options, "options"); } const data = op_v8_take_heap_snapshot(); lazyFs().writeFileSync(filename, data); return filename; } let heapSnapshotNearHeapLimitSet = false; // https://nodejs.org/api/v8.html#v8setheapsnapshotnearheaplimitlimit // // Installs a V8 near-heap-limit callback that writes a `.heapsnapshot` file to // disk (up to `limit` times) right before the process would run out of memory. function setHeapSnapshotNearHeapLimit(limit: number) { validateUint32(limit, "limit", true); if (heapSnapshotNearHeapLimitSet) { return; } op_v8_set_heap_snapshot_near_heap_limit(limit); heapSnapshotNearHeapLimitSet = true; } // https://nodejs.org/api/v8.html#v8queryobjectsctor-options // // Deno currently only supports `{ format: 'count' }`. Returning live instances // would require V8's `HeapProfiler::QueryObjects`, which isn't exposed in the // rusty_v8 bindings; the count form is what Node's leak tests rely on. function queryObjects( ctor: { name?: string; prototype?: unknown }, options: | { format?: "count" | "summary" } | undefined = undefined, ) { validateFunction(ctor, "constructor"); if (options !== undefined) { validateObject(options, "options"); if (options.format !== undefined) { validateOneOf(options.format, "options.format", ["count", "summary"]); } } const format = options?.format; const name = typeof ctor.name === "string" ? ctor.name : ""; if (name === "") { return format === "count" ? 0 : []; } const count = op_v8_query_objects_count(name); if (format === "count") { return count; } if (format === "summary") { if (count === 0) return []; return [`${count} instance(s) of ${name}`]; } // Default format returns live object handles, which would require V8's // `HeapProfiler::QueryObjects` (not exposed in rusty_v8). Returning an // empty array keeps the signature sensible. return []; } // deno-lint-ignore no-explicit-any function serialize(value: any) { const ser = new DefaultSerializer(); ser.writeHeader(); ser.writeValue(value); return ser.releaseBuffer(); } function deserialize(buffer: Buffer | ArrayBufferView | DataView) { if (!isArrayBufferView(buffer)) { throw new TypeError( "buffer must be a TypedArray or a DataView", ); } const der = new DefaultDeserializer(buffer); der.readHeader(); return der.readValue(); } const kHandle = Symbol("kHandle"); const kHeaderWritten = Symbol("kHeaderWritten"); class Serializer { [kHandle]: object; [kHeaderWritten] = false; constructor() { this[kHandle] = op_v8_new_serializer(this); } _setTreatArrayBufferViewsAsHostObjects(value: boolean): void { op_v8_set_treat_array_buffer_views_as_host_objects(this[kHandle], value); } releaseBuffer(): Buffer { const buf = Buffer.from(op_v8_release_buffer(this[kHandle])); // V8 14.9 bumped the ValueSerializer wire format version from 15 to // 16 to support ArrayBuffers larger than 4GB. Node.js cannot // deserialize format 16, which breaks consumers that feed // `v8.serialize` output to a Node.js process. For payloads smaller // than 4GB both formats encode identical bytes after the two-byte // header, so relabel the header as version 15 to keep the output // readable by Node.js. See // https://github.com/denoland/deno/issues/35113. if ( this[kHeaderWritten] && getViewByteLength(buf) < 0x100000000 && buf[0] === 0xFF && buf[1] === 0x10 ) { buf[1] = 0x0F; } return buf; } transferArrayBuffer(_id: number, _arrayBuffer: ArrayBuffer): void { op_v8_transfer_array_buffer(this[kHandle], _id, _arrayBuffer); } writeDouble(value: number): void { op_v8_write_double(this[kHandle], value); } writeHeader(): void { op_v8_write_header(this[kHandle]); this[kHeaderWritten] = true; } writeRawBytes(source: ArrayBufferView): void { if (!isArrayBufferView(source)) { throw new TypeError( "source must be a TypedArray or a DataView", ); } op_v8_write_raw_bytes(this[kHandle], source); } writeUint32(value: number): void { op_v8_write_uint32(this[kHandle], value); } writeUint64(hi: number, lo: number): void { op_v8_write_uint64(this[kHandle], hi, lo); } // deno-lint-ignore no-explicit-any writeValue(value: any): void { op_v8_write_value(this[kHandle], value); } _getDataCloneError = Error; } class Deserializer { buffer: ArrayBufferView; [kHandle]: object; constructor(buffer: ArrayBufferView) { if (!isArrayBufferView(buffer)) { throw new TypeError( "buffer must be a TypedArray or a DataView", ); } this.buffer = buffer; this[kHandle] = op_v8_new_deserializer(this, buffer); } readRawBytes(length: number): Buffer { const offset = this._readRawBytes(length); // `this.buffer` is the Deserializer's own field, not a TypedArray getter. // deno-lint-ignore deno-internal/prefer-primordials const view = this.buffer; return Buffer.from( getViewBuffer(view), getViewByteOffset(view) + offset, length, ); } _readRawBytes(length: number): number { return op_v8_read_raw_bytes(this[kHandle], length); } getWireFormatVersion(): number { return op_v8_get_wire_format_version(this[kHandle]); } readDouble(): number { return op_v8_read_double(this[kHandle]); } readHeader(): boolean { return op_v8_read_header(this[kHandle]); } readUint32(): number { return op_v8_read_uint32(this[kHandle]); } readUint64(): [hi: number, lo: number] { return op_v8_read_uint64(this[kHandle]); } readValue(): unknown { return op_v8_read_value(this[kHandle]); } transferArrayBuffer( id: number, arrayBuffer: ArrayBuffer | SharedArrayBuffer, ): void { return op_v8_transfer_array_buffer_de(this[kHandle], id, arrayBuffer); } } function arrayBufferViewTypeToIndex(abView: ArrayBufferView) { const type = ObjectPrototypeToString(abView); if (type === "[object Int8Array]") return 0; if (type === "[object Uint8Array]") return 1; if (type === "[object Uint8ClampedArray]") return 2; if (type === "[object Int16Array]") return 3; if (type === "[object Uint16Array]") return 4; if (type === "[object Int32Array]") return 5; if (type === "[object Uint32Array]") return 6; if (type === "[object Float32Array]") return 7; if (type === "[object Float64Array]") return 8; if (type === "[object DataView]") return 9; // Index 10 is FastBuffer. if (type === "[object BigInt64Array]") return 11; if (type === "[object BigUint64Array]") return 12; if (type === "[object Float16Array]") return 13; return -1; } class DefaultSerializer extends Serializer { constructor() { super(); this._setTreatArrayBufferViewsAsHostObjects(true); } // deno-lint-ignore no-explicit-any _writeHostObject(abView: any) { // 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. let i = 10; // FastBuffer if (abView.constructor !== Buffer) { i = arrayBufferViewTypeToIndex(abView); if (i === -1) { throw new this._getDataCloneError( `Unserializable host object: ${abView}`, ); } } this.writeUint32(i); this.writeUint32(getViewByteLength(abView)); this.writeRawBytes( new Uint8Array( getViewBuffer(abView), getViewByteOffset(abView), getViewByteLength(abView), ), ); } } // deno-lint-ignore no-explicit-any function arrayBufferViewIndexToType(index: number): any { if (index === 0) return Int8Array; if (index === 1) return Uint8Array; if (index === 2) return Uint8ClampedArray; if (index === 3) return Int16Array; if (index === 4) return Uint16Array; if (index === 5) return Int32Array; if (index === 6) return Uint32Array; if (index === 7) return Float32Array; if (index === 8) return Float64Array; if (index === 9) return DataView; if (index === 10) return Buffer[SymbolSpecies]; if (index === 11) return BigInt64Array; if (index === 12) return BigUint64Array; if (index === 13) return Float16Array; return undefined; } const kGCHandle = Symbol("kGCHandle"); const kGCStartTime = Symbol("kGCStartTime"); class GCProfiler { [kGCHandle]: object | null = null; [kGCStartTime]: number = 0; start() { if (this[kGCHandle] !== null) return; const handle = op_v8_gc_profiler_new(); this[kGCStartTime] = DateNow(); op_v8_gc_profiler_start(handle); this[kGCHandle] = handle; } stop() { const handle = this[kGCHandle]; if (handle === null) return undefined; this[kGCHandle] = null; const endTime = DateNow(); const result = op_v8_gc_profiler_stop(handle); if (result === null) return undefined; return { version: 1, startTime: this[kGCStartTime], endTime, statistics: result.statistics, }; } [SymbolDispose]() { const handle = this[kGCHandle]; if (handle === null) return undefined; this[kGCHandle] = null; // Ignore the report; dispose() must return undefined. op_v8_gc_profiler_stop(handle); return undefined; } } // https://nodejs.org/api/v8.html#startup-snapshot-api // // Deno does not ship `--build-snapshot` / `--snapshot-blob` for users, so this // is an API-surface polyfill that lets modules calling `v8.startupSnapshot` // load without errors. `isBuildingSnapshot()` always returns false. The // serialize/deserialize callbacks are stored but never invoked because there // is no snapshot lifecycle. `setDeserializeMainFunction` invokes the callback // synchronously so scripts that register a deserialize main still run their // entry point in plain Deno runs. // deno-lint-ignore no-explicit-any type SnapshotCallback = (data: any) => unknown; const serializeCallbacks: { fn: SnapshotCallback; data: unknown }[] = []; const deserializeCallbacks: { fn: SnapshotCallback; data: unknown }[] = []; let deserializeMainCalled = false; function startupSnapshotSetDeserializeMainFunction( fn: SnapshotCallback, data?: unknown, ) { validateFunction(fn, "callback"); if (deserializeMainCalled) { throw new Error( "v8.startupSnapshot.setDeserializeMainFunction() can only be called once.", ); } deserializeMainCalled = true; fn(data); } function startupSnapshotAddSerializeCallback( fn: SnapshotCallback, data?: unknown, ) { validateFunction(fn, "callback"); ArrayPrototypePush(serializeCallbacks, { fn, data }); } function startupSnapshotAddDeserializeCallback( fn: SnapshotCallback, data?: unknown, ) { validateFunction(fn, "callback"); ArrayPrototypePush(deserializeCallbacks, { fn, data }); } function startupSnapshotIsBuildingSnapshot() { return false; } const startupSnapshot = ObjectFreeze({ setDeserializeMainFunction: startupSnapshotSetDeserializeMainFunction, addSerializeCallback: startupSnapshotAddSerializeCallback, addDeserializeCallback: startupSnapshotAddDeserializeCallback, isBuildingSnapshot: startupSnapshotIsBuildingSnapshot, }); class DefaultDeserializer extends Deserializer { constructor(buffer: ArrayBufferView) { super(buffer); } _readHostObject() { const typeIndex = this.readUint32(); const ctor = arrayBufferViewIndexToType(typeIndex); const byteLength = this.readUint32(); const byteOffset = this._readRawBytes(byteLength); const BYTES_PER_ELEMENT = ctor?.BYTES_PER_ELEMENT ?? 1; // `this.buffer` is the Deserializer's own field, not a TypedArray getter. // deno-lint-ignore deno-internal/prefer-primordials const view = this.buffer; const offset = getViewByteOffset(view) + byteOffset; if (offset % BYTES_PER_ELEMENT === 0) { return new ctor( getViewBuffer(view), offset, byteLength / BYTES_PER_ELEMENT, ); } // Copy to an aligned buffer first. const bufferCopy = Buffer.allocUnsafe(byteLength); Buffer.from( getViewBuffer(view), byteOffset, byteLength, ).copy(bufferCopy); return new ctor( TypedArrayPrototypeGetBuffer(bufferCopy), TypedArrayPrototypeGetByteOffset(bufferCopy), byteLength / BYTES_PER_ELEMENT, ); } } // --------------------------------------------------------------------------- // v8.promiseHooks // https://nodejs.org/api/v8.html#promise-hooks // --------------------------------------------------------------------------- type PromiseHookFn = ( promise: Promise<unknown>, parent?: Promise<unknown>, ) => void; function validatePlainFunction(value: unknown, name: string) { // Reject non-functions as well as async functions and async generators - // none of them can be used as promise hooks. const ctorName = typeof value === "function" ? (value as { constructor?: { name?: string } }).constructor?.name : undefined; if ( typeof value !== "function" || ctorName === "AsyncFunction" || ctorName === "AsyncGeneratorFunction" ) { throw new TypeError( `The "${name}" argument must be of type function. Received ${typeof value}`, ); } } // Track all registered hooks so we can rebuild the combined hooks // when individual hooks are added/removed. const initHooks: PromiseHookFn[] = []; const beforeHooks: PromiseHookFn[] = []; const afterHooks: PromiseHookFn[] = []; const resolveHooks: PromiseHookFn[] = []; // Re-entrancy guard: V8 promise hooks fire for ALL promise operations, // including any promises created/resolved inside the hooks themselves. let inPromiseHook = false; // Register dispatchers once. core.setPromiseHooks is additive (no removal), // so we install permanent dispatchers that check the current hook arrays. let hooksInstalled = false; function ensureHooksInstalled() { if (hooksInstalled) return; hooksInstalled = true; core.setPromiseHooks( (promise: Promise<unknown>, parent?: Promise<unknown>) => { if (inPromiseHook || initHooks.length === 0) return; inPromiseHook = true; try { // Snapshot the list: a hook that removes itself (or another) during // dispatch must not shift the indices of hooks still to be called. const hooks = ArrayPrototypeSlice(initHooks, 0); for (let i = 0; i < hooks.length; i++) { hooks[i](promise, parent); } } finally { inPromiseHook = false; } }, (promise: Promise<unknown>) => { if (inPromiseHook || beforeHooks.length === 0) return; inPromiseHook = true; try { const hooks = ArrayPrototypeSlice(beforeHooks, 0); for (let i = 0; i < hooks.length; i++) { hooks[i](promise); } } finally { inPromiseHook = false; } }, (promise: Promise<unknown>) => { if (inPromiseHook || afterHooks.length === 0) return; inPromiseHook = true; try { const hooks = ArrayPrototypeSlice(afterHooks, 0); for (let i = 0; i < hooks.length; i++) { hooks[i](promise); } } finally { inPromiseHook = false; } }, (promise: Promise<unknown>) => { if (inPromiseHook || resolveHooks.length === 0) return; inPromiseHook = true; try { const hooks = ArrayPrototypeSlice(resolveHooks, 0); for (let i = 0; i < hooks.length; i++) { hooks[i](promise); } } finally { inPromiseHook = false; } }, ); } function removeHook(arr: PromiseHookFn[], value: PromiseHookFn) { const idx = ArrayPrototypeIndexOf(arr, value); if (idx !== -1) { ArrayPrototypeSplice(arr, idx, 1); } } const promiseHooks = { onInit(initHook: PromiseHookFn): () => void { validatePlainFunction(initHook, "initHook"); ArrayPrototypePush(initHooks, initHook); ensureHooksInstalled(); return () => removeHook(initHooks, initHook); }, onBefore(beforeHook: PromiseHookFn): () => void { validatePlainFunction(beforeHook, "beforeHook"); ArrayPrototypePush(beforeHooks, beforeHook); ensureHooksInstalled(); return () => removeHook(beforeHooks, beforeHook); }, onAfter(afterHook: PromiseHookFn): () => void { validatePlainFunction(afterHook, "afterHook"); ArrayPrototypePush(afterHooks, afterHook); ensureHooksInstalled(); return () => removeHook(afterHooks, afterHook); }, onSettled(settledHook: PromiseHookFn): () => void { validatePlainFunction(settledHook, "settledHook"); ArrayPrototypePush(resolveHooks, settledHook); ensureHooksInstalled(); return () => removeHook(resolveHooks, settledHook); }, createHook( { init, before, after, settled }: { init?: PromiseHookFn; before?: PromiseHookFn; after?: PromiseHookFn; settled?: PromiseHookFn; }, ): () => void { // Validate every provided callback before registering any of them, so a // later validation failure can't leave earlier hooks permanently // registered with no stop function to remove them. if (init !== undefined) validatePlainFunction(init, "initHook"); if (before !== undefined) validatePlainFunction(before, "beforeHook"); if (after !== undefined) validatePlainFunction(after, "afterHook"); if (settled !== undefined) validatePlainFunction(settled, "settledHook"); if (init !== undefined) ArrayPrototypePush(initHooks, init); if (before !== undefined) ArrayPrototypePush(beforeHooks, before); if (after !== undefined) ArrayPrototypePush(afterHooks, after); if (settled !== undefined) ArrayPrototypePush(resolveHooks, settled); ensureHooksInstalled(); return () => { if (init !== undefined) removeHook(initHooks, init); if (before !== undefined) removeHook(beforeHooks, before); if (after !== undefined) removeHook(afterHooks, after); if (settled !== undefined) removeHook(resolveHooks, settled); }; }, }; return { cachedDataVersionTag, getHeapCodeStatistics, getHeapSnapshot, getHeapSpaceStatistics, getHeapStatistics, queryObjects, setFlagsFromString, setHeapSnapshotNearHeapLimit, startupSnapshot, stopCoverage, takeCoverage, writeHeapSnapshot, serialize, deserialize, GCProfiler, Serializer, Deserializer, DefaultSerializer, DefaultDeserializer, promiseHooks, }; })();