summaryrefslogtreecommitdiff
path: root/ext/node/polyfills
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/polyfills')
-rw-r--r--ext/node/polyfills/02_init.js8
-rw-r--r--ext/node/polyfills/child_process.ts9
-rw-r--r--ext/node/polyfills/internal/child_process.ts95
-rw-r--r--ext/node/polyfills/process.ts1
4 files changed, 107 insertions, 6 deletions
diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js
index e3061c95d..e5a0279a5 100644
--- a/ext/node/polyfills/02_init.js
+++ b/ext/node/polyfills/02_init.js
@@ -7,15 +7,12 @@ const requireImpl = internals.requireImpl;
import { nodeGlobals } from "ext:deno_node/00_globals.js";
import "node:module";
-globalThis.nodeBootstrap = function (usesLocalNodeModulesDir, argv0) {
- initialize(usesLocalNodeModulesDir, argv0);
-};
-
let initialized = false;
function initialize(
usesLocalNodeModulesDir,
argv0,
+ ipcFd,
) {
if (initialized) {
throw Error("Node runtime already initialized");
@@ -41,6 +38,7 @@ function initialize(
// but it's the only way to get `args` and `version` and this point.
internals.__bootstrapNodeProcess(argv0, Deno.args, Deno.version);
internals.__initWorkerThreads();
+ internals.__setupChildProcessIpcChannel(ipcFd);
// `Deno[Deno.internal].requireImpl` will be unreachable after this line.
delete internals.requireImpl;
}
@@ -52,6 +50,8 @@ function loadCjsModule(moduleName, isMain, inspectBrk) {
requireImpl.Module._load(moduleName, null, { main: isMain });
}
+globalThis.nodeBootstrap = initialize;
+
internals.node = {
initialize,
loadCjsModule,
diff --git a/ext/node/polyfills/child_process.ts b/ext/node/polyfills/child_process.ts
index 94a108447..c7d007f46 100644
--- a/ext/node/polyfills/child_process.ts
+++ b/ext/node/polyfills/child_process.ts
@@ -10,6 +10,7 @@ import {
ChildProcess,
ChildProcessOptions,
normalizeSpawnArguments,
+ setupChannel,
type SpawnOptions,
spawnSync as _spawnSync,
type SpawnSyncOptions,
@@ -821,6 +822,14 @@ export function execFileSync(
return ret.stdout as string | Buffer;
}
+function setupChildProcessIpcChannel(fd: number) {
+ if (typeof fd != "number" || fd < 0) return;
+ setupChannel(process, fd);
+}
+
+globalThis.__bootstrap.internals.__setupChildProcessIpcChannel =
+ setupChildProcessIpcChannel;
+
export default {
fork,
spawn,
diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts
index 04773a8b7..b9bf13396 100644
--- a/ext/node/polyfills/internal/child_process.ts
+++ b/ext/node/polyfills/internal/child_process.ts
@@ -44,6 +44,9 @@ import { kEmptyObject } from "ext:deno_node/internal/util.mjs";
import { getValidatedPath } from "ext:deno_node/internal/fs/utils.mjs";
import process from "node:process";
+const core = globalThis.__bootstrap.core;
+const ops = core.ops;
+
export function mapValues<T, O>(
record: Readonly<Record<string, T>>,
transformer: (value: T) => O,
@@ -167,12 +170,13 @@ export class ChildProcess extends EventEmitter {
signal,
windowsVerbatimArguments = false,
} = options || {};
+ const normalizedStdio = normalizeStdioOption(stdio);
const [
stdin = "pipe",
stdout = "pipe",
stderr = "pipe",
_channel, // TODO(kt3k): handle this correctly
- ] = normalizeStdioOption(stdio);
+ ] = normalizedStdio;
const [cmd, cmdArgs] = buildCommand(
command,
args || [],
@@ -181,6 +185,8 @@ export class ChildProcess extends EventEmitter {
this.spawnfile = cmd;
this.spawnargs = [cmd, ...cmdArgs];
+ const ipc = normalizedStdio.indexOf("ipc");
+
const stringEnv = mapValues(env, (value) => value.toString());
try {
this.#process = new Deno.Command(cmd, {
@@ -191,6 +197,7 @@ export class ChildProcess extends EventEmitter {
stdout: toDenoStdio(stdout),
stderr: toDenoStdio(stderr),
windowsRawArguments: windowsVerbatimArguments,
+ ipc, // internal
}).spawn();
this.pid = this.#process.pid;
@@ -249,6 +256,10 @@ export class ChildProcess extends EventEmitter {
}
}
+ if (typeof this.#process._pipeFd == "number") {
+ setupChannel(this, this.#process._pipeFd);
+ }
+
(async () => {
const status = await this.#process.status;
this.exitCode = status.code;
@@ -1058,9 +1069,91 @@ function toDenoArgs(args: string[]): string[] {
return denoArgs;
}
+export function setupChannel(target, channel) {
+ const ipc = ops.op_node_ipc_pipe(channel);
+
+ async function readLoop() {
+ try {
+ while (true) {
+ if (!target.connected || target.killed) {
+ return;
+ }
+ const msg = await core.opAsync("op_node_ipc_read", ipc);
+ if (msg == null) {
+ // Channel closed.
+ target.disconnect();
+ return;
+ }
+
+ process.nextTick(handleMessage, msg);
+ }
+ } catch (err) {
+ if (
+ err instanceof Deno.errors.Interrupted ||
+ err instanceof Deno.errors.BadResource
+ ) {
+ return;
+ }
+ }
+ }
+
+ function handleMessage(msg) {
+ target.emit("message", msg);
+ }
+
+ target.send = function (message, handle, options, callback) {
+ if (typeof handle === "function") {
+ callback = handle;
+ handle = undefined;
+ options = undefined;
+ } else if (typeof options === "function") {
+ callback = options;
+ options = undefined;
+ } else if (options !== undefined) {
+ validateObject(options, "options");
+ }
+
+ options = { swallowErrors: false, ...options };
+
+ if (message === undefined) {
+ throw new TypeError("ERR_MISSING_ARGS", "message");
+ }
+
+ if (handle !== undefined) {
+ notImplemented("ChildProcess.send with handle");
+ }
+
+ core.opAsync("op_node_ipc_write", ipc, message)
+ .then(() => {
+ if (callback) {
+ process.nextTick(callback, null);
+ }
+ });
+ };
+
+ target.connected = true;
+
+ target.disconnect = function () {
+ if (!this.connected) {
+ this.emit("error", new Error("IPC channel is already disconnected"));
+ return;
+ }
+
+ this.connected = false;
+ process.nextTick(() => {
+ core.close(ipc);
+ target.emit("disconnect");
+ });
+ };
+
+ // Start reading messages from the channel.
+ readLoop();
+}
+
export default {
ChildProcess,
normalizeSpawnArguments,
stdioStringToArray,
spawnSync,
+ setupChannel,
};
diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts
index 575d8dfb1..352d46f42 100644
--- a/ext/node/polyfills/process.ts
+++ b/ext/node/polyfills/process.ts
@@ -69,7 +69,6 @@ import { buildAllowedFlags } from "ext:deno_node/internal/process/per_thread.mjs
const notImplementedEvents = [
"disconnect",
- "message",
"multipleResolves",
"rejectionHandled",
"worker",