summaryrefslogtreecommitdiff
path: root/runtime/js
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/js')
-rw-r--r--runtime/js/40_spawn.js206
-rw-r--r--runtime/js/90_deno_ns.js4
2 files changed, 210 insertions, 0 deletions
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js
new file mode 100644
index 000000000..c55ce657d
--- /dev/null
+++ b/runtime/js/40_spawn.js
@@ -0,0 +1,206 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+"use strict";
+
+((window) => {
+ const core = window.Deno.core;
+ const { pathFromURL } = window.__bootstrap.util;
+ const { illegalConstructorKey } = window.__bootstrap.webUtil;
+ const {
+ ArrayPrototypeMap,
+ ObjectEntries,
+ String,
+ TypeError,
+ Uint8Array,
+ PromiseAll,
+ } = window.__bootstrap.primordials;
+ const { readableStreamForRid, writableStreamForRid } =
+ window.__bootstrap.streamUtils;
+
+ function spawnChild(command, {
+ args = [],
+ cwd = undefined,
+ clearEnv = false,
+ env = {},
+ uid = undefined,
+ gid = undefined,
+ stdin = "null",
+ stdout = "piped",
+ stderr = "piped",
+ } = {}) {
+ const child = core.opSync("op_spawn_child", {
+ cmd: pathFromURL(command),
+ args: ArrayPrototypeMap(args, String),
+ cwd: pathFromURL(cwd),
+ clearEnv,
+ env: ObjectEntries(env),
+ uid,
+ gid,
+ stdin,
+ stdout,
+ stderr,
+ });
+ return new Child(illegalConstructorKey, child);
+ }
+
+ async function collectOutput(readableStream) {
+ if (!(readableStream instanceof ReadableStream)) {
+ return null;
+ }
+
+ const bufs = [];
+ let size = 0;
+ for await (const chunk of readableStream) {
+ bufs.push(chunk);
+ size += chunk.byteLength;
+ }
+
+ const buffer = new Uint8Array(size);
+ let offset = 0;
+ for (const chunk of bufs) {
+ buffer.set(chunk, offset);
+ offset += chunk.byteLength;
+ }
+
+ return buffer;
+ }
+
+ class Child {
+ #rid;
+
+ #pid;
+ get pid() {
+ return this.#pid;
+ }
+
+ #stdinRid;
+ #stdin = null;
+ get stdin() {
+ return this.#stdin;
+ }
+
+ #stdoutRid;
+ #stdout = null;
+ get stdout() {
+ return this.#stdout;
+ }
+
+ #stderrRid;
+ #stderr = null;
+ get stderr() {
+ return this.#stderr;
+ }
+
+ constructor(key = null, {
+ rid,
+ pid,
+ stdinRid,
+ stdoutRid,
+ stderrRid,
+ } = null) {
+ if (key !== illegalConstructorKey) {
+ throw new TypeError("Illegal constructor.");
+ }
+
+ this.#rid = rid;
+ this.#pid = pid;
+
+ if (stdinRid !== null) {
+ this.#stdinRid = stdinRid;
+ this.#stdin = writableStreamForRid(stdinRid);
+ }
+
+ if (stdoutRid !== null) {
+ this.#stdoutRid = stdoutRid;
+ this.#stdout = readableStreamForRid(stdoutRid);
+ }
+
+ if (stderrRid !== null) {
+ this.#stderrRid = stderrRid;
+ this.#stderr = readableStreamForRid(stderrRid);
+ }
+
+ this.#status = core.opAsync("op_spawn_wait", this.#rid).then((res) => {
+ this.#rid = null;
+ return res;
+ });
+ }
+
+ #status;
+ get status() {
+ return this.#status;
+ }
+
+ async output() {
+ if (this.#rid === null) {
+ throw new TypeError("Child process has already terminated.");
+ }
+ 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 [status, stdout, stderr] = await PromiseAll([
+ this.#status,
+ collectOutput(this.#stdout),
+ collectOutput(this.#stderr),
+ ]);
+
+ return {
+ status,
+ stdout,
+ stderr,
+ };
+ }
+
+ kill(signo) {
+ if (this.#rid === null) {
+ throw new TypeError("Child process has already terminated.");
+ }
+ core.opSync("op_kill", this.#pid, signo);
+ }
+ }
+
+ function spawn(command, options) { // TODO(@crowlKats): more options (like input)?
+ return spawnChild(command, {
+ ...options,
+ stdin: "null",
+ stdout: "piped",
+ stderr: "piped",
+ }).output();
+ }
+
+ function spawnSync(command, {
+ args = [],
+ cwd = undefined,
+ clearEnv = false,
+ env = {},
+ uid = undefined,
+ gid = undefined,
+ } = {}) { // TODO(@crowlKats): more options (like input)?
+ return core.opSync("op_spawn_sync", {
+ cmd: pathFromURL(command),
+ args: ArrayPrototypeMap(args, String),
+ cwd: pathFromURL(cwd),
+ clearEnv,
+ env: ObjectEntries(env),
+ uid,
+ gid,
+ stdin: "null",
+ stdout: "piped",
+ stderr: "piped",
+ });
+ }
+
+ window.__bootstrap.spawn = {
+ Child,
+ spawnChild,
+ spawn,
+ spawnSync,
+ };
+})(this);
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index ddaecd7c9..61e894f8a 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -151,5 +151,9 @@
funlockSync: __bootstrap.fs.funlockSync,
refTimer: __bootstrap.timers.refTimer,
unrefTimer: __bootstrap.timers.unrefTimer,
+ Child: __bootstrap.spawn.Child,
+ spawnChild: __bootstrap.spawn.spawnChild,
+ spawn: __bootstrap.spawn.spawn,
+ spawnSync: __bootstrap.spawn.spawnSync,
};
})(this);