diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index b6137e0d17dd63..cabae63ee4e49f 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -362,17 +362,25 @@ export class ChildProcess extends EventEmitter { } } -const supportedNodeStdioTypes: NodeStdio[] = ["pipe", "ignore", "inherit"]; +const supportedNodeStdioTypes: NodeStdio[] = [ + "pipe", + "ignore", + "inherit", + "ipc", +]; function toDenoStdio( pipe: NodeStdio | number | Stream | null | undefined, ): DenoStdio { if (pipe instanceof Stream) { return "inherit"; } + if (typeof pipe === "number") { + /* Assume it's a rid returned by fs APIs */ + return pipe; + } if ( - !supportedNodeStdioTypes.includes(pipe as NodeStdio) || - typeof pipe === "number" + !supportedNodeStdioTypes.includes(pipe as NodeStdio) ) { notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`); } @@ -385,6 +393,8 @@ function toDenoStdio( return "null"; case "inherit": return "inherit"; + case "ipc": + return "ipc_for_internal_use"; default: notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`); } @@ -1083,8 +1093,7 @@ function toDenoArgs(args: string[]): string[] { if (useRunArgs) { // -A is not ideal, but needed to propagate permissions. - // --unstable is needed for Node compat. - denoArgs.unshift("run", "-A", "--unstable"); + denoArgs.unshift("run", "-A"); } return denoArgs; diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index b894b35db7376f..ecf6ef49bb9e01 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -37,11 +37,12 @@ use std::os::unix::process::CommandExt; pub const UNSTABLE_FEATURE_NAME: &str = "process"; #[derive(Copy, Clone, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "snake_case")] pub enum Stdio { Inherit, Piped, Null, + IpcForInternalUse, } impl Stdio { @@ -50,6 +51,7 @@ impl Stdio { Stdio::Inherit => std::process::Stdio::inherit(), Stdio::Piped => std::process::Stdio::piped(), Stdio::Null => std::process::Stdio::null(), + _ => unreachable!(), } } } @@ -72,6 +74,9 @@ impl<'de> Deserialize<'de> for StdioOrRid { "inherit" => Ok(StdioOrRid::Stdio(Stdio::Inherit)), "piped" => Ok(StdioOrRid::Stdio(Stdio::Piped)), "null" => Ok(StdioOrRid::Stdio(Stdio::Null)), + "ipc_for_internal_use" => { + Ok(StdioOrRid::Stdio(Stdio::IpcForInternalUse)) + } val => Err(serde::de::Error::unknown_variant( val, &["inherit", "piped", "null"], @@ -102,6 +107,10 @@ impl StdioOrRid { } } } + + pub fn is_ipc(&self) -> bool { + matches!(self, StdioOrRid::Stdio(Stdio::IpcForInternalUse)) + } } deno_core::extension!( @@ -150,9 +159,9 @@ pub struct SpawnArgs { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct ChildStdio { - stdin: Stdio, - stdout: Stdio, - stderr: Stdio, + stdin: StdioOrRid, + stdout: StdioOrRid, + stderr: StdioOrRid, } #[derive(Serialize)] @@ -210,7 +219,7 @@ type CreateCommand = (std::process::Command, Option); fn create_command( state: &mut OpState, - args: SpawnArgs, + mut args: SpawnArgs, api_name: &str, ) -> Result { state @@ -249,14 +258,19 @@ fn create_command( command.uid(uid); } - command.stdin(args.stdio.stdin.as_stdio()); + if args.stdio.stdin.is_ipc() { + args.ipc = Some(0); + } else { + command.stdin(args.stdio.stdin.as_stdio(state)?); + } + command.stdout(match args.stdio.stdout { - Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?, - value => value.as_stdio(), + StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(1).as_stdio(state)?, + value => value.as_stdio(state)?, }); command.stderr(match args.stdio.stderr { - Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?, - value => value.as_stdio(), + StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(2).as_stdio(state)?, + value => value.as_stdio(state)?, }); #[cfg(unix)] @@ -608,8 +622,8 @@ fn op_spawn_sync( state: &mut OpState, #[serde] args: SpawnArgs, ) -> Result { - let stdout = matches!(args.stdio.stdout, Stdio::Piped); - let stderr = matches!(args.stdio.stderr, Stdio::Piped); + let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped)); + let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped)); let (mut command, _) = create_command(state, args, "Deno.Command().outputSync()")?; let output = command.output().with_context(|| { diff --git a/tests/specs/node/stdio_ipc/__test__.jsonc b/tests/specs/node/stdio_ipc/__test__.jsonc new file mode 100644 index 00000000000000..8ec9880fd57192 --- /dev/null +++ b/tests/specs/node/stdio_ipc/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "run -A main.mjs", + "output": "main.out", + "exitCode": 0 +} diff --git a/tests/specs/node/stdio_ipc/main.mjs b/tests/specs/node/stdio_ipc/main.mjs new file mode 100644 index 00000000000000..4a1a8ddbda7597 --- /dev/null +++ b/tests/specs/node/stdio_ipc/main.mjs @@ -0,0 +1,16 @@ +import { spawn } from "node:child_process"; +import process from "node:process"; + +if (process.argv[2] === "child") { + process.send("hahah"); +} else { + const proc = spawn(process.execPath, ["./main.mjs", "child"], { + stdio: ["ipc", "inherit", "inherit"], + }); + + proc.on("message", function (msg) { + console.log(`msg: ${msg}`); + proc.kill(); + Deno.exit(0); + }); +} diff --git a/tests/specs/node/stdio_ipc/main.out b/tests/specs/node/stdio_ipc/main.out new file mode 100644 index 00000000000000..7979ca2ebdd559 --- /dev/null +++ b/tests/specs/node/stdio_ipc/main.out @@ -0,0 +1 @@ +msg: hahah