summaryrefslogtreecommitdiff
path: root/runtime/js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-03-05 08:19:34 -0400
committerGitHub <noreply@github.com>2023-03-05 12:19:34 +0000
commitde0d148d933520e7ee519576c83e4ca282ee9021 (patch)
treed54f8fd691691ef5df08b61d3c558273f08bcab8 /runtime/js
parentd4807f458e852e6a8385a852e7caf9dd4a5b54f7 (diff)
refactor(runtime): merge "spawn" into "process" (#18022)
This commit merges "runtime/js/40_spawn.js" into "runtime/js/40_process.js", and "runtime::ops::spawn" into "runtime::ops::process". It makes little sense to have them separated given that we want to factor out these APIs into a separate extension crate.
Diffstat (limited to 'runtime/js')
-rw-r--r--runtime/js/40_process.js321
-rw-r--r--runtime/js/40_spawn.js326
-rw-r--r--runtime/js/90_deno_ns.js5
3 files changed, 318 insertions, 334 deletions
diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js
index bb224ae9c..9599b7b74 100644
--- a/runtime/js/40_process.js
+++ b/runtime/js/40_process.js
@@ -2,10 +2,6 @@
const core = globalThis.Deno.core;
const ops = core.ops;
-import { FsFile } from "internal:runtime/30_fs.js";
-import { readAll } from "internal:deno_io/12_io.js";
-import { pathFromURL } from "internal:runtime/06_util.js";
-import { assert } from "internal:deno_web/00_infra.js";
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayPrototypeMap,
@@ -14,7 +10,25 @@ const {
ObjectEntries,
SafeArrayIterator,
String,
+ ObjectPrototypeIsPrototypeOf,
+ PromisePrototypeThen,
+ SafePromiseAll,
+ SymbolFor,
+ Symbol,
} = primordials;
+import { FsFile } from "internal:runtime/30_fs.js";
+import { readAll } from "internal:deno_io/12_io.js";
+import { pathFromURL } from "internal:runtime/06_util.js";
+import { assert } from "internal:deno_web/00_infra.js";
+import * as abortSignal from "internal:deno_web/03_abort_signal.js";
+import {
+ readableStreamCollectIntoUint8Array,
+ readableStreamForRidUnrefable,
+ readableStreamForRidUnrefableRef,
+ readableStreamForRidUnrefableUnref,
+ ReadableStreamPrototype,
+ writableStreamForRid,
+} from "internal:deno_web/06_streams.js";
function opKill(pid, signo, apiName) {
ops.op_kill(pid, signo, apiName);
@@ -130,4 +144,301 @@ function run({
return new Process(res);
}
-export { kill, Process, run };
+const illegalConstructorKey = Symbol("illegalConstructorKey");
+const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
+
+function spawnChildInner(opFn, command, apiName, {
+ args = [],
+ cwd = undefined,
+ clearEnv = false,
+ env = {},
+ uid = undefined,
+ gid = undefined,
+ stdin = "null",
+ stdout = "piped",
+ stderr = "piped",
+ signal = undefined,
+ windowsRawArguments = false,
+} = {}) {
+ const child = opFn({
+ cmd: pathFromURL(command),
+ args: ArrayPrototypeMap(args, String),
+ cwd: pathFromURL(cwd),
+ clearEnv,
+ env: ObjectEntries(env),
+ uid,
+ gid,
+ stdin,
+ stdout,
+ stderr,
+ windowsRawArguments,
+ }, apiName);
+ return new ChildProcess(illegalConstructorKey, {
+ ...child,
+ signal,
+ });
+}
+
+function spawnChild(command, options = {}) {
+ return spawnChildInner(
+ ops.op_spawn_child,
+ command,
+ "Deno.Command().spawn()",
+ options,
+ );
+}
+
+function collectOutput(readableStream) {
+ if (
+ !(ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, readableStream))
+ ) {
+ return null;
+ }
+
+ return readableStreamCollectIntoUint8Array(readableStream);
+}
+
+class ChildProcess {
+ #rid;
+ #waitPromiseId;
+ #unrefed = false;
+
+ #pid;
+ get pid() {
+ return this.#pid;
+ }
+
+ #stdin = null;
+ get stdin() {
+ if (this.#stdin == null) {
+ throw new TypeError("stdin is not piped");
+ }
+ return this.#stdin;
+ }
+
+ #stdoutRid;
+ #stdout = null;
+ get stdout() {
+ if (this.#stdout == null) {
+ throw new TypeError("stdout is not piped");
+ }
+ return this.#stdout;
+ }
+
+ #stderrRid;
+ #stderr = null;
+ get stderr() {
+ if (this.#stderr == null) {
+ throw new TypeError("stderr is not piped");
+ }
+ return this.#stderr;
+ }
+
+ constructor(key = null, {
+ signal,
+ rid,
+ pid,
+ stdinRid,
+ stdoutRid,
+ stderrRid,
+ } = null) {
+ if (key !== illegalConstructorKey) {
+ throw new TypeError("Illegal constructor.");
+ }
+
+ this.#rid = rid;
+ this.#pid = pid;
+
+ if (stdinRid !== null) {
+ this.#stdin = writableStreamForRid(stdinRid);
+ }
+
+ if (stdoutRid !== null) {
+ this.#stdoutRid = stdoutRid;
+ this.#stdout = readableStreamForRidUnrefable(stdoutRid);
+ }
+
+ if (stderrRid !== null) {
+ this.#stderrRid = stderrRid;
+ this.#stderr = readableStreamForRidUnrefable(stderrRid);
+ }
+
+ const onAbort = () => this.kill("SIGTERM");
+ signal?.[abortSignal.add](onAbort);
+
+ const waitPromise = core.opAsync("op_spawn_wait", this.#rid);
+ this.#waitPromiseId = waitPromise[promiseIdSymbol];
+ this.#status = PromisePrototypeThen(waitPromise, (res) => {
+ this.#rid = null;
+ signal?.[abortSignal.remove](onAbort);
+ return res;
+ });
+ }
+
+ #status;
+ get status() {
+ return this.#status;
+ }
+
+ async output() {
+ if (this.#stdout?.locked) {
+ throw new TypeError(
+ "Can't collect output because stdout is locked",
+ );
+ }
+ if (this.#stderr?.locked) {
+ throw new TypeError(
+ "Can't collect output because stderr is locked",
+ );
+ }
+
+ const { 0: status, 1: stdout, 2: stderr } = await SafePromiseAll([
+ this.#status,
+ collectOutput(this.#stdout),
+ collectOutput(this.#stderr),
+ ]);
+
+ return {
+ success: status.success,
+ code: status.code,
+ signal: status.signal,
+ get stdout() {
+ if (stdout == null) {
+ throw new TypeError("stdout is not piped");
+ }
+ return stdout;
+ },
+ get stderr() {
+ if (stderr == null) {
+ throw new TypeError("stderr is not piped");
+ }
+ return stderr;
+ },
+ };
+ }
+
+ kill(signo = "SIGTERM") {
+ if (this.#rid === null) {
+ throw new TypeError("Child process has already terminated.");
+ }
+ ops.op_kill(this.#pid, signo, "Deno.Child.kill()");
+ }
+
+ ref() {
+ this.#unrefed = false;
+ core.refOp(this.#waitPromiseId);
+ if (this.#stdout) readableStreamForRidUnrefableRef(this.#stdout);
+ if (this.#stderr) readableStreamForRidUnrefableRef(this.#stderr);
+ }
+
+ unref() {
+ this.#unrefed = true;
+ core.unrefOp(this.#waitPromiseId);
+ if (this.#stdout) readableStreamForRidUnrefableUnref(this.#stdout);
+ if (this.#stderr) readableStreamForRidUnrefableUnref(this.#stderr);
+ }
+}
+
+function spawn(command, options) {
+ if (options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
+ );
+ }
+ return spawnChildInner(
+ ops.op_spawn_child,
+ command,
+ "Deno.Command().output()",
+ 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.Command().spawn()' 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;
+ },
+ };
+}
+
+class Command {
+ #command;
+ #options;
+
+ constructor(command, options) {
+ this.#command = command;
+ this.#options = options;
+ }
+
+ output() {
+ if (this.#options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
+ );
+ }
+ return spawn(this.#command, this.#options);
+ }
+
+ outputSync() {
+ if (this.#options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
+ );
+ }
+ return spawnSync(this.#command, this.#options);
+ }
+
+ spawn() {
+ const options = {
+ ...(this.#options ?? {}),
+ stdout: this.#options?.stdout ?? "inherit",
+ stderr: this.#options?.stderr ?? "inherit",
+ stdin: this.#options?.stdin ?? "inherit",
+ };
+ return spawnChild(this.#command, options);
+ }
+}
+
+export { ChildProcess, Command, kill, Process, run };
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js
deleted file mode 100644
index 173f596cc..000000000
--- a/runtime/js/40_spawn.js
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-const core = globalThis.Deno.core;
-const ops = core.ops;
-const primordials = globalThis.__bootstrap.primordials;
-import { pathFromURL } from "internal:runtime/06_util.js";
-import { add, remove } from "internal:deno_web/03_abort_signal.js";
-const {
- ArrayPrototypeMap,
- ObjectEntries,
- ObjectPrototypeIsPrototypeOf,
- String,
- TypeError,
- PromisePrototypeThen,
- SafePromiseAll,
- SymbolFor,
- Symbol,
-} = primordials;
-import {
- readableStreamCollectIntoUint8Array,
- readableStreamForRidUnrefable,
- readableStreamForRidUnrefableRef,
- readableStreamForRidUnrefableUnref,
- ReadableStreamPrototype,
- writableStreamForRid,
-} from "internal:deno_web/06_streams.js";
-
-const illegalConstructorKey = Symbol("illegalConstructorKey");
-
-const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
-
-function spawnChildInner(opFn, command, apiName, {
- args = [],
- cwd = undefined,
- clearEnv = false,
- env = {},
- uid = undefined,
- gid = undefined,
- stdin = "null",
- stdout = "piped",
- stderr = "piped",
- signal = undefined,
- windowsRawArguments = false,
-} = {}) {
- const child = opFn({
- cmd: pathFromURL(command),
- args: ArrayPrototypeMap(args, String),
- cwd: pathFromURL(cwd),
- clearEnv,
- env: ObjectEntries(env),
- uid,
- gid,
- stdin,
- stdout,
- stderr,
- windowsRawArguments,
- }, apiName);
- return new ChildProcess(illegalConstructorKey, {
- ...child,
- signal,
- });
-}
-
-function spawnChild(command, options = {}) {
- return spawnChildInner(
- ops.op_spawn_child,
- command,
- "Deno.Command().spawn()",
- options,
- );
-}
-
-function collectOutput(readableStream) {
- if (
- !(ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, readableStream))
- ) {
- return null;
- }
-
- return readableStreamCollectIntoUint8Array(readableStream);
-}
-
-class ChildProcess {
- #rid;
- #waitPromiseId;
- #unrefed = false;
-
- #pid;
- get pid() {
- return this.#pid;
- }
-
- #stdin = null;
- get stdin() {
- if (this.#stdin == null) {
- throw new TypeError("stdin is not piped");
- }
- return this.#stdin;
- }
-
- #stdoutRid;
- #stdout = null;
- get stdout() {
- if (this.#stdout == null) {
- throw new TypeError("stdout is not piped");
- }
- return this.#stdout;
- }
-
- #stderrRid;
- #stderr = null;
- get stderr() {
- if (this.#stderr == null) {
- throw new TypeError("stderr is not piped");
- }
- return this.#stderr;
- }
-
- constructor(key = null, {
- signal,
- rid,
- pid,
- stdinRid,
- stdoutRid,
- stderrRid,
- } = null) {
- if (key !== illegalConstructorKey) {
- throw new TypeError("Illegal constructor.");
- }
-
- this.#rid = rid;
- this.#pid = pid;
-
- if (stdinRid !== null) {
- this.#stdin = writableStreamForRid(stdinRid);
- }
-
- if (stdoutRid !== null) {
- this.#stdoutRid = stdoutRid;
- this.#stdout = readableStreamForRidUnrefable(stdoutRid);
- }
-
- if (stderrRid !== null) {
- this.#stderrRid = stderrRid;
- this.#stderr = readableStreamForRidUnrefable(stderrRid);
- }
-
- const onAbort = () => this.kill("SIGTERM");
- signal?.[add](onAbort);
-
- const waitPromise = core.opAsync("op_spawn_wait", this.#rid);
- this.#waitPromiseId = waitPromise[promiseIdSymbol];
- this.#status = PromisePrototypeThen(waitPromise, (res) => {
- this.#rid = null;
- signal?.[remove](onAbort);
- return res;
- });
- }
-
- #status;
- get status() {
- return this.#status;
- }
-
- async output() {
- if (this.#stdout?.locked) {
- throw new TypeError(
- "Can't collect output because stdout is locked",
- );
- }
- if (this.#stderr?.locked) {
- throw new TypeError(
- "Can't collect output because stderr is locked",
- );
- }
-
- const { 0: status, 1: stdout, 2: stderr } = await SafePromiseAll([
- this.#status,
- collectOutput(this.#stdout),
- collectOutput(this.#stderr),
- ]);
-
- return {
- success: status.success,
- code: status.code,
- signal: status.signal,
- get stdout() {
- if (stdout == null) {
- throw new TypeError("stdout is not piped");
- }
- return stdout;
- },
- get stderr() {
- if (stderr == null) {
- throw new TypeError("stderr is not piped");
- }
- return stderr;
- },
- };
- }
-
- kill(signo = "SIGTERM") {
- if (this.#rid === null) {
- throw new TypeError("Child process has already terminated.");
- }
- ops.op_kill(this.#pid, signo, "Deno.Child.kill()");
- }
-
- ref() {
- this.#unrefed = false;
- core.refOp(this.#waitPromiseId);
- if (this.#stdout) readableStreamForRidUnrefableRef(this.#stdout);
- if (this.#stderr) readableStreamForRidUnrefableRef(this.#stderr);
- }
-
- unref() {
- this.#unrefed = true;
- core.unrefOp(this.#waitPromiseId);
- if (this.#stdout) readableStreamForRidUnrefableUnref(this.#stdout);
- if (this.#stderr) readableStreamForRidUnrefableUnref(this.#stderr);
- }
-}
-
-function spawn(command, options) {
- if (options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
- );
- }
- return spawnChildInner(
- ops.op_spawn_child,
- command,
- "Deno.Command().output()",
- 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.Command().spawn()' 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;
- },
- };
-}
-
-class Command {
- #command;
- #options;
-
- constructor(command, options) {
- this.#command = command;
- this.#options = options;
- }
-
- output() {
- if (this.#options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
- );
- }
- return spawn(this.#command, this.#options);
- }
-
- outputSync() {
- if (this.#options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
- );
- }
- return spawnSync(this.#command, this.#options);
- }
-
- spawn() {
- const options = {
- ...(this.#options ?? {}),
- stdout: this.#options?.stdout ?? "inherit",
- stderr: this.#options?.stderr ?? "inherit",
- stdin: this.#options?.stdin ?? "inherit",
- };
- return spawnChild(this.#command, options);
- }
-}
-
-export { ChildProcess, Command };
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 39a1def90..f83695952 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -22,7 +22,6 @@ import * as fsEvents from "internal:runtime/40_fs_events.js";
import * as process from "internal:runtime/40_process.js";
import * as signals from "internal:runtime/40_signals.js";
import * as tty from "internal:runtime/40_tty.js";
-import * as spawn from "internal:runtime/40_spawn.js";
// TODO(bartlomieju): this is funky we have two `http` imports
import * as httpRuntime from "internal:runtime/40_http.js";
@@ -148,9 +147,9 @@ const denoNs = {
consoleSize: tty.consoleSize,
gid: os.gid,
uid: os.uid,
- Command: spawn.Command,
+ Command: process.Command,
// TODO(bartlomieju): why is this exported?
- ChildProcess: spawn.ChildProcess,
+ ChildProcess: process.ChildProcess,
};
const denoNsUnstable = {