/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
runtime/js/99_main.js
1 360 строк
44 KB
Tomas Zijdemans
perf(ext/web): apply the base64url op design to standard base64 (#36422)
10 авг 2026, 17:26
Не верифицирован
10 авг 2026, 17:26
ca568b1
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Remove Intl.v8BreakIterator because it is a non-standard API. delete Intl.v8BreakIterator; const internalConsole = core.loadExtScript("ext:deno_web/01_console.js"); import { core, internals, primordials } from "ext:core/mod.js"; const ops = core.ops; import { op_bootstrap_args, op_bootstrap_is_from_unconfigured_runtime, op_bootstrap_no_color, op_bootstrap_pid, op_bootstrap_stderr_no_color, op_bootstrap_stdout_no_color, op_get_ext_import_meta_proto, op_internal_log, op_main_module, op_node_has_child_ipc_pipe, op_ppid, op_proto_get_attempted, op_proto_set_attempted, op_set_format_exception_callback, op_snapshot_options, op_worker_close, op_worker_get_type, op_worker_maybe_wait_for_debugger, op_worker_post_message, op_worker_post_message_raw, op_worker_recv_message, op_worker_recv_message_sync, op_worker_sync_fetch, } from "ext:core/ops"; const { ArrayPrototypeFilter, ArrayPrototypeIncludes, ArrayPrototypeMap, Error, ErrorPrototype, FunctionPrototypeBind, ObjectAssign, ObjectDefineProperties, ObjectDefineProperty, ObjectFreeze, ObjectGetOwnPropertyDescriptors, ObjectHasOwn, ObjectIsExtensible, ObjectKeys, ObjectPrototype, ObjectPrototypeIsPrototypeOf, ObjectSetPrototypeOf, Promise, PromisePrototypeThen, PromiseResolve, queueMicrotask, ReflectApply, StringPrototypePadEnd, Symbol, SymbolDispose, SymbolIterator, TypeError, } = primordials; const { isNativeError, } = core; // Deno.serve (00_serve.ts) chains through 23_request/23_response/22_body // into the 208 KB web-streams polyfill. Only loaded if `deno serve` / a // declarative server export is actually used. let _serveMod; const lazyServeMod = () => _serveMod ?? (_serveMod = core.loadExtScript("ext:deno_http/00_serve.ts")); const event = core.loadExtScript("ext:deno_web/02_event.js"); const location = core.loadExtScript("ext:deno_web/12_location.js"); const version = core.loadExtScript("ext:runtime/01_version.ts"); const os = core.loadExtScript("ext:deno_os/30_os.js"); const { getConsoleInspectOptions, getDefaultInspectOptions, getStderrNoColor, inspectArgs, quoteString, setNoColorFns, } = core.loadExtScript("ext:deno_web/01_console.js"); const performance = core.loadExtScript("ext:deno_web/15_performance.js"); const url = core.loadExtScript("ext:deno_web/00_url.js"); // 26_fetch pulls 22_body -> 06_streams (208 KB). The only thing 99_main // needs from it at bootstrap is the wasm-streaming callback registration - // wrap that so the actual module loads on first WebAssembly streaming use. let _fetchMod; const lazyFetchMod = () => _fetchMod ?? (_fetchMod = core.loadExtScript("ext:deno_fetch/26_fetch.js")); const messagePort = core.loadExtScript("ext:deno_web/13_message_port.js"); import { denoNs, denoNsUnstableById, unstableIds, } from "ext:runtime/90_deno_ns.js"; const { errors } = core.loadExtScript("ext:runtime/01_errors.js"); const webidl = core.loadExtScript("ext:deno_webidl/00_webidl.js"); const { DOMException, QuotaExceededError, } = core.loadExtScript("ext:deno_web/01_dom_exception.js"); import { unstableForWindowOrWorkerGlobalScope, windowOrWorkerGlobalScope, } from "ext:runtime/98_global_scope_shared.js"; import { mainRuntimeGlobalProperties, memoizeLazy, } from "ext:runtime/98_global_scope_window.js"; import { workerRuntimeGlobalProperties, } from "ext:runtime/98_global_scope_worker.js"; const { SymbolMetadata } = core.loadExtScript("ext:deno_web/00_infra.js"); // Telemetry (~2000 LOC, ~70 KB of bytecode + V8 heap) is only needed when an // OTEL config flag is set, but it was being loaded unconditionally at snapshot // build time. Skip the load on the cold-start path; only enter the module // when the bootstrap config actually asks for telemetry. // // Layout of `otelConfig` (see OtelConfig::as_v8 in ext/telemetry/lib.rs): // [0] tracing_enabled (0/1) // [1] metrics_enabled (0/1) // [2] console mode (OtelConsoleConfig: 0 = Ignore, 1 = Capture, 2 = Replace) // [3..] zero or more propagator ids; if no propagator is configured the // array ends at length 3. function bootstrapOtel(otelConfig) { if ( otelConfig[0] === 0 && otelConfig[1] === 0 && otelConfig[2] === 0 && otelConfig.length <= 3 ) { return; } const { bootstrap } = core.loadExtScript("ext:deno_telemetry/telemetry.ts"); bootstrap(otelConfig); } // deno-lint-ignore deno-internal/prefer-primordials if (Symbol.metadata) { throw "V8 supports Symbol.metadata now, no need to shim it"; } ObjectDefineProperties(Symbol, { dispose: { __proto__: null, value: SymbolDispose, enumerable: false, writable: false, configurable: false, }, metadata: { __proto__: null, value: SymbolMetadata, enumerable: false, writable: false, configurable: false, }, }); internals.isFromUnconfiguredRuntime = op_bootstrap_is_from_unconfigured_runtime; // https://docs.rs/log/latest/log/enum.Level.html const LOG_LEVELS = { error: 1, warn: 2, info: 3, debug: 4, trace: 5, }; op_get_ext_import_meta_proto().log = function internalLog(levelStr, ...args) { const level = LOG_LEVELS[levelStr]; const message = inspectArgs( args, getConsoleInspectOptions(getStderrNoColor()), ); op_internal_log(this.url, level, message); }; // Equivalent of import.meta.log for use in lazy-loaded (IIFE) scripts that // lack access to import.meta. internals.log = function internalLog(levelStr, ...args) { const level = LOG_LEVELS[levelStr]; const message = inspectArgs( args, getConsoleInspectOptions(getStderrNoColor()), ); op_internal_log("ext:runtime", level, message); }; let windowIsClosing = false; let globalThis_; function windowClose() { if (!windowIsClosing) { windowIsClosing = true; // Push a macrotask to exit after a promise resolve. // This is not perfect, but should be fine for first pass. PromisePrototypeThen( PromiseResolve(), () => core.createSystemTimer( () => { // This should be fine, since only Window/MainWorker has .close() os.exit(0); }, 0, true, ), ); } } function workerClose() { if (isClosing) { return; } isClosing = true; op_worker_close(); } function postMessage(message, transferOrOptions = { __proto__: null }) { const prefix = "Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope'"; webidl.requiredArguments(arguments.length, 1, prefix); // Fast path: no transferables if ( transferOrOptions === undefined || transferOrOptions === null || (arguments.length <= 1) ) { op_worker_post_message_raw( messagePort.serializeMessageData(message, (err) => { throw new DOMException(err, "DataCloneError"); }), ); return; } message = webidl.converters.any(message); let options; if ( webidl.type(transferOrOptions) === "Object" && transferOrOptions !== undefined && transferOrOptions[SymbolIterator] !== undefined ) { const transfer = webidl.converters["sequence<object>"]( transferOrOptions, prefix, "Argument 2", ); options = { transfer }; } else { options = webidl.converters.StructuredSerializeOptions( transferOrOptions, prefix, "Argument 2", ); } const { transfer } = options; const data = messagePort.serializeJsMessageData(message, transfer); op_worker_post_message(data); } let isClosing = false; let globalDispatchEvent; let closeOnIdle; function hasMessageEventListener() { // the function name is kind of a misnomer, but we want to behave // as if we have message event listeners if a node message port is explicitly // refed (and the inverse as well) return (event.listenerCount(globalThis, "message") > 0 && !globalThis[messagePort.unrefParentPort]) || messagePort.refedMessagePortsCount > 0; } function dispatchWorkerMessage(data) { let message, transferables; try { const v = messagePort.deserializeJsMessageData(data); message = v[0]; transferables = v[1]; } catch (err) { const errorEvent = new event.MessageEvent("messageerror", { cancelable: false, data: err, }); event.setIsTrusted(errorEvent, true); globalDispatchEvent(errorEvent); return; } const msgEvent = new event.MessageEvent("message", { cancelable: false, data: message, // Skip the transferables filter for the common no-transferables case. // Passing `undefined` lets the MessageEvent constructor take its cheap // `ports == null` branch (a single frozen empty array, no iterator // validation) instead of allocating a filtered array per message. ports: transferables.length === 0 ? undefined : ArrayPrototypeFilter( transferables, (t) => ObjectPrototypeIsPrototypeOf(messagePort.MessagePortPrototype, t), ), }); event.setIsTrusted(msgEvent, true); try { globalDispatchEvent(msgEvent); } catch (e) { const errorEvent = new event.ErrorEvent("error", { cancelable: true, message: e.message, lineno: e.lineNumber ? e.lineNumber + 1 : undefined, colno: e.columnNumber ? e.columnNumber + 1 : undefined, filename: e.fileName, error: e, }); event.setIsTrusted(errorEvent, true); globalDispatchEvent(errorEvent); if (!errorEvent.defaultPrevented) { throw e; } } } async function pollForMessages() { if (!globalDispatchEvent) { globalDispatchEvent = FunctionPrototypeBind( globalThis.dispatchEvent, globalThis, ); } while (!isClosing) { const recvMessage = op_worker_recv_message(); // In a Node.js worker, unref() the op promise to prevent it from // keeping the event loop alive. This avoids the need to explicitly // call self.close() or worker.terminate(). if (closeOnIdle) { core.unrefOpPromise(recvMessage); } const data = await recvMessage; if (data === null) break; op_worker_maybe_wait_for_debugger(); dispatchWorkerMessage(data); // Drain messages already queued on the host side instead of taking the // async op + Promise path for each. The whole burst is processed within // this event-loop turn; the batch limit prevents starving the event loop // under a sustained flood. for (let i = 0; i < 1000 && !isClosing; i++) { const syncData = op_worker_recv_message_sync(); if (syncData === null) break; // Each message dispatch is its own task. Yield a microtask before // delivering this already-dequeued message so a handler that re-armed // itself in a microtask after the previous dispatch (e.g. reassigning // `onmessage` inside a `.then`) is installed first -- otherwise the // message reaches the stale handler and is lost. A synchronous // checkpoint can't help: V8 won't run microtasks reentrantly while we // are already inside one. await new Promise((resolve) => queueMicrotask(() => resolve())); if (isClosing) break; op_worker_maybe_wait_for_debugger(); dispatchWorkerMessage(syncData); } } } let loadedMainWorkerScript = false; function importScripts(...urls) { if (op_worker_get_type() !== "classic") { throw new TypeError("Cannot import scripts in a module worker"); } const baseUrl = location.getLocationHref(); const parsedUrls = ArrayPrototypeMap(urls, (scriptUrl) => { try { return new url.URL(scriptUrl, baseUrl ?? undefined).href; } catch { throw new DOMException( `Failed to parse URL: ${scriptUrl}`, "SyntaxError", ); } }); // A classic worker's main script has looser MIME type checks than any // imported scripts, so we use `loadedMainWorkerScript` to distinguish them. // TODO(andreubotella) Refactor worker creation so the main script isn't // loaded with `importScripts()`. const scripts = op_worker_sync_fetch( parsedUrls, !loadedMainWorkerScript, ); loadedMainWorkerScript = true; for (let i = 0; i < scripts.length; ++i) { const { url, script } = scripts[i]; const err = core.evalContext(script, url)[1]; if (err !== null) { throw err.thrown; } } } const opArgs = memoizeLazy(() => op_bootstrap_args()); const opPid = memoizeLazy(() => op_bootstrap_pid()); setNoColorFns( () => op_bootstrap_stdout_no_color(), () => op_bootstrap_stderr_no_color(), ); function formatException(error) { if ( isNativeError(error) || ObjectPrototypeIsPrototypeOf(ErrorPrototype, error) ) { return null; } else if (typeof error == "string") { return `Uncaught ${ inspectArgs([quoteString(error, getDefaultInspectOptions())], { colors: !getStderrNoColor(), }) }`; } else { return `Uncaught ${inspectArgs([error], { colors: !getStderrNoColor() })}`; } } core.registerErrorClass("NotFound", errors.NotFound); core.registerErrorClass("ConnectionRefused", errors.ConnectionRefused); core.registerErrorClass("ConnectionReset", errors.ConnectionReset); core.registerErrorClass("ConnectionAborted", errors.ConnectionAborted); core.registerErrorClass("NotConnected", errors.NotConnected); core.registerErrorClass("AddrInUse", errors.AddrInUse); core.registerErrorClass("AddrNotAvailable", errors.AddrNotAvailable); core.registerErrorClass("BrokenPipe", errors.BrokenPipe); core.registerErrorClass("PermissionDenied", errors.PermissionDenied); core.registerErrorClass("AlreadyExists", errors.AlreadyExists); core.registerErrorClass("InvalidData", errors.InvalidData); core.registerErrorClass("TimedOut", errors.TimedOut); core.registerErrorClass("WouldBlock", errors.WouldBlock); core.registerErrorClass("WriteZero", errors.WriteZero); core.registerErrorClass("UnexpectedEof", errors.UnexpectedEof); core.registerErrorClass("Http", errors.Http); core.registerErrorClass("Busy", errors.Busy); core.registerErrorClass("NotSupported", errors.NotSupported); core.registerErrorClass("FilesystemLoop", errors.FilesystemLoop); core.registerErrorClass("IsADirectory", errors.IsADirectory); core.registerErrorClass("NetworkUnreachable", errors.NetworkUnreachable); core.registerErrorClass("NotADirectory", errors.NotADirectory); core.registerErrorBuilder( "DOMExceptionOperationError", function DOMExceptionOperationError(msg) { return new DOMException(msg, "OperationError"); }, ); core.registerErrorBuilder( "DOMExceptionQuotaExceededError", function DOMExceptionQuotaExceededError(msg) { return new QuotaExceededError(msg); }, ); core.registerErrorBuilder( "DOMExceptionNotSupportedError", function DOMExceptionNotSupportedError(msg) { return new DOMException(msg, "NotSupportedError"); }, ); core.registerErrorBuilder( "DOMExceptionNetworkError", function DOMExceptionNetworkError(msg) { return new DOMException(msg, "NetworkError"); }, ); core.registerErrorBuilder( "DOMExceptionAbortError", function DOMExceptionAbortError(msg) { return new DOMException(msg, "AbortError"); }, ); core.registerErrorBuilder( "DOMExceptionInvalidCharacterError", function DOMExceptionInvalidCharacterError(msg) { return new DOMException(msg, "InvalidCharacterError"); }, ); core.registerErrorBuilder( "DOMExceptionDataError", function DOMExceptionDataError(msg) { return new DOMException(msg, "DataError"); }, ); core.registerErrorBuilder( "DOMExceptionInvalidStateError", function DOMExceptionInvalidStateError(msg) { return new DOMException(msg, "InvalidStateError"); }, ); core.registerErrorBuilder( "DOMExceptionSyntaxError", function DOMExceptionSyntaxError(msg) { return new DOMException(msg, "SyntaxError"); }, ); core.registerErrorBuilder( "DOMExceptionIndexSizeError", function DOMExceptionIndexSizeError(msg) { return new DOMException(msg, "IndexSizeError"); }, ); core.registerErrorBuilder( "DOMExceptionTypeMismatchError", function DOMExceptionTypeMismatchError(msg) { return new DOMException(msg, "TypeMismatchError"); }, ); core.registerErrorBuilder( "DOMExceptionInvalidAccessError", function DOMExceptionInvalidAccessError(msg) { return new DOMException(msg, "InvalidAccessError"); }, ); function runtimeStart( denoVersion, v8Version, tsVersion, target, ) { core.setWasmStreamingCallback(function wasmStreamingCallback(source, rid) { const handleWasmStreaming = lazyFetchMod().handleWasmStreaming; return handleWasmStreaming(source, rid); }); core.setReportExceptionCallback(event.reportException); op_set_format_exception_callback(formatException); version.setVersions( denoVersion, v8Version, tsVersion, ); core.setBuildInfo(target); } core.setUnhandledPromiseRejectionHandler(processUnhandledPromiseRejection); core.setHandledPromiseRejectionHandler(processRejectionHandled); // Notification that the core received an unhandled promise rejection that is about to // terminate the runtime. If we can handle it, attempt to do so. function processUnhandledPromiseRejection(promise, reason) { const rejectionEvent = new event.PromiseRejectionEvent( "unhandledrejection", { cancelable: true, promise, reason, }, ); // Note that the handler may throw, causing a recursive "error" event globalThis_.dispatchEvent(rejectionEvent); // If event was not yet prevented, try handing it off to Node compat layer // (if it was initialized) if ( !rejectionEvent.defaultPrevented && typeof internals.nodeProcessUnhandledRejectionCallback !== "undefined" ) { internals.nodeProcessUnhandledRejectionCallback(rejectionEvent); } // If event was not prevented (or "unhandledrejection" listeners didn't // throw) we will let Rust side handle it. if (rejectionEvent.defaultPrevented) { return true; } return false; } function processRejectionHandled(promise, reason) { const rejectionHandledEvent = new event.PromiseRejectionEvent( "rejectionhandled", { promise, reason }, ); // Note that the handler may throw, causing a recursive "error" event globalThis_.dispatchEvent(rejectionHandledEvent); if (typeof internals.nodeProcessRejectionHandledCallback !== "undefined") { internals.nodeProcessRejectionHandledCallback(rejectionHandledEvent); } } function dispatchLoadEvent() { globalThis_.dispatchEvent(new event.Event("load")); } function dispatchBeforeUnloadEvent() { return globalThis_.dispatchEvent( new event.Event("beforeunload", { cancelable: true }), ); } function dispatchUnloadEvent() { globalThis_.dispatchEvent(new event.Event("unload")); } let hasBootstrapped = false; // Set up global properties shared by main and worker runtime. core.defineGlobalProperties(globalThis, windowOrWorkerGlobalScope); // Set up global properties shared by main and worker runtime that are exposed // by unstable features if those are enabled. function exposeUnstableFeaturesForWindowOrWorkerGlobalScope(unstableFeatures) { const featureIds = ArrayPrototypeMap( ObjectKeys( unstableForWindowOrWorkerGlobalScope, ), (k) => k | 0, ); for (let i = 0; i <= featureIds.length; i++) { const featureId = featureIds[i]; if (ArrayPrototypeIncludes(unstableFeatures, featureId)) { const props = unstableForWindowOrWorkerGlobalScope[featureId]; core.defineGlobalProperties(globalThis, { ...props }); } } } // NOTE(bartlomieju): remove all the ops that have already been imported using // "virtual op module" (`ext:core/ops`). const NOT_IMPORTED_OPS = [ // Related to `Deno.bench()` API "op_bench_now", "op_dispatch_bench_event", "op_register_bench", "op_bench_get_origin", // Related to `Deno.jupyter` REPL API "op_jupyter_broadcast", "op_jupyter_input", "op_jupyter_create_png_from_texture", "op_jupyter_get_buffer", // Related to the Jupyter ZMQ kernel worker "op_jupyter_get_connection_info", "op_jupyter_repl_evaluate", "op_jupyter_repl_get_properties", "op_jupyter_repl_global_lexical_scope_names", "op_jupyter_repl_call_function_on_args", "op_jupyter_repl_call_function_on", "op_jupyter_repl_interrupt", "op_jupyter_repl_cancel_interrupt", "op_jupyter_recv_iopub", "op_jupyter_recv_input", "op_jupyter_send_input_reply", "op_jupyter_deno_version", "op_jupyter_typescript_version", // Used in jupyter API "op_base64_encode_from_buffer", // Used in the lint API "op_lint_report", "op_lint_get_source", "op_lint_create_serialized_ast", "op_is_cancelled", // Related to `Deno.test()` API "op_test_event_step_result_failed", "op_test_event_step_result_ignored", "op_test_event_step_result_ok", "op_test_event_step_wait", "op_test_op_sanitizer_collect", "op_test_op_sanitizer_finish", "op_test_op_sanitizer_get_async_message", "op_test_op_sanitizer_report", "op_restore_test_permissions", "op_register_test_step", "op_register_test_hook", "op_register_test", "op_test_get_origin", "op_test_event_exit", "op_test_isolate_exit", "op_pledge_test_permissions", "op_test_snapshot_in_update_mode", "op_test_snapshot_read", "op_test_snapshot_write", "op_test_event_snapshot_summary", // TODO(bartlomieju): used in various integration tests - figure out a way // to not depend on them. "op_set_exit_code", "op_napi_open", // Related to `Deno.desktop` API (deno compile --desktop) "BrowserWindow", "Dock", "Tray", "Notification", "op_desktop_apply_patch", "op_desktop_confirm_update", "op_desktop_verify_ed25519", "op_desktop_init", "op_desktop_recv_event", "op_desktop_resolve_bind_call", "op_desktop_reject_bind_call", "op_desktop_alert", "op_desktop_confirm", "op_desktop_prompt", "op_desktop_send_error_report", "op_desktop_request_notification_permission", "op_desktop_query_notification_permission", // deno deploy subcommand "op_deploy_token_get", "op_deploy_token_set", "op_deploy_token_delete", ]; function removeImportedOps() { const allOpNames = ObjectKeys(ops); for (let i = 0; i < allOpNames.length; i++) { const opName = allOpNames[i]; if (!ArrayPrototypeIncludes(NOT_IMPORTED_OPS, opName)) { delete ops[opName]; } } } // `Deno[Deno.internal]` is reachable from user code. Preserve its existing // internal compatibility surface, but keep extension-loading capabilities on // the core object imported through `ext:core/mod.js`. const userVisibleCoreDescriptors = ObjectGetOwnPropertyDescriptors(core); delete userVisibleCoreDescriptors.createLazyLoader; delete userVisibleCoreDescriptors.loadExtScript; const userVisibleCore = ObjectFreeze(ObjectDefineProperties( { __proto__: null }, userVisibleCoreDescriptors, )); ObjectAssign(internals, { core: userVisibleCore }); const internalSymbol = Symbol("Deno.internal"); // `Deno.test` and its sub-methods are no-ops outside of `deno test`, kept for // compatibility so they don't error under `deno run`. Mirrors the surface of // the real `Deno.test` defined in cli/js/40_test.js. function noopTest() {} noopTest.ignore = () => {}; noopTest.only = () => {}; noopTest.beforeAll = () => {}; noopTest.beforeEach = () => {}; noopTest.afterEach = () => {}; noopTest.afterAll = () => {}; noopTest.sanitizer = () => {}; const noopTestEach = () => () => {}; noopTest.each = noopTestEach; noopTest.only.each = noopTestEach; noopTest.ignore.each = noopTestEach; // Build finalDenoNs without spreading denoNs: spread invokes every getter, // including the lazy ones (Deno.serve / Deno.run / etc.) that intentionally // avoid loading 06_streams / 22_body / 40_process at snapshot time. Use // ObjectDefineProperties + getOwnPropertyDescriptors to preserve the lazy // descriptors. const finalDenoNs = ObjectDefineProperties( { internal: internalSymbol, [internalSymbol]: internals, // Deno.test, Deno.bench, Deno.lint are noops here, but kept for // compatibility; so that they don't cause errors when used outside of // `deno test`/`deno bench`/`deno lint` contexts. test: noopTest, bench: () => {}, lint: { runPlugin: () => { throw new Error( "`Deno.lint.runPlugin` is only available in `deno test` subcommand.", ); }, }, }, ObjectGetOwnPropertyDescriptors(denoNs), ); ObjectDefineProperties(finalDenoNs, { pid: core.propGetterOnly(opPid), // `ppid` should not be memoized. // https://github.com/denoland/deno/issues/23004 ppid: core.propGetterOnly(() => op_ppid()), noColor: core.propGetterOnly(() => op_bootstrap_no_color()), args: core.propGetterOnly(opArgs), mainModule: core.propGetterOnly(() => op_main_module()), exitCode: { __proto__: null, get() { return os.getExitCode(); }, set(value) { os.setExitCode(value); }, }, }); const { tsVersion, v8Version, target, } = op_snapshot_options(); const executionModes = { none: 0, worker: 1, run: 2, repl: 3, eval: 4, test: 5, bench: 6, serve: 7, jupyter: 8, }; let protoSetRecorded = false; let protoGetRecorded = false; // By default Deno disables the `Object.prototype.__proto__` accessor for // security reasons (it enables prototype pollution), see // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__ // // Historically this was done with `delete Object.prototype.__proto__`, which // makes `obj.__proto__` reads return `undefined` and writes silently create a // useless own property. Making the accessor *throw* instead would surface those // silent bugs, but it broke real packages such as Playwright (see // denoland/deno#34730 / #34772), so we keep the silent behavior. // // Instead of deleting we install an accessor that reproduces the deleted // semantics exactly (read -> `undefined`, write -> own data property, prototype // unchanged) but additionally records the first read and the first write. When // the program later crashes, the uncaught-error formatter (runtime/fmt_errors.rs) // reads those flags and suggests `--unstable-unsafe-proto`. A write surfaces // downstream so any later crash triggers the nudge; a read crashes at the // access site, so the formatter additionally requires `__proto__` on the // failing line before nudging. The `__proto__` key in object literals (e.g. // `{ __proto__: null }`) is separate syntax and is unaffected. function disableProtoAccessor() { ObjectDefineProperty(ObjectPrototype, "__proto__", { __proto__: null, configurable: true, enumerable: false, // Distinct getter/setter function objects: the native accessor uses // separate functions and WPT asserts accessor get/set are unique. get: function __proto__() { if (!protoGetRecorded) { protoGetRecorded = true; op_proto_get_attempted(); } return undefined; }, set: function __proto__(value) { if (!protoSetRecorded) { protoSetRecorded = true; op_proto_set_attempted(); } // Reproduce the previous `delete` behavior: a bare assignment created a // normal own data property. Skip non-extensible receivers, where that // assignment was a silent no-op in sloppy mode (and a throw in strict // mode); keeping it silent here matches the "stay silent" goal above. if (ObjectIsExtensible(this)) { ObjectDefineProperty(this, "__proto__", { __proto__: null, value, writable: true, enumerable: true, configurable: true, }); } }, }); } function bootstrapMainRuntime(runtimeOptions, warmup = false) { if (!warmup) { if (hasBootstrapped) { throw new Error("Worker runtime already bootstrapped"); } const { 0: denoVersion, 1: location_, 2: unstableFeatures, 5: hasNodeModulesDir, 6: argv0, 7: nodeDebug, 8: mode, 9: servePort, 10: serveHost, 11: serveIsMain, 12: serveWorkerCountOrIndex, 13: otelConfig, 15: standalone, 16: autoServe, 17: nodeClusterUniqueId, 18: nodeClusterSchedPolicy, 19: disableOffscreenCanvas, } = runtimeOptions; denoNs.build.standalone = standalone; let serveIsMain_ = serveIsMain; let serveWorkerCountOrIndex_ = serveWorkerCountOrIndex; if (autoServe) { serveIsMain_ = true; serveWorkerCountOrIndex_ = 0; } if (mode === executionModes.serve || autoServe) { const hasMultipleThreads = serveIsMain_ ? serveWorkerCountOrIndex_ > 0 // count > 0 : true; if (hasMultipleThreads) { const serveLogIndex = serveIsMain_ ? 0 : (serveWorkerCountOrIndex_ + 1); const base = `serve-worker-${serveLogIndex}`; // 15 = "serve-worker-nn".length, assuming // serveWorkerCount < 100 const prefix = `[${StringPrototypePadEnd(base, 15, " ")}]`; // deno-lint-ignore no-global-assign console = new internalConsole.Console((msg, level) => core.print(`${prefix} ` + msg, level > 1) ); } } if (mode === executionModes.run || mode === executionModes.serve) { let serve = undefined; core.addMainModuleHandler((main) => { if (ObjectHasOwn(main, "default")) { const dflt = main.default; // `registerDeclarativeServer` returns immediately unless the default // export has an own `fetch`, but merely reaching that check loads // 00_serve.ts -> 23_request/23_response/22_body -> the web-streams // polyfill: ~430 KB across 11 modules. Every CommonJS entry point // surfaces `module.exports` as `default`, and plenty of ESM ones // have an unrelated `export default`, so that graph was being // compiled for programs that will never serve anything. Hoist the // guard here. `dflt == null` still calls through, so the TypeError // `Object.hasOwn(null, ...)` raises under `deno serve` is unchanged. if (dflt == null || ObjectHasOwn(dflt, "fetch")) { try { serve = lazyServeMod().registerDeclarativeServer(dflt); } catch (e) { if (mode === executionModes.serve || autoServe) { throw e; } } } } if (mode === executionModes.serve && !serve) { if (serveIsMain_) { // Only error if main worker import.meta.log( "error", `%cerror: %cdeno serve requires %cexport default { fetch }%c in the main module, did you mean to run \"deno run\"?`, "color: yellow;", "color: inherit;", "font-weight: bold;", "font-weight: normal;", ); } return; } if (serve) { if (mode === executionModes.run && !autoServe) { import.meta.log( "error", `%cwarning: %cDetected %cexport default { fetch }%c, did you mean to run \"deno serve\"?`, "color: yellow;", "color: inherit;", "font-weight: bold;", "font-weight: normal;", ); } if (mode === executionModes.serve || autoServe) { serve({ servePort, serveHost, workerCountWhenMain: serveIsMain_ ? serveWorkerCountOrIndex_ : undefined, }); } } }); } removeImportedOps(); performance.setTimeOrigin(); globalThis_ = globalThis; // Remove bootstrapping data from the global scope. Lazy-loaded IIFE // scripts (`ext:.../*.js`) and the synthetic_esm backing-script path // both read `globalThis.__bootstrap.core.ops` at module body; the // Rust `load_ext_script` reinstalls a captured snapshot view of // `__bootstrap` for the duration of each script's evaluation (see // `BootstrapInstallGuard` in `libs/core/modules/map.rs`). delete globalThis.__bootstrap; delete globalThis.bootstrap; hasBootstrapped = true; // If the `--location` flag isn't set, make `globalThis.location` `undefined` and // writable, so that they can mock it themselves if they like. If the flag was // set, define `globalThis.location`, using the provided value. if (location_ == null) { mainRuntimeGlobalProperties.location = { writable: true, configurable: true, }; } else { location.setLocationHref(location_); } // Use defineGlobalProperties so the lazy-loaded descriptors (alert, // confirm, prompt, Storage) get their `lazyNameSym` stamped with the // property name. Without this, the setter calls // `Object.defineProperty(this, undefined, ...)` which fails because the // global `undefined` is non-configurable on `globalThis`. core.defineGlobalProperties(globalThis, mainRuntimeGlobalProperties); ObjectDefineProperties(globalThis, { // TODO(bartlomieju): in the future we might want to change the // behavior of setting `name` to actually update the process name. // Empty string matches what browsers do. name: core.propWritable(""), close: core.propWritable(windowClose), closed: core.propGetterOnly(() => windowIsClosing), }); if (disableOffscreenCanvas) { delete globalThis.OffscreenCanvas; } exposeUnstableFeaturesForWindowOrWorkerGlobalScope(unstableFeatures); ObjectSetPrototypeOf(globalThis, Window.prototype); bootstrapOtel(otelConfig); // Wrap the console unconditionally (like the worker bootstrap does) // rather than only under --inspect*: the inspector can also be // activated later at runtime (node:inspector open(), SIGUSR1), and // without the wrap those sessions never receive // Runtime.consoleAPICalled events. core.wrapConsole(globalThis.console, core.v8Console); event.defineEventHandler(globalThis, "error"); event.defineEventHandler(globalThis, "load"); event.defineEventHandler(globalThis, "beforeunload"); event.defineEventHandler(globalThis, "unload"); runtimeStart( denoVersion, v8Version, tsVersion, target, ); // TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign` // above any properties that are defined elsewhere using `Object.defineProperty` // are lost. let jupyterNs = undefined; ObjectDefineProperty(finalDenoNs, "jupyter", { __proto__: null, get() { if (jupyterNs) { return jupyterNs; } throw new Error( "Deno.jupyter is only available in `deno jupyter` subcommand", ); }, set(val) { jupyterNs = val; }, }); for (let i = 0; i <= unstableFeatures.length; i++) { const id = unstableFeatures[i]; const unstable = denoNsUnstableById[id]; if (unstable) { ObjectDefineProperties( finalDenoNs, ObjectGetOwnPropertyDescriptors(unstable), ); } } if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) { disableProtoAccessor(); } // Setup `Deno` global - we're actually overriding already existing global // `Deno` with `Deno` namespace from "./deno.ts". ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs)); const nodeBootstrapArgs = { usesLocalNodeModulesDir: hasNodeModulesDir, runningOnMainThread: true, argv0, nodeDebug, nodeClusterUniqueId, nodeClusterSchedPolicy, // Stashed so process.ts's self-trigger can call __bootstrapNodeProcess // without reading Deno.* (the no-deno-api-in-polyfills lint counts // Deno.* references in node polyfills; passing them in keeps process.ts // at zero new violations). denoArgs: Deno.args, denoVersion: Deno.version, }; if (nodeBootstrap) { nodeBootstrap(nodeBootstrapArgs); } else if (op_node_has_child_ipc_pipe()) { // node-defer: this main process is a forked child with an IPC pipe. It // needs the IPC channel (set up in the full `initialize`) ready // synchronously before its main module calls process.send / // process.on("message"). Run the full node bootstrap EAGERLY (like // workers) -- a forked IPC child is a node process, so the deser win // doesn't apply. node:process first (fully evaluates), then node:module // (its closure captures a finished node:process, so no cold-bootstrap // TDZ), then `initialize`. core.createLazyLoader("node:process")(); core.createLazyLoader("node:module")(); globalThis.nodeBootstrap(nodeBootstrapArgs); } else { // node-defer: node:module (01_require.js) is `lazy_loaded_esm`, so its // top-level `globalThis.nodeBootstrap = initialize` hasn't run yet and // `nodeBootstrap` is undefined here. Stash the args so node:process and // 01_require.js self-bootstrap from them when first lazily loaded (on // the first node:* use), so non-node programs never pay node bootstrap. internals.__nodeBootstrapArgs = nodeBootstrapArgs; } } else { // Warmup } } function bootstrapWorkerRuntime( runtimeOptions, name, internalName, workerId, workerType, maybeWorkerMetadata, warmup = false, ) { if (!warmup) { if (hasBootstrapped) { throw new Error("Worker runtime already bootstrapped"); } const { 0: denoVersion, 1: location_, 2: unstableFeatures, 4: enableTestingFeaturesFlag, 5: hasNodeModulesDir, 6: argv0, 7: nodeDebug, 13: otelConfig, 15: standalone, 17: nodeClusterUniqueId, 18: nodeClusterSchedPolicy, 19: disableOffscreenCanvas, } = runtimeOptions; denoNs.build.standalone = standalone; closeOnIdle = runtimeOptions[14]; removeImportedOps(); performance.setTimeOrigin(); globalThis_ = globalThis; // Remove bootstrapping data from the global scope. Lazy-loaded IIFE // scripts (`ext:.../*.js`) and the synthetic_esm backing-script path // both read `globalThis.__bootstrap.core.ops` at module body; the // Rust `load_ext_script` reinstalls a captured snapshot view of // `__bootstrap` for the duration of each script's evaluation (see // `BootstrapInstallGuard` in `libs/core/modules/map.rs`). delete globalThis.__bootstrap; delete globalThis.bootstrap; hasBootstrapped = true; if (workerType === "node") { delete workerRuntimeGlobalProperties["WorkerGlobalScope"]; delete workerRuntimeGlobalProperties["self"]; } ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties); ObjectDefineProperties(globalThis, { name: core.propWritable(name), // TODO(bartlomieju): should be readonly? close: core.propNonEnumerable(workerClose), postMessage: core.propWritable(postMessage), }); if (disableOffscreenCanvas) { delete globalThis.OffscreenCanvas; } if (enableTestingFeaturesFlag) { ObjectDefineProperty( globalThis, "importScripts", core.propWritable(importScripts), ); } exposeUnstableFeaturesForWindowOrWorkerGlobalScope(unstableFeatures); ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype); bootstrapOtel(otelConfig); core.wrapConsole(globalThis.console, core.v8Console); event.defineEventHandler(globalThis, "message"); event.defineEventHandler(globalThis, "error", undefined, true); // `Deno.exit()` closes the worker using the internal worker close // operation. Setting an exit code using an op in worker context is a no-op. os.setExitHandler((_exitCode) => { workerClose(); }); runtimeStart( denoVersion, v8Version, tsVersion, target, internalName ?? name, ); location.setLocationHref(location_); globalThis.pollForMessages = pollForMessages; globalThis.hasMessageEventListener = hasMessageEventListener; for (let i = 0; i <= unstableFeatures.length; i++) { const id = unstableFeatures[i]; const unstable = denoNsUnstableById[id]; if (unstable) { ObjectDefineProperties( finalDenoNs, ObjectGetOwnPropertyDescriptors(unstable), ); } } // Not available in workers const moduleSpecifier = finalDenoNs.mainModule; delete finalDenoNs.mainModule; if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) { disableProtoAccessor(); } // Setup `Deno` global - we're actually overriding already existing global // `Deno` with `Deno` namespace from "./deno.ts". ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs)); const workerMetadata = maybeWorkerMetadata ? messagePort.deserializeJsMessageData(maybeWorkerMetadata) : undefined; const nodeBootstrapArgs = { usesLocalNodeModulesDir: hasNodeModulesDir, runningOnMainThread: false, argv0, workerId, maybeWorkerMetadata: workerMetadata, nodeDebug, nodeClusterUniqueId, nodeClusterSchedPolicy, moduleSpecifier: workerType === "node" ? moduleSpecifier : null, // Stashed so process.ts's self-trigger can call __bootstrapNodeProcess // without reading Deno.* (see the main-thread branch above). denoArgs: Deno.args, denoVersion: Deno.version, }; if (nodeBootstrap) { nodeBootstrap(nodeBootstrapArgs); } else if (workerType === "node") { // node-defer: node worker_threads need the FULL node bootstrap eagerly: // require, workerData, SharedArrayBuffer, etc. must be ready before the // worker's first line runs. Web workers stay lazy like the main thread // so non-node workers never pay node bootstrap. // Load node:process FIRST (fully evaluates + runs the process bootstrap), // THEN node:module (its closure now captures a fully-evaluated // node:process, avoiding the cold-bootstrap TDZ), then run `initialize`. core.createLazyLoader("node:process")(); core.createLazyLoader("node:module")(); globalThis.nodeBootstrap(nodeBootstrapArgs); } else { internals.__nodeBootstrapArgs = nodeBootstrapArgs; } } else { // Warmup return; } } const nodeBootstrap = globalThis.nodeBootstrap; delete globalThis.nodeBootstrap; // node-defer: node:process sets internals.dispatchProcess{Exit,BeforeExit}Event // during its bootstrap, which is now deferred to first node:* use -- i.e. AFTER // this module evaluates. Capturing the values here would freeze the no-op // (node not yet bootstrapped). Instead dispatch dynamically: look up the // current internals handler at call time. No-op when node was never // bootstrapped (a non-node program has no process exit listeners). const dispatchProcessExitEvent = (...args) => internals.dispatchProcessExitEvent ? ReflectApply(internals.dispatchProcessExitEvent, internals, args) : undefined; const dispatchProcessBeforeExitEvent = (...args) => internals.dispatchProcessBeforeExitEvent ? ReflectApply(internals.dispatchProcessBeforeExitEvent, internals, args) : false; globalThis.bootstrap = { mainRuntime: bootstrapMainRuntime, workerRuntime: bootstrapWorkerRuntime, dispatchLoadEvent, dispatchUnloadEvent, dispatchBeforeUnloadEvent, dispatchProcessExitEvent, dispatchProcessBeforeExitEvent, }; event.setEventTargetData(globalThis); event.saveGlobalThisReference(globalThis); event.defineEventHandler(globalThis, "unhandledrejection"); // Nothing listens to this, but it warms up the code paths for event dispatch (new event.EventTarget()).dispatchEvent(new event.Event("warmup")); removeImportedOps(); // Run the warmup path through node and runtime/worker bootstrap functions bootstrapMainRuntime(undefined, true); bootstrapWorkerRuntime( undefined, undefined, undefined, undefined, undefined, undefined, true, ); // Skip warmup. The warmup branch creates placeholder stdin/stdout/stderr // streams that the runtime bootstrap (__bootstrapNodeProcess(warmup=false)) // then unconditionally overwrites with fresh TTYWriteStream instances, so // the only observable effect of warmup was pulling node:stream and friends // into the snapshot via createWritableStdioStream/initStdin. // nodeBootstrap({ warmup: true });