diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/js/40_spawn.js | 126 | ||||
-rw-r--r-- | runtime/js/90_deno_ns.js | 1 | ||||
-rw-r--r-- | runtime/js/99_main.js | 70 | ||||
-rw-r--r-- | runtime/ops/spawn.rs | 120 |
4 files changed, 251 insertions, 66 deletions
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js index a927d619e..4d2fb1607 100644 --- a/runtime/js/40_spawn.js +++ b/runtime/js/40_spawn.js @@ -26,7 +26,7 @@ const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); - function spawnChildInner(command, apiName, { + function spawnChildInner(opFn, command, apiName, { args = [], cwd = undefined, clearEnv = false, @@ -39,7 +39,7 @@ signal = undefined, windowsRawArguments = false, } = {}) { - const child = ops.op_spawn_child({ + const child = opFn({ cmd: pathFromURL(command), args: ArrayPrototypeMap(args, String), cwd: pathFromURL(cwd), @@ -58,8 +58,10 @@ }); } - function spawnChild(command, options = {}) { - return spawnChildInner(command, "Deno.spawnChild()", options); + function createSpawnChild(opFn) { + return function spawnChild(command, options = {}) { + return spawnChildInner(opFn, command, "Deno.spawnChild()", options); + }; } async function collectOutput(readableStream) { @@ -227,68 +229,72 @@ } } - function spawn(command, options) { - if (options?.stdin === "piped") { - throw new TypeError( - "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", - ); - } - return spawnChildInner(command, "Deno.spawn()", options).output(); + function createSpawn(opFn) { + return function spawn(command, options) { + if (options?.stdin === "piped") { + throw new TypeError( + "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", + ); + } + return spawnChildInner(opFn, command, "Deno.spawn()", options).output(); + }; } - function spawnSync(command, { - args = [], - cwd = undefined, - clearEnv = false, - env = {}, - uid = undefined, - gid = undefined, - stdin = "null", - stdout = "piped", - stderr = "piped", - windowsRawArguments = false, - } = {}) { - if (stdin === "piped") { - throw new TypeError( - "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", - ); - } - const result = ops.op_spawn_sync({ - cmd: pathFromURL(command), - args: ArrayPrototypeMap(args, String), - cwd: pathFromURL(cwd), - clearEnv, - env: ObjectEntries(env), - uid, - gid, - stdin, - stdout, - stderr, - windowsRawArguments, - }); - return { - success: result.status.success, - code: result.status.code, - signal: result.status.signal, - get stdout() { - if (result.stdout == null) { - throw new TypeError("stdout is not piped"); - } - return result.stdout; - }, - get stderr() { - if (result.stderr == null) { - throw new TypeError("stderr is not piped"); - } - return result.stderr; - }, + function createSpawnSync(opFn) { + return function spawnSync(command, { + args = [], + cwd = undefined, + clearEnv = false, + env = {}, + uid = undefined, + gid = undefined, + stdin = "null", + stdout = "piped", + stderr = "piped", + windowsRawArguments = false, + } = {}) { + if (stdin === "piped") { + throw new TypeError( + "Piped stdin is not supported for this function, use 'Deno.spawnChild()' instead", + ); + } + const result = opFn({ + cmd: pathFromURL(command), + args: ArrayPrototypeMap(args, String), + cwd: pathFromURL(cwd), + clearEnv, + env: ObjectEntries(env), + uid, + gid, + stdin, + stdout, + stderr, + windowsRawArguments, + }); + return { + success: result.status.success, + code: result.status.code, + signal: result.status.signal, + get stdout() { + if (result.stdout == null) { + throw new TypeError("stdout is not piped"); + } + return result.stdout; + }, + get stderr() { + if (result.stderr == null) { + throw new TypeError("stderr is not piped"); + } + return result.stderr; + }, + }; }; } window.__bootstrap.spawn = { Child, - spawnChild, - spawn, - spawnSync, + createSpawn, + createSpawnChild, + createSpawnSync, }; })(this); diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 64653d469..033ad421e 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -4,6 +4,7 @@ ((window) => { const core = window.Deno.core; const __bootstrap = window.__bootstrap; + __bootstrap.denoNs = { metrics: core.metrics, test: __bootstrap.testing.test, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index ef99969b2..9b4a9e857 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -700,6 +700,7 @@ delete Intl.v8BreakIterator; const wrapConsole = window.__bootstrap.console.wrapConsole; // Remove bootstrapping data from the global scope + const __bootstrap = globalThis.__bootstrap; delete globalThis.__bootstrap; delete globalThis.bootstrap; util.log("bootstrapMainRuntime"); @@ -755,6 +756,27 @@ delete Intl.v8BreakIterator; const internalSymbol = Symbol("Deno.internal"); + // These have to initialized here and not in `90_deno_ns.js` because + // the op function that needs to be passed will be invalidated by creating + // a snapshot + ObjectAssign(internals, { + nodeUnstable: { + spawnChild: __bootstrap.spawn.createSpawnChild( + ops.op_node_unstable_spawn_child, + ), + spawn: __bootstrap.spawn.createSpawn(ops.op_node_unstable_spawn_child), + spawnSync: __bootstrap.spawn.createSpawnSync( + ops.op_node_unstable_spawn_sync, + ), + serve: __bootstrap.flash.createServe(ops.op_node_unstable_flash_serve), + upgradeHttpRaw: __bootstrap.flash.upgradeHttpRaw, + listenDatagram: __bootstrap.net.createListenDatagram( + ops.op_node_unstable_net_listen_udp, + ops.op_node_unstable_net_listen_unixpacket, + ), + }, + }); + const finalDenoNs = { core, internal: internalSymbol, @@ -773,6 +795,19 @@ delete Intl.v8BreakIterator; if (runtimeOptions.unstableFlag) { ObjectAssign(finalDenoNs, denoNsUnstable); + // These have to initialized here and not in `90_deno_ns.js` because + // the op function that needs to be passed will be invalidated by creating + // a snapshot + ObjectAssign(finalDenoNs, { + spawnChild: __bootstrap.spawn.createSpawnChild(ops.op_spawn_child), + spawn: __bootstrap.spawn.createSpawn(ops.op_spawn_child), + spawnSync: __bootstrap.spawn.createSpawnSync(ops.op_spawn_sync), + serve: __bootstrap.flash.createServe(ops.op_flash_serve), + listenDatagram: __bootstrap.net.createListenDatagram( + ops.op_net_listen_udp, + ops.op_net_listen_unixpacket, + ), + }); } // Setup `Deno` global - we're actually overriding already existing global @@ -800,6 +835,7 @@ delete Intl.v8BreakIterator; const wrapConsole = window.__bootstrap.console.wrapConsole; // Remove bootstrapping data from the global scope + const __bootstrap = globalThis.__bootstrap; delete globalThis.__bootstrap; delete globalThis.bootstrap; util.log("bootstrapWorkerRuntime"); @@ -849,6 +885,27 @@ delete Intl.v8BreakIterator; const internalSymbol = Symbol("Deno.internal"); + // These have to initialized here and not in `90_deno_ns.js` because + // the op function that needs to be passed will be invalidated by creating + // a snapshot + ObjectAssign(internals, { + nodeUnstable: { + spawnChild: __bootstrap.spawn.createSpawnChild( + ops.op_node_unstable_spawn_child, + ), + spawn: __bootstrap.spawn.createSpawn(ops.op_node_unstable_spawn_child), + spawnSync: __bootstrap.spawn.createSpawnSync( + ops.op_node_unstable_spawn_sync, + ), + serve: __bootstrap.flash.createServe(ops.op_node_unstable_flash_serve), + upgradeHttpRaw: __bootstrap.flash.upgradeHttpRaw, + listenDatagram: __bootstrap.net.createListenDatagram( + ops.op_node_unstable_net_listen_udp, + ops.op_node_unstable_net_listen_unixpacket, + ), + }, + }); + const finalDenoNs = { core, internal: internalSymbol, @@ -859,6 +916,19 @@ delete Intl.v8BreakIterator; }; if (runtimeOptions.unstableFlag) { ObjectAssign(finalDenoNs, denoNsUnstable); + // These have to initialized here and not in `90_deno_ns.js` because + // the op function that needs to be passed will be invalidated by creating + // a snapshot + ObjectAssign(finalDenoNs, { + spawnChild: __bootstrap.spawn.createSpawnChild(ops.op_spawn_child), + spawn: __bootstrap.spawn.createSpawn(ops.op_spawn_child), + spawnSync: __bootstrap.spawn.createSpawnSync(ops.op_spawn_sync), + serve: __bootstrap.flash.createServe(ops.op_flash_serve), + listenDatagram: __bootstrap.net.createListenDatagram( + ops.op_net_listen_udp, + ops.op_net_listen_unixpacket, + ), + }); } ObjectDefineProperties(finalDenoNs, { pid: util.readOnly(runtimeOptions.pid), diff --git a/runtime/ops/spawn.rs b/runtime/ops/spawn.rs index 7fe77302a..03ab7d5c2 100644 --- a/runtime/ops/spawn.rs +++ b/runtime/ops/spawn.rs @@ -31,8 +31,10 @@ pub fn init() -> Extension { Extension::builder() .ops(vec![ op_spawn_child::decl(), + op_node_unstable_spawn_child::decl(), op_spawn_wait::decl(), op_spawn_sync::decl(), + op_node_unstable_spawn_sync::decl(), ]) .build() } @@ -123,6 +125,70 @@ pub struct SpawnOutput { stderr: Option<ZeroCopyBuf>, } +fn node_unstable_create_command( + state: &mut OpState, + args: SpawnArgs, + api_name: &str, +) -> Result<std::process::Command, AnyError> { + state + .borrow_mut::<Permissions>() + .run + .check(&args.cmd, Some(api_name))?; + + let mut command = std::process::Command::new(args.cmd); + + #[cfg(windows)] + if args.windows_raw_arguments { + for arg in args.args.iter() { + command.raw_arg(arg); + } + } else { + command.args(args.args); + } + + #[cfg(not(windows))] + command.args(args.args); + + if let Some(cwd) = args.cwd { + command.current_dir(cwd); + } + + if args.clear_env { + command.env_clear(); + } + command.envs(args.env); + + #[cfg(unix)] + if let Some(gid) = args.gid { + command.gid(gid); + } + #[cfg(unix)] + if let Some(uid) = args.uid { + command.uid(uid); + } + #[cfg(unix)] + // TODO(bartlomieju): + #[allow(clippy::undocumented_unsafe_blocks)] + unsafe { + command.pre_exec(|| { + libc::setgroups(0, std::ptr::null()); + Ok(()) + }); + } + + command.stdin(args.stdio.stdin.as_stdio()); + command.stdout(match args.stdio.stdout { + Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?, + value => value.as_stdio(), + }); + command.stderr(match args.stdio.stderr { + Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?, + value => value.as_stdio(), + }); + + Ok(command) +} + fn create_command( state: &mut OpState, args: SpawnArgs, @@ -200,14 +266,11 @@ struct Child { stderr_rid: Option<ResourceId>, } -#[op] -fn op_spawn_child( +fn spawn_child( state: &mut OpState, - args: SpawnArgs, - api_name: String, + command: std::process::Command, ) -> Result<Child, AnyError> { - let mut command = - tokio::process::Command::from(create_command(state, args, &api_name)?); + let mut command = tokio::process::Command::from(command); // TODO(@crowlkats): allow detaching processes. // currently deno will orphan a process when exiting with an error or Deno.exit() // We want to kill child when it's closed @@ -243,6 +306,26 @@ fn op_spawn_child( } #[op] +fn op_spawn_child( + state: &mut OpState, + args: SpawnArgs, + api_name: String, +) -> Result<Child, AnyError> { + let command = create_command(state, args, &api_name)?; + spawn_child(state, command) +} + +#[op] +fn op_node_unstable_spawn_child( + state: &mut OpState, + args: SpawnArgs, + api_name: String, +) -> Result<Child, AnyError> { + let command = node_unstable_create_command(state, args, &api_name)?; + spawn_child(state, command) +} + +#[op] async fn op_spawn_wait( state: Rc<RefCell<OpState>>, rid: ResourceId, @@ -283,3 +366,28 @@ fn op_spawn_sync( }, }) } + +#[op] +fn op_node_unstable_spawn_sync( + state: &mut OpState, + args: SpawnArgs, +) -> Result<SpawnOutput, AnyError> { + let stdout = matches!(args.stdio.stdout, Stdio::Piped); + let stderr = matches!(args.stdio.stderr, Stdio::Piped); + let output = + node_unstable_create_command(state, args, "Deno.spawnSync()")?.output()?; + + Ok(SpawnOutput { + status: output.status.try_into()?, + stdout: if stdout { + Some(output.stdout.into()) + } else { + None + }, + stderr: if stderr { + Some(output.stderr.into()) + } else { + None + }, + }) +} |