From 980fb056851b2b6004cbfcfa109ac3c650a7e2ba Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 17:37:23 +0530 Subject: [PATCH 01/24] implement node:tty --- ext/node/lib.rs | 2 +- ext/node/polyfills/tty.js | 82 +++++++++++++++++++++++++++++++++++++++ ext/node/polyfills/tty.ts | 25 ------------ 3 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 ext/node/polyfills/tty.js delete mode 100644 ext/node/polyfills/tty.ts diff --git a/ext/node/lib.rs b/ext/node/lib.rs index a54d5a0102950c..9a76b6b6ab5c0f 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -489,7 +489,7 @@ deno_core::extension!(deno_node, "timers.ts" with_specifier "node:timers", "timers/promises.ts" with_specifier "node:timers/promises", "tls.ts" with_specifier "node:tls", - "tty.ts" with_specifier "node:tty", + "tty.js" with_specifier "node:tty", "url.ts" with_specifier "node:url", "util.ts" with_specifier "node:util", "util/types.ts" with_specifier "node:util/types", diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js new file mode 100644 index 00000000000000..87d27a45f5e952 --- /dev/null +++ b/ext/node/polyfills/tty.js @@ -0,0 +1,82 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { Socket } from "node:net"; +import { ERR_INVALID_FD } from "ext:deno_node/internal/errors.ts"; +import { LibuvStreamWrap } from "ext:deno_node/internal_binding/stream_wrap.ts"; +import { providerType } from "ext:deno_node/internal_binding/async_wrap.ts"; + +// Returns true when the given numeric fd is associated with a TTY and false otherwise. +function isatty(fd) { + if (typeof fd !== "number") { + return false; + } + try { + return Deno.isatty(fd); + } catch (_) { + return false; + } +} + +class TTY extends LibuvStreamWrap { + constructor(handle) { + super(providerType.TTYWRAP, handle); + } +} + +export class ReadStream extends Socket { + constructor(fd, options) { + if (fd >> 0 !== fd || fd < 0) { + throw new ERR_INVALID_FD(fd); + } + + // We only support `stdin`. + if (fd != 0) throw new Error("Only fd 0 is supported."); + + const tty = new TTY(Deno.stdin); + super({ + readableHighWaterMark: 0, + handle: tty, + manualStart: true, + ...options, + }); + + this.isRaw = false; + this.isTTY = true; + } + + setRawMode(flag) { + flag = !!flag; + this._handle.setRaw(flag); + + this.isRaw = flag; + return this; + } +} + +export class WriteStream extends Socket { + constructor(fd) { + if (fd >> 0 !== fd || fd < 0) { + throw new ERR_INVALID_FD(fd); + } + + // We only support `stdin`, `stdout` and `stderr`. + if (fd > 2) throw new Error("Only fd 0, 1 and 2 are supported."); + + const tty = new TTY( + fd === 0 ? Deno.stdin : (fd === 1 ? Deno.stdout : Deno.stderr), + ); + + super({ + readableHighWaterMark: 0, + handle: tty, + manualStart: true, + }); + + const { columns, rows } = Deno.consoleSize(); + this.columns = columns; + this.rows = rows; + } +} + +export { isatty }; +export default { isatty, WriteStream, ReadStream }; diff --git a/ext/node/polyfills/tty.ts b/ext/node/polyfills/tty.ts deleted file mode 100644 index d33f779caaa328..00000000000000 --- a/ext/node/polyfills/tty.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -import { Socket } from "node:net"; - -// Returns true when the given numeric fd is associated with a TTY and false otherwise. -function isatty(fd: number) { - if (typeof fd !== "number") { - return false; - } - try { - return Deno.isatty(fd); - } catch (_) { - return false; - } -} - -// TODO(kt3k): Implement tty.ReadStream class -export class ReadStream extends Socket { -} -// TODO(kt3k): Implement tty.WriteStream class -export class WriteStream extends Socket { -} - -export { isatty }; -export default { isatty, WriteStream, ReadStream }; From eba9e82618ab8322f476f673ff39e90aacb03d21 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 19:27:58 +0530 Subject: [PATCH 02/24] Revert lazy stdin streams --- ext/node/polyfills/process.ts | 152 +++++++++++++++------------------- 1 file changed, 67 insertions(+), 85 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 64a3ef31be56a4..97ae2598eccafa 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -54,41 +54,14 @@ export let platform = ""; // TODO(kt3k): This should be set at start up time export let pid = 0; -// 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(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; -} +// 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; -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"; @@ -634,19 +607,13 @@ class Process extends EventEmitter { memoryUsage = memoryUsage; /** https://nodejs.org/api/process.html#process_process_stderr */ - get stderr(): Writable { - return getStderr(); - } + stderr = stderr; /** https://nodejs.org/api/process.html#process_process_stdin */ - get stdin(): Readable { - return getStdin(); - } + stdin = stdin; /** https://nodejs.org/api/process.html#process_process_stdout */ - get stdout(): Writable { - return getStdout(); - } + stdout = stdout; /** https://nodejs.org/api/process.html#process_process_version */ version = version; @@ -892,52 +859,67 @@ 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; From c98a5abe40dc50552f2ba2a7a3e76d6e73b28f78 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 19:39:09 +0530 Subject: [PATCH 03/24] Fix lint --- ext/node/polyfills/process.ts | 2 -- ext/node/polyfills/tty.js | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 97ae2598eccafa..fa57f48aaf63e9 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -33,8 +33,6 @@ export { _nextTick as nextTick, chdir, cwd, env, version, versions }; import { createWritableStdioStream, initStdin, - Readable, - Writable, } from "ext:deno_node/_process/streams.mjs"; import { enableNextTick, diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js index 87d27a45f5e952..975e618eb9c3d1 100644 --- a/ext/node/polyfills/tty.js +++ b/ext/node/polyfills/tty.js @@ -5,6 +5,8 @@ import { ERR_INVALID_FD } from "ext:deno_node/internal/errors.ts"; import { LibuvStreamWrap } from "ext:deno_node/internal_binding/stream_wrap.ts"; import { providerType } from "ext:deno_node/internal_binding/async_wrap.ts"; +const { Error } = globalThis.__bootstrap.primordials; + // Returns true when the given numeric fd is associated with a TTY and false otherwise. function isatty(fd) { if (typeof fd !== "number") { From 8e8e7e791275f1b68c620744d20865dbd4da07ea Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 20:06:41 +0530 Subject: [PATCH 04/24] Restore stdio on exit --- cli/main.rs | 1 + cli/util/unix.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/cli/main.rs b/cli/main.rs index 1ccd694ee9e818..7fa63ca9f67074 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -259,6 +259,7 @@ fn unwrap_or_exit(result: Result) -> T { pub fn main() { setup_panic_hook(); + util::unix::prepare_stdio(); util::unix::raise_fd_limit(); util::windows::ensure_stdio_open(); #[cfg(windows)] diff --git a/cli/util/unix.rs b/cli/util/unix.rs index fd0c94ea68d530..3d02d7ef2de746 100644 --- a/cli/util/unix.rs +++ b/cli/util/unix.rs @@ -43,3 +43,26 @@ pub fn raise_fd_limit() { } } } + +pub fn prepare_stdio() { + use libc::atexit; + use libc::tcgetattr; + use libc::tcsetattr; + use libc::termios; + + #[cfg(unix)] + // SAFETY: Save current state of stdio and restore it when we exit. + unsafe { + let mut termios = std::mem::zeroed::(); + if tcgetattr(libc::STDIN_FILENO, &mut termios) == 0 { + static mut ORIG_TERMIOS: Option = None; + ORIG_TERMIOS = Some(termios); + + extern "C" fn reset_stdio() { + unsafe { tcsetattr(libc::STDIN_FILENO, 0, &ORIG_TERMIOS.unwrap()) }; + } + + atexit(reset_stdio); + } + } +} From b871a5afb84ea2cbd79937f00e3714b6eb86a778 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 20:26:26 +0530 Subject: [PATCH 05/24] x --- cli/util/unix.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/util/unix.rs b/cli/util/unix.rs index 3d02d7ef2de746..00be186dab55f1 100644 --- a/cli/util/unix.rs +++ b/cli/util/unix.rs @@ -59,6 +59,7 @@ pub fn prepare_stdio() { ORIG_TERMIOS = Some(termios); extern "C" fn reset_stdio() { + // SAFETY: Reset the stdio state. unsafe { tcsetattr(libc::STDIN_FILENO, 0, &ORIG_TERMIOS.unwrap()) }; } From 3b3741916d3242539dc9ee225daf51b96c44338d Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 21:58:58 +0530 Subject: [PATCH 06/24] fix lint windows --- cli/util/unix.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/util/unix.rs b/cli/util/unix.rs index 00be186dab55f1..2fa3c206360b9d 100644 --- a/cli/util/unix.rs +++ b/cli/util/unix.rs @@ -45,14 +45,14 @@ pub fn raise_fd_limit() { } pub fn prepare_stdio() { - use libc::atexit; - use libc::tcgetattr; - use libc::tcsetattr; - use libc::termios; - #[cfg(unix)] // SAFETY: Save current state of stdio and restore it when we exit. unsafe { + use libc::atexit; + use libc::tcgetattr; + use libc::tcsetattr; + use libc::termios; + let mut termios = std::mem::zeroed::(); if tcgetattr(libc::STDIN_FILENO, &mut termios) == 0 { static mut ORIG_TERMIOS: Option = None; From 181cddd51e0ddfe6a75cba75d0aa08c408fea447 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 12 Oct 2023 22:01:15 +0530 Subject: [PATCH 07/24] oopsie --- ext/node/polyfills/process.ts | 43 ----------------------------------- 1 file changed, 43 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index fa57f48aaf63e9..b7c346939497c7 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -857,49 +857,6 @@ 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(); From cfed35dd6b696ccd5765abbfe1bdf81a4c91b101 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 27 Oct 2023 10:18:00 +0530 Subject: [PATCH 08/24] windows fixes? --- ext/node/polyfills/process.ts | 55 +++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index b7c346939497c7..44f8f66c1a1579 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -43,6 +43,9 @@ import { isWindows } from "ext:deno_node/_util/os.ts"; import * as io from "ext:deno_io/12_io.js"; import { Command } from "ext:runtime/40_process.js"; +let argv0Getter = () => ""; +export let argv0 = "deno"; + // TODO(kt3k): This should be set at start up time export let arch = ""; @@ -52,14 +55,19 @@ export let platform = ""; // TODO(kt3k): This should be set at start up time export let pid = 0; -// 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; +export let stdin = initStdin(); -export { stderr, stdin, stdout }; +/** https://nodejs.org/api/process.html#process_process_stdout */ +export let stdout = createWritableStdioStream( + io.stdout, + "stdout", +); + +/** https://nodejs.org/api/process.html#process_process_stderr */ +export let stderr = createWritableStdioStream( + io.stderr, + "stderr", +); import { getBinding } from "ext:deno_node/internal_binding/mod.ts"; import * as constants from "ext:deno_node/internal_binding/constants.ts"; @@ -379,6 +387,15 @@ class Process extends EventEmitter { */ argv = argv; + get argv0() { + if (!argv0) { + argv0 = argv0Getter(); + } + return argv0; + } + + set argv0(_val) {} + /** https://nodejs.org/api/process.html#process_process_chdir_directory */ chdir = chdir; @@ -816,23 +833,25 @@ function synchronizeListeners() { // Should be called only once, in `runtime/js/99_main.js` when the runtime is // bootstrapped. internals.__bootstrapNodeProcess = function ( - argv0: string | undefined, + argv0Val: string | undefined, args: string[], denoVersions: Record, ) { // Overwrites the 1st item with getter. - if (typeof argv0 === "string") { + if (typeof argv0Val === "string") { Object.defineProperty(argv, "0", { get: () => { - return argv0; + return argv0Val; }, }); + argv0Getter = () => argv0Val; } else { Object.defineProperty(argv, "0", { get: () => { return Deno.execPath(); }, }); + argv0Getter = () => Deno.execPath(); } // Overwrites the 2st item with getter. @@ -857,24 +876,10 @@ internals.__bootstrapNodeProcess = function ( core.setMacrotaskCallback(runNextTicks); enableNextTick(); - // 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; }; -export default process; +export default process; \ No newline at end of file From a268c839d26273e847584c6d9b24bf8fa443a1ca Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 27 Oct 2023 10:20:42 +0530 Subject: [PATCH 09/24] fmt --- ext/node/polyfills/process.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 44f8f66c1a1579..7a0db53b07f9bb 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -882,4 +882,4 @@ internals.__bootstrapNodeProcess = function ( delete internals.__bootstrapNodeProcess; }; -export default process; \ No newline at end of file +export default process; From bc168990fb1c2b6a5f1707c2783cca00259b5b92 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 27 Oct 2023 10:21:55 +0530 Subject: [PATCH 10/24] fix js lint --- ext/node/polyfills/process.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 7a0db53b07f9bb..89ea60be3ebe4a 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -55,16 +55,16 @@ export let platform = ""; // TODO(kt3k): This should be set at start up time export let pid = 0; -export let stdin = initStdin(); +export const stdin = initStdin(); /** https://nodejs.org/api/process.html#process_process_stdout */ -export let stdout = createWritableStdioStream( +export const stdout = createWritableStdioStream( io.stdout, "stdout", ); /** https://nodejs.org/api/process.html#process_process_stderr */ -export let stderr = createWritableStdioStream( +export const stderr = createWritableStdioStream( io.stderr, "stderr", ); From 7bf36468453df937ec40c5a08339345ee9587679 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 27 Oct 2023 12:39:21 +0530 Subject: [PATCH 11/24] macOS fix? --- ext/node/polyfills/process.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 89ea60be3ebe4a..7f9230ebd869f9 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -55,19 +55,9 @@ export let platform = ""; // TODO(kt3k): This should be set at start up time export let pid = 0; -export const stdin = initStdin(); +let stdin, stdout, stderr; -/** https://nodejs.org/api/process.html#process_process_stdout */ -export const stdout = createWritableStdioStream( - io.stdout, - "stdout", -); - -/** https://nodejs.org/api/process.html#process_process_stderr */ -export const stderr = createWritableStdioStream( - io.stderr, - "stderr", -); +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"; @@ -877,6 +867,21 @@ internals.__bootstrapNodeProcess = function ( enableNextTick(); process.setStartTime(Date.now()); + + stdin = process.stdin = initStdin(); + + /** https://nodejs.org/api/process.html#process_process_stdout */ + stdout = process.stdout = createWritableStdioStream( + io.stdout, + "stdout", + ); + + /** https://nodejs.org/api/process.html#process_process_stderr */ + stderr = process.stderr = createWritableStdioStream( + io.stderr, + "stderr", + ); + // @ts-ignore Remove setStartTime and #startTime is not modifiable delete process.setStartTime; delete internals.__bootstrapNodeProcess; From 362fe089dfb3836e6fdaf9ab08d569422625fb35 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 28 Oct 2023 23:31:47 +0530 Subject: [PATCH 12/24] maybe fixes really --- ext/node/polyfills/_process/streams.mjs | 33 +++++++++++++------------ ext/node/polyfills/process.ts | 6 ++--- ext/node/polyfills/tty.js | 9 +++---- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index b6efef65ea6f73..f419739069fc1f 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -5,6 +5,7 @@ // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import { isatty } from "ext:runtime/40_tty.js"; import { clearLine, clearScreenDown, @@ -15,6 +16,7 @@ import { Duplex, Readable, Writable } from "node:stream"; import { isWindows } from "ext:deno_node/_util/os.ts"; import { fs as fsConstants } from "ext:deno_node/internal_binding/constants.ts"; import * as io from "ext:deno_io/12_io.js"; +import * as tty from "node:tty"; // https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41 export function createWritableStdioStream(writer, name) { @@ -23,7 +25,7 @@ export function createWritableStdioStream(writer, name) { write(buf, enc, cb) { if (!writer) { this.destroy( - new Error(`Deno.${name} is not available in this environment`), + new Error(`Deno.${name} is not available in this environment`) ); return; } @@ -100,7 +102,8 @@ export function createWritableStdioStream(writer, name) { // https://github.com/nodejs/node/blob/v18.12.1/src/node_util.cc#L257 function _guessStdinType(fd) { if (typeof fd !== "number" || fd < 0) return "UNKNOWN"; - if (Deno.isatty?.(fd)) return "TTY"; + // TODO: workaround on windows for non-TTY stdin + if (isWindows || isatty(fd)) return "TTY"; try { const fileInfo = Deno.fstatSync?.(fd); @@ -144,11 +147,14 @@ function _guessStdinType(fd) { const _read = function (size) { const p = Buffer.alloc(size || 16 * 1024); - io.stdin?.read(p).then((length) => { - this.push(length === null ? null : p.slice(0, length)); - }, (error) => { - this.destroy(error); - }); + io.stdin?.read(p).then( + (length) => { + this.push(length === null ? null : p.slice(0, length)); + }, + (error) => { + this.destroy(error); + } + ); }; /** https://nodejs.org/api/process.html#process_process_stdin */ @@ -172,17 +178,12 @@ export const initStdin = () => { }); break; } - case "TTY": + case "TTY": { + stdin = new tty.ReadStream(fd); + break; + } case "PIPE": case "TCP": { - // TODO(PolarETech): - // For TTY, `new Duplex()` should be replaced `new tty.ReadStream()` if possible. - // There are two problems that need to be resolved. - // 1. Using them here introduces a circular dependency. - // 2. Creating a tty.ReadStream() is not currently supported. - // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L194 - // https://github.com/nodejs/node/blob/v18.12.1/lib/tty.js#L47 - // For PIPE and TCP, `new Duplex()` should be replaced `new net.Socket()` if possible. // There are two problems that need to be resolved. // 1. Using them here introduces a circular dependency. diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 7f9230ebd869f9..fd7e1a4f82fdc6 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -866,10 +866,7 @@ internals.__bootstrapNodeProcess = function ( core.setMacrotaskCallback(runNextTicks); enableNextTick(); - process.setStartTime(Date.now()); - stdin = process.stdin = initStdin(); - /** https://nodejs.org/api/process.html#process_process_stdout */ stdout = process.stdout = createWritableStdioStream( io.stdout, @@ -882,9 +879,12 @@ internals.__bootstrapNodeProcess = function ( "stderr", ); + process.setStartTime(Date.now()); + // @ts-ignore Remove setStartTime and #startTime is not modifiable delete process.setStartTime; delete internals.__bootstrapNodeProcess; }; + export default process; diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js index 975e618eb9c3d1..6cc5ac62e0957a 100644 --- a/ext/node/polyfills/tty.js +++ b/ext/node/polyfills/tty.js @@ -1,10 +1,9 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -import { Socket } from "node:net"; import { ERR_INVALID_FD } from "ext:deno_node/internal/errors.ts"; import { LibuvStreamWrap } from "ext:deno_node/internal_binding/stream_wrap.ts"; import { providerType } from "ext:deno_node/internal_binding/async_wrap.ts"; - +import { Duplex } from "node:stream"; const { Error } = globalThis.__bootstrap.primordials; // Returns true when the given numeric fd is associated with a TTY and false otherwise. @@ -25,7 +24,7 @@ class TTY extends LibuvStreamWrap { } } -export class ReadStream extends Socket { +export class ReadStream extends Duplex { constructor(fd, options) { if (fd >> 0 !== fd || fd < 0) { throw new ERR_INVALID_FD(fd); @@ -55,7 +54,7 @@ export class ReadStream extends Socket { } } -export class WriteStream extends Socket { +export class WriteStream extends Duplex { constructor(fd) { if (fd >> 0 !== fd || fd < 0) { throw new ERR_INVALID_FD(fd); @@ -65,7 +64,7 @@ export class WriteStream extends Socket { if (fd > 2) throw new Error("Only fd 0, 1 and 2 are supported."); const tty = new TTY( - fd === 0 ? Deno.stdin : (fd === 1 ? Deno.stdout : Deno.stderr), + fd === 0 ? Deno.stdin : fd === 1 ? Deno.stdout : Deno.stderr ); super({ From 9e3e2a632679b8328386c4428e0f3a50ed5fd125 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 28 Oct 2023 23:33:00 +0530 Subject: [PATCH 13/24] fmt --- ext/node/polyfills/_process/streams.mjs | 4 ++-- ext/node/polyfills/process.ts | 1 - ext/node/polyfills/tty.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index f419739069fc1f..995f907cd4062d 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -25,7 +25,7 @@ export function createWritableStdioStream(writer, name) { write(buf, enc, cb) { if (!writer) { this.destroy( - new Error(`Deno.${name} is not available in this environment`) + new Error(`Deno.${name} is not available in this environment`), ); return; } @@ -153,7 +153,7 @@ const _read = function (size) { }, (error) => { this.destroy(error); - } + }, ); }; diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index fd7e1a4f82fdc6..a4fc3317d2a752 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -886,5 +886,4 @@ internals.__bootstrapNodeProcess = function ( delete internals.__bootstrapNodeProcess; }; - export default process; diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js index 6cc5ac62e0957a..54f8f6eae62098 100644 --- a/ext/node/polyfills/tty.js +++ b/ext/node/polyfills/tty.js @@ -64,7 +64,7 @@ export class WriteStream extends Duplex { if (fd > 2) throw new Error("Only fd 0, 1 and 2 are supported."); const tty = new TTY( - fd === 0 ? Deno.stdin : fd === 1 ? Deno.stdout : Deno.stderr + fd === 0 ? Deno.stdin : fd === 1 ? Deno.stdout : Deno.stderr, ); super({ From 4099f2aacf1be522fbb4fd2aca17f97127d6a407 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 29 Oct 2023 11:26:33 +0530 Subject: [PATCH 14/24] implement guessHandleType --- ext/node/lib.rs | 1 + ext/node/ops/mod.rs | 1 + ext/node/ops/util.rs | 76 +++++++++++++++++++++ ext/node/polyfills/_process/streams.mjs | 48 +------------ ext/node/polyfills/internal_binding/util.ts | 9 ++- 5 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 ext/node/ops/util.rs diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 3e63d30f4ded0e..730554f2d84d32 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -270,6 +270,7 @@ deno_core::extension!(deno_node, ops::require::op_require_read_package_scope

, ops::require::op_require_package_imports_resolve

, ops::require::op_require_break_on_next_statement, + ops::util::op_node_guess_handle_type, ], esm_entry_point = "ext:deno_node/02_init.js", esm = [ diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs index cf4abf3ddc5773..d1bb4b7f4802d9 100644 --- a/ext/node/ops/mod.rs +++ b/ext/node/ops/mod.rs @@ -6,6 +6,7 @@ pub mod http2; pub mod idna; pub mod os; pub mod require; +pub mod util; pub mod v8; pub mod winerror; pub mod zlib; diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs new file mode 100644 index 00000000000000..6e9cbc01ae1180 --- /dev/null +++ b/ext/node/ops/util.rs @@ -0,0 +1,76 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::OpState; +use deno_core::ResourceHandle; +use deno_core::ResourceHandleFd; + +#[repr(u32)] +enum HandleType { + #[allow(dead_code)] + TCP = 0, + TTY, + #[allow(dead_code)] + UDP, + FILE, + PIPE, + UNKNOWN, +} + +#[op2(fast)] +pub fn op_node_guess_handle_type( + state: &mut OpState, + rid: u32, +) -> Result { + let handle = state.resource_table.get_handle(rid)?; + let handle_type = match handle { + ResourceHandle::Fd(handle) => guess_handle_type(handle), + _ => HandleType::UNKNOWN, + }; + + Ok(handle_type as u32) +} + +#[cfg(windows)] +fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { + use winapi::um::{ + consoleapi::GetConsoleMode, + fileapi::GetFileType, + winbase::{FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE}, + }; + + let mut mode = 0; + match unsafe { GetFileType(handle) } { + FILE_TYPE_DISK => HandleType::FILE, + FILE_TYPE_CHAR => { + if unsafe { GetConsoleMode(handle, &mut mode) } == 1 { + HandleType::TTY + } else { + HandleType::FILE + } + } + FILE_TYPE_PIPE => HandleType::PIPE, + _ => HandleType::UNKNOWN, + } +} + +#[cfg(unix)] +fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { + // SAFETY: The resource remains open for the for the duration of borrow_raw + if unsafe { std::os::fd::BorrowedFd::borrow_raw(fd).is_terminal() } { + return HandleType::TTY; + } + + let mut s = unsafe { std::mem::zeroed() }; + if libc::fstat(handle, &mut s) == 1 { + return HandleType::UNKNOWN; + } + + match s.st_mode & 61440 { + libc::S_IFREG | libc::S_IFCHR => HandleType::FILE, + libc::S_IFIFO => HandleType::PIPE, + libc::S_IFSOCK => HandleType::TCP, + _ => HandleType::UNKNOWN, + } +} diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index f419739069fc1f..456921c866894d 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -13,10 +13,9 @@ import { moveCursor, } from "ext:deno_node/internal/readline/callbacks.mjs"; import { Duplex, Readable, Writable } from "node:stream"; -import { isWindows } from "ext:deno_node/_util/os.ts"; -import { fs as fsConstants } from "ext:deno_node/internal_binding/constants.ts"; import * as io from "ext:deno_io/12_io.js"; import * as tty from "node:tty"; +import { guessHandleType } from "ext:deno_node/internal_binding/util.ts"; // https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41 export function createWritableStdioStream(writer, name) { @@ -97,52 +96,9 @@ export function createWritableStdioStream(writer, name) { return stream; } -// TODO(PolarETech): This function should be replaced by -// `guessHandleType()` in "../internal_binding/util.ts". -// https://github.com/nodejs/node/blob/v18.12.1/src/node_util.cc#L257 function _guessStdinType(fd) { if (typeof fd !== "number" || fd < 0) return "UNKNOWN"; - // TODO: workaround on windows for non-TTY stdin - if (isWindows || isatty(fd)) return "TTY"; - - try { - const fileInfo = Deno.fstatSync?.(fd); - - // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/unix/tty.c#L333 - if (!isWindows) { - switch (fileInfo.mode & fsConstants.S_IFMT) { - case fsConstants.S_IFREG: - case fsConstants.S_IFCHR: - return "FILE"; - case fsConstants.S_IFIFO: - return "PIPE"; - case fsConstants.S_IFSOCK: - // TODO(PolarETech): Need a better way to identify "TCP". - // Currently, unable to exclude UDP. - return "TCP"; - default: - return "UNKNOWN"; - } - } - - // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/win/handle.c#L31 - if (fileInfo.isFile) { - // TODO(PolarETech): Need a better way to identify a piped stdin on Windows. - // On Windows, `Deno.fstatSync(rid).isFile` returns true even for a piped stdin. - // Therefore, a piped stdin cannot be distinguished from a file by this property. - // The mtime, atime, and birthtime of the file are "2339-01-01T00:00:00.000Z", - // so use the property as a workaround. - if (fileInfo.birthtime.valueOf() === 11644473600000) return "PIPE"; - return "FILE"; - } - } catch (e) { - // TODO(PolarETech): Need a better way to identify a character file on Windows. - // "EISDIR" error occurs when stdin is "null" on Windows, - // so use the error as a workaround. - if (isWindows && e.code === "EISDIR") return "FILE"; - } - - return "UNKNOWN"; + return guessHandleType(fd); } const _read = function (size) { diff --git a/ext/node/polyfills/internal_binding/util.ts b/ext/node/polyfills/internal_binding/util.ts index a2d355c1ebdf4c..f3b9ae3d776a46 100644 --- a/ext/node/polyfills/internal_binding/util.ts +++ b/ext/node/polyfills/internal_binding/util.ts @@ -28,10 +28,13 @@ // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials -import { notImplemented } from "ext:deno_node/_utils.ts"; +const core = globalThis.Deno.core; +const ops = core.ops; -export function guessHandleType(_fd: number): string { - notImplemented("util.guessHandleType"); +const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN']; +export function guessHandleType(fd: number): string { + const type = ops.op_node_guess_handle_type(fd); + return handleTypes[type]; } export const ALL_PROPERTIES = 0; From ad61842de78623ce686a3b5d3dcc7ade4349a059 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 29 Oct 2023 13:50:41 +0530 Subject: [PATCH 15/24] zzz --- ext/node/polyfills/_process/streams.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index 52d82034ee6f62..8c20dd8014d7c3 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -5,7 +5,6 @@ // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; -import { isatty } from "ext:runtime/40_tty.js"; import { clearLine, clearScreenDown, @@ -24,7 +23,7 @@ export function createWritableStdioStream(writer, name) { write(buf, enc, cb) { if (!writer) { this.destroy( - new Error(`Deno.${name} is not available in this environment`), + new Error(`Deno.${name} is not available in this environment`) ); return; } @@ -109,7 +108,7 @@ const _read = function (size) { }, (error) => { this.destroy(error); - }, + } ); }; From 29db367b79f5f77dbf86319c6f099c21a3934a7a Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 29 Oct 2023 16:34:30 +0530 Subject: [PATCH 16/24] fixes --- ext/node/ops/util.rs | 56 ++++++++++++--------- ext/node/polyfills/_process/streams.mjs | 4 +- ext/node/polyfills/internal_binding/util.ts | 2 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 6e9cbc01ae1180..769754f0df3bbd 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -9,13 +9,13 @@ use deno_core::ResourceHandleFd; #[repr(u32)] enum HandleType { #[allow(dead_code)] - TCP = 0, - TTY, + Tcp = 0, + Tty, #[allow(dead_code)] - UDP, - FILE, - PIPE, - UNKNOWN, + Udp, + File, + Pipe, + Unknown, } #[op2(fast)] @@ -26,7 +26,7 @@ pub fn op_node_guess_handle_type( let handle = state.resource_table.get_handle(rid)?; let handle_type = match handle { ResourceHandle::Fd(handle) => guess_handle_type(handle), - _ => HandleType::UNKNOWN, + _ => HandleType::Unknown, }; Ok(handle_type as u32) @@ -34,43 +34,49 @@ pub fn op_node_guess_handle_type( #[cfg(windows)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { - use winapi::um::{ - consoleapi::GetConsoleMode, - fileapi::GetFileType, - winbase::{FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE}, - }; + use winapi::um::consoleapi::GetConsoleMode; + use winapi::um::fileapi::GetFileType; + use winapi::um::winbase::FILE_TYPE_CHAR; + use winapi::um::winbase::FILE_TYPE_DISK; + use winapi::um::winbase::FILE_TYPE_PIPE; let mut mode = 0; + // SAFETY: Call to win32 fileapi. `handle` is a valid fd. match unsafe { GetFileType(handle) } { - FILE_TYPE_DISK => HandleType::FILE, + FILE_TYPE_DISK => HandleType::File, FILE_TYPE_CHAR => { + // SAFETY: Call to win32 consoleapi. `handle` is a valid fd. + // `mode` is a valid pointer. if unsafe { GetConsoleMode(handle, &mut mode) } == 1 { - HandleType::TTY + HandleType::Tty } else { - HandleType::FILE + HandleType::File } } - FILE_TYPE_PIPE => HandleType::PIPE, - _ => HandleType::UNKNOWN, + FILE_TYPE_PIPE => HandleType::Pipe, + _ => HandleType::Unknown, } } #[cfg(unix)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { + use std::io::IsTerminal; // SAFETY: The resource remains open for the for the duration of borrow_raw - if unsafe { std::os::fd::BorrowedFd::borrow_raw(fd).is_terminal() } { - return HandleType::TTY; + if unsafe { std::os::fd::BorrowedFd::borrow_raw(handle).is_terminal() } { + return HandleType::Tty; } + // SAFETY: It is safe to zero-initialize a `libc::stat` struct. let mut s = unsafe { std::mem::zeroed() }; - if libc::fstat(handle, &mut s) == 1 { - return HandleType::UNKNOWN; + // SAFETY: Call to libc + if unsafe { libc::fstat(handle, &mut s) } == 1 { + return HandleType::Unknown; } match s.st_mode & 61440 { - libc::S_IFREG | libc::S_IFCHR => HandleType::FILE, - libc::S_IFIFO => HandleType::PIPE, - libc::S_IFSOCK => HandleType::TCP, - _ => HandleType::UNKNOWN, + libc::S_IFREG | libc::S_IFCHR => HandleType::File, + libc::S_IFIFO => HandleType::Pipe, + libc::S_IFSOCK => HandleType::Tcp, + _ => HandleType::Unknown, } } diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs index 8c20dd8014d7c3..39ee89a8224138 100644 --- a/ext/node/polyfills/_process/streams.mjs +++ b/ext/node/polyfills/_process/streams.mjs @@ -23,7 +23,7 @@ export function createWritableStdioStream(writer, name) { write(buf, enc, cb) { if (!writer) { this.destroy( - new Error(`Deno.${name} is not available in this environment`) + new Error(`Deno.${name} is not available in this environment`), ); return; } @@ -108,7 +108,7 @@ const _read = function (size) { }, (error) => { this.destroy(error); - } + }, ); }; diff --git a/ext/node/polyfills/internal_binding/util.ts b/ext/node/polyfills/internal_binding/util.ts index f3b9ae3d776a46..38eeebee007624 100644 --- a/ext/node/polyfills/internal_binding/util.ts +++ b/ext/node/polyfills/internal_binding/util.ts @@ -31,7 +31,7 @@ const core = globalThis.Deno.core; const ops = core.ops; -const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN']; +const handleTypes = ["TCP", "TTY", "UDP", "FILE", "PIPE", "UNKNOWN"]; export function guessHandleType(fd: number): string { const type = ops.op_node_guess_handle_type(fd); return handleTypes[type]; From 2308af9e6259fe241c983d58307a0e947495865e Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 29 Oct 2023 19:41:26 +0530 Subject: [PATCH 17/24] x --- ext/node/ops/util.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 769754f0df3bbd..999326edc51bc4 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -24,6 +24,11 @@ pub fn op_node_guess_handle_type( rid: u32, ) -> Result { let handle = state.resource_table.get_handle(rid)?; + + if handle.is_terminal() { + return Ok(HandleType::Tty as u32); + } + let handle_type = match handle { ResourceHandle::Fd(handle) => guess_handle_type(handle), _ => HandleType::Unknown, @@ -43,16 +48,7 @@ fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { let mut mode = 0; // SAFETY: Call to win32 fileapi. `handle` is a valid fd. match unsafe { GetFileType(handle) } { - FILE_TYPE_DISK => HandleType::File, - FILE_TYPE_CHAR => { - // SAFETY: Call to win32 consoleapi. `handle` is a valid fd. - // `mode` is a valid pointer. - if unsafe { GetConsoleMode(handle, &mut mode) } == 1 { - HandleType::Tty - } else { - HandleType::File - } - } + FILE_TYPE_DISK | FILE_TYPE_CHAR => HandleType::File, FILE_TYPE_PIPE => HandleType::Pipe, _ => HandleType::Unknown, } @@ -60,12 +56,6 @@ fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { #[cfg(unix)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { - use std::io::IsTerminal; - // SAFETY: The resource remains open for the for the duration of borrow_raw - if unsafe { std::os::fd::BorrowedFd::borrow_raw(handle).is_terminal() } { - return HandleType::Tty; - } - // SAFETY: It is safe to zero-initialize a `libc::stat` struct. let mut s = unsafe { std::mem::zeroed() }; // SAFETY: Call to libc From 71bae420a37ff75f4b266780169eadc82e15c73b Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 29 Oct 2023 19:53:43 +0530 Subject: [PATCH 18/24] x --- ext/node/ops/util.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 999326edc51bc4..478ca6088535c2 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -39,13 +39,11 @@ pub fn op_node_guess_handle_type( #[cfg(windows)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { - use winapi::um::consoleapi::GetConsoleMode; use winapi::um::fileapi::GetFileType; use winapi::um::winbase::FILE_TYPE_CHAR; use winapi::um::winbase::FILE_TYPE_DISK; use winapi::um::winbase::FILE_TYPE_PIPE; - let mut mode = 0; // SAFETY: Call to win32 fileapi. `handle` is a valid fd. match unsafe { GetFileType(handle) } { FILE_TYPE_DISK | FILE_TYPE_CHAR => HandleType::File, From 7c350d305a89478cc03d70d52d9e78c6977052fe Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 19:21:24 +0530 Subject: [PATCH 19/24] add some tests --- .../test/parallel/test-tty-stdin-end.js | 7 ++++++ .../test/parallel/test-ttywrap-invalid-fd.js | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 cli/tests/node_compat/test/parallel/test-tty-stdin-end.js create mode 100644 cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js diff --git a/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js b/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js new file mode 100644 index 00000000000000..6bbda5166fca4f --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. +// https://github.com/nodejs/node/issues/1068 + +process.stdin.emit('end'); \ No newline at end of file diff --git a/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js b/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js new file mode 100644 index 00000000000000..0cb91da3410541 --- /dev/null +++ b/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js @@ -0,0 +1,25 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +'use strict'; + +const tty = require('tty'); +const assert = require('assert'); + +assert.throws( + () => new tty.WriteStream(-1), + { + code: 'ERR_INVALID_FD', + name: 'RangeError', + message: '"fd" must be a positive integer: -1' + } +); + +assert.throws( + () => new tty.ReadStream(-1), + { + code: 'ERR_INVALID_FD', + name: 'RangeError', + message: '"fd" must be a positive integer: -1' + } +); \ No newline at end of file From e9a79cf303cb4058504d3af1c0c687c48e425270 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 19:29:45 +0530 Subject: [PATCH 20/24] fix --- cli/tests/node_compat/config.jsonc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/tests/node_compat/config.jsonc b/cli/tests/node_compat/config.jsonc index 8a31f4dc264ace..c8bc3fcd50812b 100644 --- a/cli/tests/node_compat/config.jsonc +++ b/cli/tests/node_compat/config.jsonc @@ -625,6 +625,8 @@ "test-timers-unref-throw-then-ref.js", "test-timers-user-call.js", "test-timers-zero-timeout.js", + "test-tty-stdin-end.js", + "test-ttywrap-invalid-fd.js", "test-url-domain-ascii-unicode.js", "test-url-fileurltopath.js", "test-url-format-invalid-input.js", From 709b63592e58161f141481cb3599f20b7d68a3e3 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 19:37:11 +0530 Subject: [PATCH 21/24] x --- .../test/parallel/test-tty-stdin-end.js | 9 +++- .../test/parallel/test-ttywrap-invalid-fd.js | 51 ++++++++++++++++++- tools/node_compat/TODO.md | 4 +- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js b/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js index 6bbda5166fca4f..ee38cbd2cfc480 100644 --- a/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js +++ b/cli/tests/node_compat/test/parallel/test-tty-stdin-end.js @@ -1,7 +1,14 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually. + 'use strict'; require('../common'); // This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. // https://github.com/nodejs/node/issues/1068 -process.stdin.emit('end'); \ No newline at end of file +process.stdin.emit('end'); diff --git a/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js b/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js index 0cb91da3410541..95b9bffe6ab49f 100644 --- a/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js +++ b/cli/tests/node_compat/test/parallel/test-ttywrap-invalid-fd.js @@ -1,9 +1,20 @@ // deno-fmt-ignore-file // deno-lint-ignore-file +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually. + +// Flags: --expose-internals 'use strict'; +// const common = require('../common'); const tty = require('tty'); +// const { internalBinding } = require('internal/test/binding'); +// const { +// UV_EBADF, +// UV_EINVAL +// } = internalBinding('uv'); const assert = require('assert'); assert.throws( @@ -15,6 +26,44 @@ assert.throws( } ); +// { +// const info = { +// code: common.isWindows ? 'EBADF' : 'EINVAL', +// message: common.isWindows ? 'bad file descriptor' : 'invalid argument', +// errno: common.isWindows ? UV_EBADF : UV_EINVAL, +// syscall: 'uv_tty_init' +// }; + +// const suffix = common.isWindows ? +// 'EBADF (bad file descriptor)' : 'EINVAL (invalid argument)'; +// const message = `TTY initialization failed: uv_tty_init returned ${suffix}`; + +// assert.throws( +// () => { +// common.runWithInvalidFD((fd) => { +// new tty.WriteStream(fd); +// }); +// }, { +// code: 'ERR_TTY_INIT_FAILED', +// name: 'SystemError', +// message, +// info +// } +// ); + +// assert.throws( +// () => { +// common.runWithInvalidFD((fd) => { +// new tty.ReadStream(fd); +// }); +// }, { +// code: 'ERR_TTY_INIT_FAILED', +// name: 'SystemError', +// message, +// info +// }); +// } + assert.throws( () => new tty.ReadStream(-1), { @@ -22,4 +71,4 @@ assert.throws( name: 'RangeError', message: '"fd" must be a positive integer: -1' } -); \ No newline at end of file +); diff --git a/tools/node_compat/TODO.md b/tools/node_compat/TODO.md index 41dfb70b0e788d..ecb917bed7ea8f 100644 --- a/tools/node_compat/TODO.md +++ b/tools/node_compat/TODO.md @@ -3,7 +3,7 @@ NOTE: This file should not be manually edited. Please edit `cli/tests/node_compat/config.json` and run `deno task setup` in `tools/node_compat` dir instead. -Total: 2924 +Total: 2922 - [abort/test-abort-backtrace.js](https://github.com/nodejs/node/tree/v18.12.1/test/abort/test-abort-backtrace.js) - [abort/test-abort-fatal-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/abort/test-abort-fatal-error.js) @@ -2356,9 +2356,7 @@ Total: 2924 - [parallel/test-trace-exit.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-trace-exit.js) - [parallel/test-tracing-no-crash.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tracing-no-crash.js) - [parallel/test-tty-backwards-api.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tty-backwards-api.js) -- [parallel/test-tty-stdin-end.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tty-stdin-end.js) - [parallel/test-tty-stdin-pipe.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tty-stdin-pipe.js) -- [parallel/test-ttywrap-invalid-fd.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-ttywrap-invalid-fd.js) - [parallel/test-ttywrap-stack.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-ttywrap-stack.js) - [parallel/test-unhandled-exception-rethrow-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-unhandled-exception-rethrow-error.js) - [parallel/test-unicode-node-options.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-unicode-node-options.js) From 28db64ced1e5a0b625f64a5be4acf9da785f3ea6 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 19:44:28 +0530 Subject: [PATCH 22/24] x --- cli/tests/node_compat/config.jsonc | 2 +- tools/node_compat/TODO.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/tests/node_compat/config.jsonc b/cli/tests/node_compat/config.jsonc index c8bc3fcd50812b..93a51b671383e2 100644 --- a/cli/tests/node_compat/config.jsonc +++ b/cli/tests/node_compat/config.jsonc @@ -88,6 +88,7 @@ "test-querystring.js", "test-readline-interface.js", "test-stdin-from-file-spawn.js", + "test-ttywrap-invalid-fd.js", "test-url-urltooptions.js", "test-util-format.js", "test-util-inspect-namespace.js", @@ -626,7 +627,6 @@ "test-timers-user-call.js", "test-timers-zero-timeout.js", "test-tty-stdin-end.js", - "test-ttywrap-invalid-fd.js", "test-url-domain-ascii-unicode.js", "test-url-fileurltopath.js", "test-url-format-invalid-input.js", diff --git a/tools/node_compat/TODO.md b/tools/node_compat/TODO.md index ecb917bed7ea8f..b4c971d8918100 100644 --- a/tools/node_compat/TODO.md +++ b/tools/node_compat/TODO.md @@ -3,7 +3,7 @@ NOTE: This file should not be manually edited. Please edit `cli/tests/node_compat/config.json` and run `deno task setup` in `tools/node_compat` dir instead. -Total: 2922 +Total: 2923 - [abort/test-abort-backtrace.js](https://github.com/nodejs/node/tree/v18.12.1/test/abort/test-abort-backtrace.js) - [abort/test-abort-fatal-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/abort/test-abort-fatal-error.js) @@ -2357,6 +2357,7 @@ Total: 2922 - [parallel/test-tracing-no-crash.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tracing-no-crash.js) - [parallel/test-tty-backwards-api.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tty-backwards-api.js) - [parallel/test-tty-stdin-pipe.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-tty-stdin-pipe.js) +- [parallel/test-ttywrap-invalid-fd.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-ttywrap-invalid-fd.js) - [parallel/test-ttywrap-stack.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-ttywrap-stack.js) - [parallel/test-unhandled-exception-rethrow-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-unhandled-exception-rethrow-error.js) - [parallel/test-unicode-node-options.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-unicode-node-options.js) From 8896c9cb25777227c112ddd81aee8054df15727f Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 20:46:34 +0530 Subject: [PATCH 23/24] x --- ext/node/ops/util.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index 478ca6088535c2..f9231d37817da7 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -25,10 +25,6 @@ pub fn op_node_guess_handle_type( ) -> Result { let handle = state.resource_table.get_handle(rid)?; - if handle.is_terminal() { - return Ok(HandleType::Tty as u32); - } - let handle_type = match handle { ResourceHandle::Fd(handle) => guess_handle_type(handle), _ => HandleType::Unknown, @@ -39,6 +35,7 @@ pub fn op_node_guess_handle_type( #[cfg(windows)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { + use winapi::um::consoleapi::GetConsoleMode; use winapi::um::fileapi::GetFileType; use winapi::um::winbase::FILE_TYPE_CHAR; use winapi::um::winbase::FILE_TYPE_DISK; @@ -46,7 +43,17 @@ fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { // SAFETY: Call to win32 fileapi. `handle` is a valid fd. match unsafe { GetFileType(handle) } { - FILE_TYPE_DISK | FILE_TYPE_CHAR => HandleType::File, + FILE_TYPE_DISK => HandleType::File, + FILE_TYPE_CHAR => { + let mut mode = 0; + // SAFETY: Call to win32 consoleapi. `handle` is a valid fd. + // `mode` is a valid pointer. + if unsafe { GetConsoleMode(handle, &mut mode) } == 1 { + HandleType::Tty + } else { + HandleType::File + } + } FILE_TYPE_PIPE => HandleType::Pipe, _ => HandleType::Unknown, } @@ -54,6 +61,12 @@ fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { #[cfg(unix)] fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { + use std::io::IsTerminal; + // SAFETY: The resource remains open for the duration of borrow_raw. + if unsafe { std::os::fs::BorrowedFd::borrow_raw(handle).is_terminal() } { + return HandleType::Tty; + } + // SAFETY: It is safe to zero-initialize a `libc::stat` struct. let mut s = unsafe { std::mem::zeroed() }; // SAFETY: Call to libc From 3d56be1f607deac3d34ef3dc67a60d86625e17b1 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 30 Oct 2023 20:50:27 +0530 Subject: [PATCH 24/24] x --- ext/node/ops/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/node/ops/util.rs b/ext/node/ops/util.rs index f9231d37817da7..1cb80e0e3bf14a 100644 --- a/ext/node/ops/util.rs +++ b/ext/node/ops/util.rs @@ -63,7 +63,7 @@ fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { fn guess_handle_type(handle: ResourceHandleFd) -> HandleType { use std::io::IsTerminal; // SAFETY: The resource remains open for the duration of borrow_raw. - if unsafe { std::os::fs::BorrowedFd::borrow_raw(handle).is_terminal() } { + if unsafe { std::os::fd::BorrowedFd::borrow_raw(handle).is_terminal() } { return HandleType::Tty; }