diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/node/global.rs | 6 | ||||
-rw-r--r-- | ext/node/lib.rs | 24 | ||||
-rw-r--r-- | ext/node/polyfills/02_init.js | 4 | ||||
-rw-r--r-- | ext/node/polyfills/process.ts | 266 | ||||
-rw-r--r-- | ext/node/resolution.rs | 7 |
5 files changed, 209 insertions, 98 deletions
diff --git a/ext/node/global.rs b/ext/node/global.rs index 78e009971..52c1b6bb9 100644 --- a/ext/node/global.rs +++ b/ext/node/global.rs @@ -1,5 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use std::mem::MaybeUninit; use std::rc::Rc; use deno_core::v8; @@ -266,13 +267,14 @@ fn current_mode(scope: &mut v8::HandleScope) -> Mode { let Some(v8_string) = v8::StackTrace::current_script_name_or_source_url(scope) else { return Mode::Deno; }; - let string = v8_string.to_rust_string_lossy(scope); let op_state = deno_core::JsRuntime::op_state_from(scope); let op_state = op_state.borrow(); let Some(node_resolver) = op_state.try_borrow::<Rc<NodeResolver>>() else { return Mode::Deno; }; - if node_resolver.in_npm_package_with_cache(string) { + let mut buffer = [MaybeUninit::uninit(); 2048]; + let str = v8_string.to_rust_cow_lossy(scope, &mut buffer); + if node_resolver.in_npm_package_with_cache(str) { Mode::Node } else { Mode::Deno diff --git a/ext/node/lib.rs b/ext/node/lib.rs index e2643a84f..c7d617666 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -8,7 +8,6 @@ use std::rc::Rc; use deno_core::error::AnyError; use deno_core::located_script_name; use deno_core::op; -use deno_core::serde_json; use deno_core::serde_v8; use deno_core::url::Url; #[allow(unused_imports)] @@ -558,29 +557,6 @@ deno_core::extension!(deno_node, }, ); -pub fn initialize_runtime( - js_runtime: &mut JsRuntime, - uses_local_node_modules_dir: bool, - maybe_binary_command_name: Option<&str>, -) -> Result<(), AnyError> { - let argv0 = if let Some(binary_command_name) = maybe_binary_command_name { - serde_json::to_string(binary_command_name)? - } else { - "undefined".to_string() - }; - let source_code = format!( - r#"(function loadBuiltinNodeModules(usesLocalNodeModulesDir, argv0) {{ - Deno[Deno.internal].node.initialize( - usesLocalNodeModulesDir, - argv0 - ); - }})({uses_local_node_modules_dir}, {argv0});"#, - ); - - js_runtime.execute_script(located_script_name!(), source_code.into())?; - Ok(()) -} - pub fn load_cjs_module( js_runtime: &mut JsRuntime, module: &str, diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js index d73d5d822..e3061c95d 100644 --- a/ext/node/polyfills/02_init.js +++ b/ext/node/polyfills/02_init.js @@ -7,6 +7,10 @@ const requireImpl = internals.requireImpl; import { nodeGlobals } from "ext:deno_node/00_globals.js"; import "node:module"; +globalThis.nodeBootstrap = function (usesLocalNodeModulesDir, argv0) { + initialize(usesLocalNodeModulesDir, argv0); +}; + let initialized = false; function initialize( diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 4c375760d..c7c22b562 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -33,6 +33,8 @@ export { _nextTick as nextTick, chdir, cwd, env, version, versions }; import { createWritableStdioStream, initStdin, + Readable, + Writable, } from "ext:deno_node/_process/streams.mjs"; import { enableNextTick, @@ -52,15 +54,42 @@ export let platform = ""; // TODO(kt3k): This should be set at start up time export let pid = 0; -// TODO(kt3k): Give better types to stdio objects -// deno-lint-ignore no-explicit-any -let stderr = null as any; -// deno-lint-ignore no-explicit-any -let stdin = null as any; -// deno-lint-ignore no-explicit-any -let stdout = null as any; +// We want streams to be as lazy as possible, but we cannot export a getter in a module. To +// work around this we make these proxies that eagerly instantiate the underlying object on +// first access of any property/method. +function makeLazyStream<T>(objectFactory: () => T): T { + return new Proxy({}, { + get: function (_, prop, receiver) { + // deno-lint-ignore no-explicit-any + return Reflect.get(objectFactory() as any, prop, receiver); + }, + has: function (_, prop) { + // deno-lint-ignore no-explicit-any + return Reflect.has(objectFactory() as any, prop); + }, + ownKeys: function (_) { + // deno-lint-ignore no-explicit-any + return Reflect.ownKeys(objectFactory() as any); + }, + set: function (_, prop, value, receiver) { + // deno-lint-ignore no-explicit-any + return Reflect.set(objectFactory() as any, prop, value, receiver); + }, + getPrototypeOf: function (_) { + // deno-lint-ignore no-explicit-any + return Reflect.getPrototypeOf(objectFactory() as any); + }, + getOwnPropertyDescriptor(_, prop) { + // deno-lint-ignore no-explicit-any + return Reflect.getOwnPropertyDescriptor(objectFactory() as any, prop); + }, + }) as T; +} + +export let stderr = makeLazyStream(getStderr); +export let stdin = makeLazyStream(getStdin); +export let stdout = makeLazyStream(getStdout); -export { stderr, stdin, stdout }; import { getBinding } from "ext:deno_node/internal_binding/mod.ts"; import * as constants from "ext:deno_node/internal_binding/constants.ts"; import * as uv from "ext:deno_node/internal_binding/uv.ts"; @@ -605,13 +634,19 @@ class Process extends EventEmitter { memoryUsage = memoryUsage; /** https://nodejs.org/api/process.html#process_process_stderr */ - stderr = stderr; + get stderr(): Writable { + return getStderr(); + } /** https://nodejs.org/api/process.html#process_process_stdin */ - stdin = stdin; + get stdin(): Readable { + return getStdin(); + } /** https://nodejs.org/api/process.html#process_process_stdout */ - stdout = stdout; + get stdout(): Writable { + return getStdout(); + } /** https://nodejs.org/api/process.html#process_process_version */ version = version; @@ -704,6 +739,115 @@ addReadOnlyProcessAlias("throwDeprecation", "--throw-deprecation"); export const removeListener = process.removeListener; export const removeAllListeners = process.removeAllListeners; +let unhandledRejectionListenerCount = 0; +let uncaughtExceptionListenerCount = 0; +let beforeExitListenerCount = 0; +let exitListenerCount = 0; + +process.on("newListener", (event: string) => { + switch (event) { + case "unhandledRejection": + unhandledRejectionListenerCount++; + break; + case "uncaughtException": + uncaughtExceptionListenerCount++; + break; + case "beforeExit": + beforeExitListenerCount++; + break; + case "exit": + exitListenerCount++; + break; + default: + return; + } + synchronizeListeners(); +}); + +process.on("removeListener", (event: string) => { + switch (event) { + case "unhandledRejection": + unhandledRejectionListenerCount--; + break; + case "uncaughtException": + uncaughtExceptionListenerCount--; + break; + case "beforeExit": + beforeExitListenerCount--; + break; + case "exit": + exitListenerCount--; + break; + default: + return; + } + synchronizeListeners(); +}); + +function processOnError(event: ErrorEvent) { + if (process.listenerCount("uncaughtException") > 0) { + event.preventDefault(); + } + + uncaughtExceptionHandler(event.error, "uncaughtException"); +} + +function processOnBeforeUnload(event: Event) { + process.emit("beforeExit", process.exitCode || 0); + processTicksAndRejections(); + if (core.eventLoopHasMoreWork()) { + event.preventDefault(); + } +} + +function processOnUnload() { + if (!process._exiting) { + process._exiting = true; + process.emit("exit", process.exitCode || 0); + } +} + +function synchronizeListeners() { + // Install special "unhandledrejection" handler, that will be called + // last. + if ( + unhandledRejectionListenerCount > 0 || uncaughtExceptionListenerCount > 0 + ) { + internals.nodeProcessUnhandledRejectionCallback = (event) => { + if (process.listenerCount("unhandledRejection") === 0) { + // The Node.js default behavior is to raise an uncaught exception if + // an unhandled rejection occurs and there are no unhandledRejection + // listeners. + + event.preventDefault(); + uncaughtExceptionHandler(event.reason, "unhandledRejection"); + return; + } + + event.preventDefault(); + process.emit("unhandledRejection", event.reason, event.promise); + }; + } else { + internals.nodeProcessUnhandledRejectionCallback = undefined; + } + + if (uncaughtExceptionListenerCount > 0) { + globalThis.addEventListener("error", processOnError); + } else { + globalThis.removeEventListener("error", processOnError); + } + if (beforeExitListenerCount > 0) { + globalThis.addEventListener("beforeunload", processOnBeforeUnload); + } else { + globalThis.removeEventListener("beforeunload", processOnBeforeUnload); + } + if (exitListenerCount > 0) { + globalThis.addEventListener("unload", processOnUnload); + } else { + globalThis.removeEventListener("unload", processOnUnload); + } +} + // Should be called only once, in `runtime/js/99_main.js` when the runtime is // bootstrapped. internals.__bootstrapNodeProcess = function ( @@ -748,68 +892,52 @@ internals.__bootstrapNodeProcess = function ( core.setMacrotaskCallback(runNextTicks); enableNextTick(); - // Install special "unhandledrejection" handler, that will be called - // last. - internals.nodeProcessUnhandledRejectionCallback = (event) => { - if (process.listenerCount("unhandledRejection") === 0) { - // The Node.js default behavior is to raise an uncaught exception if - // an unhandled rejection occurs and there are no unhandledRejection - // listeners. - if (process.listenerCount("uncaughtException") === 0) { - throw event.reason; - } - - event.preventDefault(); - uncaughtExceptionHandler(event.reason, "unhandledRejection"); - return; - } - - event.preventDefault(); - process.emit("unhandledRejection", event.reason, event.promise); - }; - - globalThis.addEventListener("error", (event) => { - if (process.listenerCount("uncaughtException") > 0) { - event.preventDefault(); - } - - uncaughtExceptionHandler(event.error, "uncaughtException"); - }); - - globalThis.addEventListener("beforeunload", (e) => { - process.emit("beforeExit", process.exitCode || 0); - processTicksAndRejections(); - if (core.eventLoopHasMoreWork()) { - e.preventDefault(); - } - }); - - globalThis.addEventListener("unload", () => { - if (!process._exiting) { - process._exiting = true; - process.emit("exit", process.exitCode || 0); - } - }); - - // Initializes stdin - stdin = process.stdin = initStdin(); - - /** https://nodejs.org/api/process.html#process_process_stderr */ - stderr = process.stderr = createWritableStdioStream( - io.stderr, - "stderr", - ); - - /** https://nodejs.org/api/process.html#process_process_stdout */ - stdout = process.stdout = createWritableStdioStream( - io.stdout, - "stdout", - ); - process.setStartTime(Date.now()); // @ts-ignore Remove setStartTime and #startTime is not modifiable delete process.setStartTime; delete internals.__bootstrapNodeProcess; }; +// deno-lint-ignore no-explicit-any +let stderr_ = null as any; +// deno-lint-ignore no-explicit-any +let stdin_ = null as any; +// deno-lint-ignore no-explicit-any +let stdout_ = null as any; + +function getStdin(): Readable { + if (!stdin_) { + stdin_ = initStdin(); + stdin = stdin_; + Object.defineProperty(process, "stdin", { get: () => stdin_ }); + } + return stdin_; +} + +/** https://nodejs.org/api/process.html#process_process_stdout */ +function getStdout(): Writable { + if (!stdout_) { + stdout_ = createWritableStdioStream( + io.stdout, + "stdout", + ); + stdout = stdout_; + Object.defineProperty(process, "stdout", { get: () => stdout_ }); + } + return stdout_; +} + +/** https://nodejs.org/api/process.html#process_process_stderr */ +function getStderr(): Writable { + if (!stderr_) { + stderr_ = createWritableStdioStream( + io.stderr, + "stderr", + ); + stderr = stderr_; + Object.defineProperty(process, "stderr", { get: () => stderr_ }); + } + return stderr_; +} + export default process; diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs index 4c43fcbad..20501b0f1 100644 --- a/ext/node/resolution.rs +++ b/ext/node/resolution.rs @@ -1,5 +1,6 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; @@ -128,10 +129,10 @@ impl NodeResolver { self.npm_resolver.in_npm_package(specifier) } - pub fn in_npm_package_with_cache(&self, specifier: String) -> bool { + pub fn in_npm_package_with_cache(&self, specifier: Cow<str>) -> bool { let mut cache = self.in_npm_package_cache.lock(); - if let Some(result) = cache.get(&specifier) { + if let Some(result) = cache.get(specifier.as_ref()) { return *result; } @@ -141,7 +142,7 @@ impl NodeResolver { } else { false }; - cache.insert(specifier, result); + cache.insert(specifier.into_owned(), result); result } |