/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libs/core/01_core.js
1 308 строк
39 KB
Maciej Wilk
fix(runtime): bridge console to the inspector regardless of --inspect flag (#35795)
20 июл 2026, 16:07
Не верифицирован
20 июл 2026, 16:07
5419c02
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. "use strict"; ((window) => { const { ArrayPrototypeMap, ArrayPrototypePush, Error, ErrorCaptureStackTrace, FunctionPrototypeBind, ObjectAssign, ObjectDefineProperties, ObjectDefineProperty, ObjectFreeze, ObjectFromEntries, ObjectKeys, ObjectHasOwn, setQueueMicrotask, SafeMap, SafeWeakMap, StringPrototypeSlice, Symbol, SymbolFor, TypedArrayPrototypeGetLength, TypedArrayPrototypeJoin, TypedArrayPrototypeSlice, TypedArrayPrototypeGetSymbolToStringTag, TypeError, } = window.__bootstrap.primordials; const { ops, hasPromise, promiseIdSymbol, registerErrorClass, } = window.Deno.core; const { __setLeakTracingEnabled, __isLeakTracingEnabled, __initializeCoreMethods, __resolvePromise, FixedQueue, } = window.__infra; const __timers = window.__timers; delete window.__timers; const { op_abort_wasm_streaming, op_current_user_call_site, op_compile_function, op_decode, op_deserialize, op_destructure_error, op_dispatch_exception, op_encode, op_encode_binary_string, op_eval_context, op_structured_clone, op_event_loop_has_more_work, op_get_extras_binding_object, op_get_promise_details, op_get_proxy_details, op_get_ext_import_meta_proto, op_drain_pending_rejections, op_lazy_load_esm, op_load_ext_script, op_set_captured_bootstrap, op_memory_usage, op_op_names, op_print, op_ref_op, op_resources, op_run_microtasks, op_serialize, op_add_main_module_handler, op_set_handled_promise_rejection_handler, op_set_promise_hooks, op_set_wasm_streaming_callback, op_str_byte_length, op_unref_op, op_cancel_handle, op_leak_tracing_enable, op_leak_tracing_submit, op_leak_tracing_get_all, op_leak_tracing_get, op_immediate_check, op_is_any_array_buffer, op_is_arguments_object, op_is_array_buffer, op_is_array_buffer_view, op_is_async_function, op_is_big_int_object, op_is_boolean_object, op_is_boxed_primitive, op_is_data_view, op_is_date, op_is_generator_function, op_is_generator_object, op_is_map, op_is_map_iterator, op_is_module_namespace_object, op_is_native_error, op_is_number_object, op_is_promise, op_is_proxy, op_is_reg_exp, op_is_set, op_is_set_iterator, op_is_shared_array_buffer, op_is_string_object, op_is_symbol_object, op_is_typed_array, op_is_weak_map, op_is_weak_set, } = ops; const { getContinuationPreservedEmbedderData, setContinuationPreservedEmbedderData, } = op_get_extras_binding_object(); // core/infra collaborative code delete window.__infra; __initializeCoreMethods( submitLeakTrace, ); function submitLeakTrace(id) { const error = new Error(); ErrorCaptureStackTrace(error, submitLeakTrace); // "Error\n".length == 6 op_leak_tracing_submit(0, id, StringPrototypeSlice(error.stack, 6)); } let unhandledPromiseRejectionHandler = () => false; // --------------------------------------------------------------------------- // Immediate queue (ImmediateList linked list + drain loop) // --------------------------------------------------------------------------- class ImmediateList { constructor() { this.head = null; this.tail = null; } append(item) { if (this.tail !== null) { this.tail._idleNext = item; item._idlePrev = this.tail; } else { this.head = item; } this.tail = item; } remove(item) { if (item._idleNext) { item._idleNext._idlePrev = item._idlePrev; } if (item._idlePrev) { item._idlePrev._idleNext = item._idleNext; } if (item === this.head) { this.head = item._idleNext; } if (item === this.tail) { this.tail = item._idlePrev; } item._idleNext = null; item._idlePrev = null; } } const immediateQueue = new ImmediateList(); const outstandingQueue = new ImmediateList(); // Shared buffer with Rust - avoids JS-to-Rust op calls for immediate info. // Indices: 0 = count, 1 = ref_count, 2 = has_outstanding const kImmCount = 0; const kImmRefCount = 1; const kImmHasOutstanding = 2; const kRefed = Symbol("refed"); let immediateInfo; function queueImmediate(immediate) { immediateInfo[kImmCount]++; immediateQueue.append(immediate); } function clearImmediate(immediate) { if (!immediate || immediate._destroyed) { return; } immediateInfo[kImmCount]--; immediate._destroyed = true; if (immediate[kRefed]) { immediateInfo[kImmRefCount]--; if (immediateInfo[kImmRefCount] === 0) { op_immediate_check(false); } } immediate[kRefed] = null; emitDestroy(immediate.asyncId); immediate._onImmediate = null; immediateQueue.remove(immediate); } function runImmediates() { const queue = outstandingQueue.head !== null ? outstandingQueue : immediateQueue; let immediate = queue.head; if (queue !== outstandingQueue) { queue.head = queue.tail = null; immediateInfo[kImmHasOutstanding] = 1; } let prevImmediate; let ranAtLeastOneImmediate = false; while (immediate !== null) { if (ranAtLeastOneImmediate) { runNextTicks(); } else { ranAtLeastOneImmediate = true; } if (immediate._destroyed) { outstandingQueue.head = immediate = prevImmediate._idleNext; continue; } immediate._destroyed = true; immediateInfo[kImmCount]--; if (immediate[kRefed]) { immediateInfo[kImmRefCount]--; if (immediateInfo[kImmRefCount] === 0) { op_immediate_check(false); } } immediate[kRefed] = null; prevImmediate = immediate; const asyncId = immediate.asyncId; emitBefore(asyncId, immediate.triggerAsyncId, immediate); try { const argv = immediate._argv; if (!argv) { immediate._onImmediate(); } else { immediate._onImmediate(...argv); } } finally { immediate._onImmediate = null; emitDestroy(asyncId); outstandingQueue.head = immediate = immediate._idleNext; } emitAfter(asyncId); } if (queue === outstandingQueue) { outstandingQueue.head = null; } immediateInfo[kImmHasOutstanding] = 0; } // --------------------------------------------------------------------------- // NextTick queue // // Closely mirrors Node.js lib/internal/process/task_queues.js. // The queue and drain loop live here in core; Node-specific concerns // (validation, async hooks init, exit check) stay in ext/node/. // --------------------------------------------------------------------------- const queue = new FixedQueue(); // Async hook emit functions. Default to no-ops; ext/node/ replaces them // at bootstrap via setAsyncHooksEmit() with the real implementations // from async_hooks.ts (emitBefore, emitAfter, emitDestroy). let emitBefore = (_asyncId, _triggerAsyncId, _resource) => {}; let emitAfter = (_asyncId) => {}; let emitDestroy = (_asyncId) => {}; function setAsyncHooksEmit(before, after, destroy) { emitBefore = before; emitAfter = after; emitDestroy = destroy; } // Shared buffer with Rust - avoids JS-to-Rust op calls for tick scheduling. // Index 0: hasTickScheduled // Index 1: hasRejectionToWarn (set by Rust in promise_reject_callback) // Set by Rust during store_js_callbacks via Deno.core.__tickInfo. const kHasTickScheduled = 0; const kHasRejectionToWarn = 1; let tickInfo; function hasTickScheduled() { return tickInfo[kHasTickScheduled] === 1; } function hasRejectionToWarn() { return tickInfo[kHasRejectionToWarn] === 1; } function setHasRejectionToWarn(value) { tickInfo[kHasRejectionToWarn] = value ? 1 : 0; } function setHasTickScheduled(value) { tickInfo[kHasTickScheduled] = value ? 1 : 0; } // Enqueue a tick object. The object must have { callback, args } and // an async context snapshot. It may contain additional fields for // async hooks (asyncId, triggerAsyncId). function queueNextTick(tickObject) { if (queue.isEmpty()) { setHasTickScheduled(true); } queue.push(tickObject); } // Drain pending promise rejections from the Rust-side queue and process // them through the unhandledPromiseRejectionHandler. Returns true if any // rejections were processed (matching Node.js processPromiseRejections). function processPromiseRejections() { tickInfo[kHasRejectionToWarn] = 0; const rejections = op_drain_pending_rejections(); if (rejections === undefined) { return false; } for (let i = 0; i < rejections.length; i += 3) { const prevContext = getAsyncContext(); setAsyncContext(rejections[i + 2]); try { const handled = unhandledPromiseRejectionHandler( rejections[i], rejections[i + 1], ); if (!handled) { const err = rejections[i + 1]; op_dispatch_exception(err, true); } } finally { setAsyncContext(prevContext); } } return true; } // Matches Node.js processTicksAndRejections() from // lib/internal/process/task_queues.js function processTicksAndRejections() { let tock; do { // deno-lint-ignore no-cond-assign while ((tock = queue.shift()) !== null) { const oldContext = getAsyncContext(); setAsyncContext(tock.snapshot); const asyncId = tock.asyncId; emitBefore(asyncId, tock.triggerAsyncId, tock); try { const callback = tock.callback; if (tock.args === undefined) { callback(); } else { const args = tock.args; switch (args.length) { case 1: callback(args[0]); break; case 2: callback(args[0], args[1]); break; case 3: callback(args[0], args[1], args[2]); break; case 4: callback(args[0], args[1], args[2], args[3]); break; default: callback(...args); } } emitAfter(asyncId); } catch (e) { // In Node.js, errors propagate from JS to C++ (TryCatch in // node_task_queue.cc InternalCallbackScope::Close), which calls // TriggerUncaughtException and then re-enters the drain loop. // We approximate this by catching here and routing through // reportExceptionCallback (which triggers uncaughtException), // then continuing the drain loop. reportExceptionCallback(e); } finally { emitDestroy(asyncId); } setAsyncContext(oldContext); } op_run_microtasks(); } while (!queue.isEmpty() || processPromiseRejections()); setHasTickScheduled(false); setHasRejectionToWarn(false); } // Flush microtasks and drain the nextTick queue if work is pending. // Under Explicit microtask policy, microtasks (promise continuations) // don't run automatically. We flush them here so that any ticks they // schedule are discovered and drained in the same iteration. // // IMPORTANT: When ticks are already scheduled, we skip the microtask // flush and go straight to processTicksAndRejections, which drains // ticks BEFORE running microtasks. This preserves the Node.js // invariant that nextTick callbacks fire before Promise.then // continuations in the same event loop phase. // // This is the single drain function used by: __eventLoopTick (from // Rust), __drainNextTickAndMacrotasks (I/O tight loop), runNextTicks // (interleaved between timer/immediate callbacks), and runImmediates. function drainTicks() { if (!hasTickScheduled() && !hasRejectionToWarn()) { op_run_microtasks(); if (!hasTickScheduled() && !hasRejectionToWarn()) { return; } } processTicksAndRejections(); } // Alias for timer/immediate interleaving (matches Node.js name). const runNextTicks = drainTicks; // Wire runNextTicks into the timer module so processTimers can // interleave nextTick drains between timer callbacks. __timers.setRunNextTicks(runNextTicks); // Wire reportException so timer callback errors are dispatched // via the uncaught exception handler rather than propagating. // Use a wrapper since reportExceptionCallback is defined later. __timers.setReportException((e) => reportExceptionCallback(e)); // Called from Rust when the native user timer fires. function __processTimers(now) { return __timers.processTimers(now); } // Resolve completed async ops. // Called from Rust with args: (promiseId, isOk, res, ...) // // NOTE: This does NOT drain ticks. Under Explicit microtask policy, // microtasks from op resolution are deferred. Rust calls // __drainNextTickAndMacrotasks separately after this returns, // with the correct microtask checkpoint ordering to preserve // the nextTick-before-then invariant. function __eventLoopTick() { for (let i = 0; i < arguments.length; i += 3) { const promiseId = arguments[i]; const isOk = arguments[i + 1]; const res = arguments[i + 2]; __resolvePromise(promiseId, res, isOk); } } // Drain nextTick/microtask queues only (no timers or ops). // Used in the I/O tight loop and when ticks are pending without ops. function __drainNextTickAndMacrotasks() { drainTicks(); } // Phase 2: Handle unhandled promise rejections. // Called from Rust with a flat array: [promise, reason, context, promise, reason, context, ...] function __handleRejections() { for (let i = 0; i < arguments.length; i += 3) { // Restore the async context that was active when the promise was // rejected, so that AsyncLocalStorage.getStore() works correctly // inside unhandledrejection handlers (matching Node.js behavior). const prevContext = getAsyncContext(); setAsyncContext(arguments[i + 2]); try { const handled = unhandledPromiseRejectionHandler( arguments[i], arguments[i + 1], ); if (!handled) { const err = arguments[i + 1]; op_dispatch_exception(err, true); } } finally { setAsyncContext(prevContext); } } } // Report an exception (called from Rust for timer callback errors). function __reportException(e) { reportExceptionCallback(e); } function runImmediateCallbacks() { try { runImmediates(); } catch (e) { reportExceptionCallback(e); } } function refOp(promiseId) { if (!hasPromise(promiseId)) { return; } op_ref_op(promiseId); } function unrefOp(promiseId) { if (!hasPromise(promiseId)) { return; } op_unref_op(promiseId); } function refOpPromise(promise) { refOp(promise[promiseIdSymbol]); } function unrefOpPromise(promise) { unrefOp(promise[promiseIdSymbol]); } function resources() { return ObjectFromEntries(op_resources()); } let reportExceptionCallback = (error) => { op_dispatch_exception(error, false); }; // Used to report errors thrown from functions passed to `queueMicrotask()`. // The callback will be passed the thrown error. For example, you can use this // to dispatch an error event to the global scope. // In other words, set the implementation for // https://html.spec.whatwg.org/multipage/webappapis.html#report-the-exception function setReportExceptionCallback(cb) { if (cb === null || cb === undefined) { reportExceptionCallback = (error) => { op_dispatch_exception(error, false); }; } else { if (typeof cb != "function") { throw new TypeError("expected a function"); } reportExceptionCallback = cb; } } // V8 installs a native `queueMicrotask` on the global object (enabled via the // `--enable-queue-microtask` flag). We wrap it below to route uncaught // exceptions through `reportExceptionCallback`, so we grab a reference to the // native implementation here - it lets us enqueue microtasks without crossing // the op boundary. // // V8 skips experimental globals while snapshotting, so the native function is // only installed at runtime. In a from-scratch runtime it's already on the // global when this module runs (before our wrapper replaces it below), so we // can grab it directly. When restoring from a snapshot it isn't there yet, so // fall back to a lazy capture on first use: our wrapper shadows the native one // as an own property of the global, so momentarily remove our property to // reveal the native one underneath, then restore our wrapper. let nativeQueueMicrotask = window.queueMicrotask; function captureNativeQueueMicrotask() { const wrapper = window.queueMicrotask; delete window.queueMicrotask; nativeQueueMicrotask = window.queueMicrotask; window.queueMicrotask = wrapper; return nativeQueueMicrotask; } function queueMicrotask(cb) { if (typeof cb != "function") { throw new TypeError("expected a function"); } const enqueue = nativeQueueMicrotask ?? captureNativeQueueMicrotask(); return enqueue(() => { try { cb(); } catch (error) { reportExceptionCallback(error); } }); } // Some "extensions" rely on "BadResource", "Interrupted", "NotCapable" // errors in the JS code (eg. "deno_net") so they are provided in "Deno.core" // but later reexported on "Deno.errors" class BadResource extends Error { constructor(msg, options) { super(msg, options); this.name = "BadResource"; } } const BadResourcePrototype = BadResource.prototype; class Interrupted extends Error { constructor(msg, options) { super(msg, options); this.name = "Interrupted"; } } const InterruptedPrototype = Interrupted.prototype; class NotCapable extends Error { constructor(msg, options) { super(msg, options); this.name = "NotCapable"; } } const NotCapablePrototype = NotCapable.prototype; registerErrorClass("BadResource", BadResource); registerErrorClass("Interrupted", Interrupted); registerErrorClass("NotCapable", NotCapable); const promiseHooks = [ [], // init [], // before [], // after [], // resolve ]; function setPromiseHooks(init, before, after, resolve) { const hooks = [init, before, after, resolve]; for (let i = 0; i < hooks.length; i++) { const hook = hooks[i]; // Skip if no callback was provided for this hook type. if (hook == null) { continue; } // Verify that the type of `hook` is a function. if (typeof hook !== "function") { throw new TypeError(`Expected function at position ${i}`); } // Add the hook to the list. ArrayPrototypePush(promiseHooks[i], hook); } const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => { switch (hooks.length) { case 0: return undefined; case 1: return hooks[0]; case 2: return create2xHookWrapper(hooks[0], hooks[1]); case 3: return create3xHookWrapper(hooks[0], hooks[1], hooks[2]); default: return createHookListWrapper(hooks); } // The following functions are used to create wrapper functions that call // all the hooks in a list of a certain length. The reason to use a // function that creates a wrapper is to minimize the number of objects // captured in the closure. function create2xHookWrapper(hook1, hook2) { return function (promise, parent) { hook1(promise, parent); hook2(promise, parent); }; } function create3xHookWrapper(hook1, hook2, hook3) { return function (promise, parent) { hook1(promise, parent); hook2(promise, parent); hook3(promise, parent); }; } function createHookListWrapper(hooks) { return function (promise, parent) { for (let i = 0; i < hooks.length; i++) { const hook = hooks[i]; hook(promise, parent); } }; } }); op_set_promise_hooks( wrappedHooks[0], wrappedHooks[1], wrappedHooks[2], wrappedHooks[3], ); } const { op_close: close, op_try_close: tryClose, op_cancel_read: cancelRead, op_read: read, op_read_all: readAll, op_write: write, op_write_all: writeAll, op_write_type_error: writeTypeError, op_read_sync: readSync, op_write_sync: writeSync, op_shutdown: shutdown, op_is_terminal: isTerminal, } = ops; const callSiteRetBuf = new Uint32Array(2); const callSiteRetBufU8 = new Uint8Array(callSiteRetBuf.buffer); function currentUserCallSite() { const fileName = op_current_user_call_site(callSiteRetBufU8); const lineNumber = callSiteRetBuf[0]; const columnNumber = callSiteRetBuf[1]; return { fileName, lineNumber, columnNumber }; } const hostObjectBrand = SymbolFor("Deno.core.hostObject"); const transferableResources = {}; const registerTransferableResource = (name, send, receive) => { if (transferableResources[name]) { throw new Error(`${name} is already registered`); } transferableResources[name] = { send, receive }; }; const getTransferableResource = (name) => transferableResources[name]; const cloneableDeserializers = { __proto__: null }; const registerCloneableResource = (name, deserialize) => { if (cloneableDeserializers[name]) { throw new Error(`${name} is already registered`); } cloneableDeserializers[name] = deserialize; }; const getCloneableDeserializers = () => cloneableDeserializers; // A helper function that will bind our own console implementation // with default implementation of Console from V8. This will cause // console messages to be piped to inspector console. // // We are using `Deno.core.callConsole` binding to preserve proper stack // frames in inspector console. This has to be done because V8 considers // the last JS stack frame as gospel for the inspector. In our case we // specifically want the latest user stack frame to be the one that matters // though. // // Inspired by: // https://github.com/nodejs/node/blob/1317252dfe8824fd9cfee125d2aaa94004db2f3b/lib/internal/util/inspector.js#L39-L61 function wrapConsole(customConsole, consoleFromV8) { const callConsole = window.Deno.core.callConsole; const keys = ObjectKeys(consoleFromV8); for (let i = 0; i < keys.length; ++i) { const key = keys[i]; if (ObjectHasOwn(customConsole, key)) { customConsole[key] = FunctionPrototypeBind( callConsole, customConsole, consoleFromV8[key], customConsole[key], ); // Restore the original method name clobbered by the bind above // ("bound callConsole"), like Node does in its `wrapConsole`. ObjectDefineProperty(customConsole[key], "name", { __proto__: null, value: key, }); } else { // Add additional console APIs from the inspector customConsole[key] = consoleFromV8[key]; } } } // Minimal console implementation, that uses `Deno.core.print` under the hood. // It's not fully fledged and is meant to make debugging slightly easier when working with // only `deno_core` crate. class CoreConsole { log = (...args) => { op_print(`${consoleStringify(...args)}\n`, false); }; debug = (...args) => { op_print(`${consoleStringify(...args)}\n`, false); }; warn = (...args) => { op_print(`${consoleStringify(...args)}\n`, false); }; error = (...args) => { op_print(`${consoleStringify(...args)}\n`, false); }; } // Default impl of contextual logging op_get_ext_import_meta_proto().log = function internalLog(level, ...args) { console.error(`[${level.toUpperCase()}]`, ...args); }; const consoleStringify = (...args) => args.map(consoleStringifyArg).join(" "); const consoleStringifyArg = (arg) => { if ( typeof arg === "string" || typeof arg === "boolean" || typeof arg === "number" || arg === null || arg === undefined ) { return arg; } const tag = TypedArrayPrototypeGetSymbolToStringTag(arg); if (op_is_typed_array(arg)) { return `${tag}(${TypedArrayPrototypeGetLength(arg)}) [${ TypedArrayPrototypeJoin(TypedArrayPrototypeSlice(arg, 0, 10), ", ") }]`; } if (tag !== undefined) { tag + " " + JSON.stringify(arg, undefined, 2); } else { return JSON.stringify(arg, undefined, 2); } }; const v8Console = globalThis.console; const coreConsole = new CoreConsole(); globalThis.console = coreConsole; wrapConsole(coreConsole, v8Console); function propWritable(value) { return { value, writable: true, enumerable: true, configurable: true, }; } function propNonEnumerable(value) { return { value, writable: true, enumerable: false, configurable: true, }; } function propReadOnly(value) { return { value, enumerable: true, writable: false, configurable: true, }; } function propGetterOnly(getter) { return { get: getter, set() {}, enumerable: true, configurable: true, }; } // Marker symbol identifying a lazy-loaded property descriptor. The property // name is filled in by `defineGlobalProperties` at install time so the // setter can mimic data-property [[Set]] semantics (define an own data // property on the receiver) instead of mutating a closure shared across all // receivers - see denoland/deno#34403. const lazyNameSym = Symbol("lazyName"); function propWritableLazyLoaded(getter, loadFn) { const desc = { get() { return getter(loadFn()); }, set(v) { ObjectDefineProperty(this, desc[lazyNameSym], { value: v, writable: true, enumerable: true, configurable: true, }); }, enumerable: true, configurable: true, [lazyNameSym]: undefined, }; return desc; } function propNonEnumerableLazyLoaded(getter, loadFn) { const desc = { get() { return getter(loadFn()); }, set(v) { const name = desc[lazyNameSym]; // Match OrdinarySetWithOwnDescriptor semantics for an inherited // writable data property: a direct set on the holder updates the // value in place (preserving enumerable: false), while an inherited // set creates a new enumerable own data property on the receiver. if (ObjectHasOwn(this, name)) { ObjectDefineProperty(this, name, { value: v, writable: true, enumerable: false, configurable: true, }); } else { ObjectDefineProperty(this, name, { value: v, writable: true, enumerable: true, configurable: true, }); } }, enumerable: false, configurable: true, [lazyNameSym]: undefined, }; return desc; } // Like `Object.defineProperties`, but also stamps the property name into any // lazy-loaded descriptors so their setters can target the correct receiver. function defineGlobalProperties(target, props) { const keys = ObjectKeys(props); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const desc = props[key]; if (desc !== null && typeof desc === "object" && lazyNameSym in desc) { desc[lazyNameSym] = key; } } ObjectDefineProperties(target, props); } function createLazyLoader(specifier) { let value; return function lazyLoad() { if (!value) { value = op_lazy_load_esm(specifier); } return value; }; } const loadedScripts = { __proto__: null }; // Note: the Rust side of `op_load_ext_script` (in `libs/core/modules/map.rs`) // temporarily reinstalls a captured snapshot-time view of `__bootstrap` // on `globalThis` for the duration of each script evaluation if // `__bootstrap` isn't already on the global. Every `lazy_loaded_js` // polyfill's IIFE preamble destructures `globalThis.__bootstrap` and // `__bootstrap.core.ops`, but at runtime (`runtime/js/99_main.js`) the // harness deletes `globalThis.__bootstrap` and `removeImportedOps()` // strips most entries out of `Deno.core.ops`. The captured view (a // shallow clone of `core.ops` immune to `removeImportedOps`) is // registered via `op_set_captured_bootstrap` at the end of this IIFE. // Doing the install in Rust means the `synthetic_esm` dispatch path // (which calls `load_ext_script` directly without going through this // wrapper) gets the same treatment without leaving `__bootstrap` // permanently on the global (where user code can observe it via // `Object.keys`). function loadExtScript(specifier) { if (specifier in loadedScripts) { return loadedScripts[specifier]; } const result = op_load_ext_script(specifier); loadedScripts[specifier] = result; return result; } const getAsyncContext = getContinuationPreservedEmbedderData; const setAsyncContext = setContinuationPreservedEmbedderData; const kNoAsyncContextRestore = Symbol("Deno.core.noAsyncContextRestore"); function scopeAsyncContext(ctx) { const old = getAsyncContext(); setAsyncContext(ctx); return { __proto__: null, [Symbol.dispose]() { setAsyncContext(old); }, }; } let asyncVariableCounter = 0; class AsyncVariable { #id = asyncVariableCounter++; #data = new SafeWeakMap(); enter(value) { const previousContextMapping = getAsyncContext(); this.#enterWithPreviousContext(value, previousContextMapping); return previousContextMapping; } enterIfActive(value) { const previousContextMapping = getAsyncContext(); if ( previousContextMapping === null || previousContextMapping === undefined || ObjectKeys(previousContextMapping).length === 0 ) { return kNoAsyncContextRestore; } this.#enterWithPreviousContext(value, previousContextMapping); return previousContextMapping; } #enterWithPreviousContext(value, previousContextMapping) { const entry = { id: this.#id }; const asyncContextMapping = { __proto__: null, ...previousContextMapping, [this.#id]: entry, }; this.#data.set(entry, value); setAsyncContext(asyncContextMapping); } get() { const current = getAsyncContext(); const entry = current?.[this.#id]; if (entry) { return this.#data.get(entry); } return undefined; } } // Extra Deno.core.* exports const core = ObjectAssign(globalThis.Deno.core, { internalRidSymbol: Symbol("Deno.internal.rid"), internalFdSymbol: Symbol("Deno.internal.fd"), resources, __eventLoopTick, __processTimers, __setTickInfo(buf) { tickInfo = buf; }, __setImmediateInfo(buf) { immediateInfo = buf; }, __drainNextTickAndMacrotasks, __handleRejections, __reportException, __setTimerInfo: __timers.__setTimerInfo, immediateRefCount(increase) { if (increase) { if (immediateInfo[kImmRefCount] === 0) { op_immediate_check(true); } immediateInfo[kImmRefCount]++; } else { immediateInfo[kImmRefCount]--; if (immediateInfo[kImmRefCount] === 0) { op_immediate_check(false); } } }, runImmediateCallbacks, BadResource, BadResourcePrototype, Interrupted, InterruptedPrototype, NotCapable, NotCapablePrototype, refOpPromise, unrefOpPromise, setReportExceptionCallback, setPromiseHooks, consoleStringify, close, tryClose, cancelRead, read, readAll, write, writeAll, writeTypeError, readSync, writeSync, shutdown, isTerminal, print: (msg, isErr) => op_print(msg, isErr), setLeakTracingEnabled: (enabled) => { __setLeakTracingEnabled(enabled); op_leak_tracing_enable(enabled); }, isLeakTracingEnabled: () => __isLeakTracingEnabled(), getAllLeakTraces: () => { const traces = op_leak_tracing_get_all(); return new SafeMap(traces); }, getLeakTraceForPromise: (promise) => op_leak_tracing_get(0, promise[promiseIdSymbol]), queueNextTick, processTicksAndRejections, runNextTicks, setAsyncHooksEmit, queueImmediate, clearImmediate, runImmediates, immediateQueue, getActiveImmediateCount() { return immediateInfo[kImmRefCount]; }, kRefed, runMicrotasks: () => op_run_microtasks(), hasTickScheduled, setHasTickScheduled, compileFunction: ( source, specifier, hostDefinedOptions, params, ) => { const [result, error] = op_compile_function( source, specifier, hostDefinedOptions, params, ); if (error) { const { 0: thrown, 1: isNativeError, 2: isCompileError } = error; return [ result, { thrown, isNativeError, isCompileError, }, ]; } return [result, null]; }, evalContext: ( source, specifier, hostDefinedOptions, ) => { const [result, error] = op_eval_context( source, specifier, hostDefinedOptions, ); if (error) { const { 0: thrown, 1: isNativeError, 2: isCompileError } = error; return [ result, { thrown, isNativeError, isCompileError, }, ]; } return [result, null]; }, hostObjectBrand, registerTransferableResource, getTransferableResource, registerCloneableResource, getCloneableDeserializers, encode: (text) => op_encode(text), encodeBinaryString: (buffer) => op_encode_binary_string(buffer), decode: (buffer) => op_decode(buffer), structuredClone: (value, deserializers) => op_structured_clone(value, deserializers ?? cloneableDeserializers), serialize: ( value, options, errorCallback, ) => { return op_serialize( value, options?.hostObjects, options?.transferredArrayBuffers, options?.forStorage ?? false, errorCallback, ); }, deserialize: (buffer, options) => { return op_deserialize( buffer, options?.hostObjects, options?.transferredArrayBuffers, options?.deserializers, options?.forStorage ?? false, ); }, getPromiseDetails: (promise) => op_get_promise_details(promise), getProxyDetails: (proxy) => op_get_proxy_details(proxy), isAnyArrayBuffer: (value) => op_is_any_array_buffer(value), isArgumentsObject: (value) => op_is_arguments_object(value), isArrayBuffer: (value) => op_is_array_buffer(value), isArrayBufferView: (value) => op_is_array_buffer_view(value), isAsyncFunction: (value) => op_is_async_function(value), isBigIntObject: (value) => op_is_big_int_object(value), isBooleanObject: (value) => op_is_boolean_object(value), isBoxedPrimitive: (value) => op_is_boxed_primitive(value), isDataView: (value) => op_is_data_view(value), isDate: (value) => op_is_date(value), isGeneratorFunction: (value) => op_is_generator_function(value), isGeneratorObject: (value) => op_is_generator_object(value), isMap: (value) => op_is_map(value), isMapIterator: (value) => op_is_map_iterator(value), isModuleNamespaceObject: (value) => op_is_module_namespace_object(value), isNativeError: (value) => op_is_native_error(value), isNumberObject: (value) => op_is_number_object(value), isPromise: (value) => op_is_promise(value), isProxy: (value) => op_is_proxy(value), isRegExp: (value) => op_is_reg_exp(value), isSet: (value) => op_is_set(value), isSetIterator: (value) => op_is_set_iterator(value), isSharedArrayBuffer: (value) => op_is_shared_array_buffer(value), isStringObject: (value) => op_is_string_object(value), isSymbolObject: (value) => op_is_symbol_object(value), isTypedArray: (value) => op_is_typed_array(value), isWeakMap: (value) => op_is_weak_map(value), isWeakSet: (value) => op_is_weak_set(value), memoryUsage: () => op_memory_usage(), setWasmStreamingCallback: (fn) => op_set_wasm_streaming_callback(fn), abortWasmStreaming: ( rid, error, ) => op_abort_wasm_streaming(rid, error), destructureError: (error) => op_destructure_error(error), opNames: () => op_op_names(), eventLoopHasMoreWork: () => op_event_loop_has_more_work(), byteLength: (str) => op_str_byte_length(str), addMainModuleHandler: (handler) => op_add_main_module_handler(handler), setHandledPromiseRejectionHandler: (handler) => op_set_handled_promise_rejection_handler(handler), setUnhandledPromiseRejectionHandler: (handler) => unhandledPromiseRejectionHandler = handler, reportUnhandledException: (e) => op_dispatch_exception(e, false), reportUnhandledPromiseRejection: (e) => op_dispatch_exception(e, true), createTimer: __timers.createTimer, cancelTimer: __timers.cancelTimer, refreshTimer: __timers.refreshTimer, refTimer: __timers.refTimer, unrefTimer: __timers.unrefTimer, // System timers bypass Deno's test sanitizer resource tracking. createSystemTimer: (callback, after, isRefed) => __timers.createTimer(callback, after, undefined, false, !!isRefed, true), createSystemInterval: (callback, interval, isRefed) => __timers.createTimer( callback, interval, undefined, true, !!isRefed, true, ), currentUserCallSite, wrapConsole, v8Console, propReadOnly, propWritable, propNonEnumerable, propGetterOnly, propWritableLazyLoaded, propNonEnumerableLazyLoaded, defineGlobalProperties, createLazyLoader, loadExtScript, createCancelHandle: () => op_cancel_handle(), getAsyncContext, setAsyncContext, scopeAsyncContext, AsyncVariable, kNoAsyncContextRestore, }); const internals = {}; ObjectAssign(globalThis.__bootstrap, { core, internals }); ObjectAssign(globalThis.Deno, { core }); ObjectFreeze(globalThis.__bootstrap.core); // Build the snapshot-time view of __bootstrap and hand it off to Rust. // The Rust `load_ext_script` (libs/core/modules/map.rs) temporarily // installs this on `globalThis.__bootstrap` for each script evaluation // when the live `__bootstrap` has already been deleted (i.e. after // runtime bootstrap). See the comment above `loadExtScript` earlier in // this file for context. const capturedCore = ObjectAssign({ __proto__: null }, core); capturedCore.ops = ObjectAssign({ __proto__: null }, core.ops); const capturedBootstrap = { __proto__: null, primordials: globalThis.__bootstrap.primordials, core: capturedCore, internals, }; op_set_captured_bootstrap(capturedBootstrap); // Direct bindings on `globalThis` ObjectAssign(globalThis, { queueMicrotask }); setQueueMicrotask(queueMicrotask); })(globalThis);