summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2022-11-10 22:03:28 +0100
committerGitHub <noreply@github.com>2022-11-10 22:03:28 +0100
commit8d5c0112fbbed188f97218ace2357feba3a8746f (patch)
treecf08da990c8bf90da116e8db3ac6332cd37578a1
parent53e974b276b095faf52918c4c6e988e9d2788cef (diff)
feat: don't require --unstable flag for npm programs (#16520)
This PR adds copies of several unstable APIs that are available in "Deno[Deno.internal].nodeUnstable" namespace. These copies do not perform unstable check (ie. don't require "--unstable" flag to be present). Otherwise they work exactly the same, including permission checks. These APIs are not meant to be used by users directly and can change at any time. Copies of following APIs are available in that namespace: - Deno.spawnChild - Deno.spawn - Deno.spawnSync - Deno.serve - Deno.upgradeHttpRaw - Deno.listenDatagram
-rw-r--r--cli/ops/mod.rs6
-rw-r--r--cli/proc_state.rs1
-rw-r--r--cli/tests/integration/npm_tests.rs11
-rw-r--r--ext/flash/01_http.js418
-rw-r--r--ext/flash/lib.rs28
-rw-r--r--ext/net/01_net.js50
-rw-r--r--ext/net/ops.rs33
-rw-r--r--ext/net/ops_unix.rs27
-rw-r--r--runtime/js/40_spawn.js126
-rw-r--r--runtime/js/90_deno_ns.js1
-rw-r--r--runtime/js/99_main.js70
-rw-r--r--runtime/ops/spawn.rs120
m---------test_util/std0
13 files changed, 569 insertions, 322 deletions
diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs
index df3331353..c02b951b2 100644
--- a/cli/ops/mod.rs
+++ b/cli/ops/mod.rs
@@ -1,7 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::proc_state::ProcState;
-use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::Extension;
@@ -27,10 +26,5 @@ fn init_proc_state(ps: ProcState) -> Extension {
#[op]
fn op_npm_process_state(state: &mut OpState) -> Result<String, AnyError> {
let proc_state = state.borrow_mut::<ProcState>();
- if !proc_state.options.unstable() {
- bail!(
- "Unstable use of npm process state. The --unstable flag must be provided."
- )
- }
Ok(proc_state.npm_resolver.get_npm_process_state())
}
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 117451c56..29f4ce3bd 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -274,6 +274,7 @@ impl ProcState {
/// module before attempting to `load()` it from a `JsRuntime`. It will
/// populate `self.graph_data` in memory with the necessary source code, write
/// emits where necessary or report any module graph / type checking errors.
+ #[allow(clippy::too_many_arguments)]
pub async fn prepare_module_load(
&self,
roots: Vec<ModuleSpecifier>,
diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs
index 1eb476287..ced9ad8a3 100644
--- a/cli/tests/integration/npm_tests.rs
+++ b/cli/tests/integration/npm_tests.rs
@@ -103,7 +103,7 @@ itest!(dual_cjs_esm {
});
itest!(child_process_fork_test {
- args: "run --unstable -A --quiet npm/child_process_fork_test/main.ts",
+ args: "run -A --quiet npm/child_process_fork_test/main.ts",
output: "npm/child_process_fork_test/main.out",
envs: env_vars(),
http_server: true,
@@ -621,7 +621,7 @@ itest!(node_modules_dir_with_deps {
});
itest!(node_modules_dir_yargs {
- args: "run --allow-read --allow-env --unstable --node-modules-dir $TESTDATA/npm/cjs_yargs/main.js",
+ args: "run --allow-read --allow-env --node-modules-dir $TESTDATA/npm/cjs_yargs/main.js",
output: "npm/cjs_yargs/main.out",
envs: env_vars(),
http_server: true,
@@ -1050,7 +1050,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("-A")
.arg("main.ts")
.envs(env_vars())
@@ -1082,7 +1081,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("-A")
.arg("main.ts")
.envs(env_vars())
@@ -1097,7 +1095,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("--reload")
.arg("-A")
.arg("main.ts")
@@ -1114,7 +1111,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("--node-modules-dir")
.arg("-A")
.arg("main.ts")
@@ -1139,7 +1135,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("--node-modules-dir")
.arg("-A")
.arg("main.ts")
@@ -1156,7 +1151,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("--node-modules-dir")
.arg("--reload")
.arg("-A")
@@ -1174,7 +1168,6 @@ fn peer_deps_with_copied_folders_and_lockfile() {
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(temp_dir.path())
.arg("run")
- .arg("--unstable")
.arg("--node-modules-dir")
.arg("--no-lock")
.arg("--reload")
diff --git a/ext/flash/01_http.js b/ext/flash/01_http.js
index 4435860ff..7a6b9bc47 100644
--- a/ext/flash/01_http.js
+++ b/ext/flash/01_http.js
@@ -422,239 +422,241 @@
})();
}
- async function serve(arg1, arg2) {
- let options = undefined;
- let handler = undefined;
- if (arg1 instanceof Function) {
- handler = arg1;
- options = arg2;
- } else if (arg2 instanceof Function) {
- handler = arg2;
- options = arg1;
- } else {
- options = arg1;
- }
- if (handler === undefined) {
+ function createServe(opFn) {
+ return async function serve(arg1, arg2) {
+ let options = undefined;
+ let handler = undefined;
+ if (arg1 instanceof Function) {
+ handler = arg1;
+ options = arg2;
+ } else if (arg2 instanceof Function) {
+ handler = arg2;
+ options = arg1;
+ } else {
+ options = arg1;
+ }
+ if (handler === undefined) {
+ if (options === undefined) {
+ throw new TypeError(
+ "No handler was provided, so an options bag is mandatory.",
+ );
+ }
+ handler = options.handler;
+ }
+ if (!(handler instanceof Function)) {
+ throw new TypeError("A handler function must be provided.");
+ }
if (options === undefined) {
- throw new TypeError(
- "No handler was provided, so an options bag is mandatory.",
- );
+ options = {};
}
- handler = options.handler;
- }
- if (!(handler instanceof Function)) {
- throw new TypeError("A handler function must be provided.");
- }
- if (options === undefined) {
- options = {};
- }
- const signal = options.signal;
+ const signal = options.signal;
- const onError = options.onError ?? function (error) {
- console.error(error);
- return new Response("Internal Server Error", { status: 500 });
- };
-
- const onListen = options.onListen ?? function ({ port }) {
- console.log(
- `Listening on http://${
- hostnameForDisplay(listenOpts.hostname)
- }:${port}/`,
- );
- };
+ const onError = options.onError ?? function (error) {
+ console.error(error);
+ return new Response("Internal Server Error", { status: 500 });
+ };
- const listenOpts = {
- hostname: options.hostname ?? "127.0.0.1",
- port: options.port ?? 9000,
- reuseport: options.reusePort ?? false,
- };
- if (options.cert || options.key) {
- if (!options.cert || !options.key) {
- throw new TypeError(
- "Both cert and key must be provided to enable HTTPS.",
+ const onListen = options.onListen ?? function ({ port }) {
+ console.log(
+ `Listening on http://${
+ hostnameForDisplay(listenOpts.hostname)
+ }:${port}/`,
);
+ };
+
+ const listenOpts = {
+ hostname: options.hostname ?? "127.0.0.1",
+ port: options.port ?? 9000,
+ reuseport: options.reusePort ?? false,
+ };
+ if (options.cert || options.key) {
+ if (!options.cert || !options.key) {
+ throw new TypeError(
+ "Both cert and key must be provided to enable HTTPS.",
+ );
+ }
+ listenOpts.cert = options.cert;
+ listenOpts.key = options.key;
}
- listenOpts.cert = options.cert;
- listenOpts.key = options.key;
- }
- const serverId = core.ops.op_flash_serve(listenOpts);
- const serverPromise = core.opAsync("op_flash_drive_server", serverId);
-
- PromisePrototypeCatch(
- PromisePrototypeThen(
- core.opAsync("op_flash_wait_for_listening", serverId),
- (port) => {
- onListen({ hostname: listenOpts.hostname, port });
- },
- ),
- () => {},
- );
- const finishedPromise = PromisePrototypeCatch(serverPromise, () => {});
-
- const server = {
- id: serverId,
- transport: listenOpts.cert && listenOpts.key ? "https" : "http",
- hostname: listenOpts.hostname,
- port: listenOpts.port,
- closed: false,
- finished: finishedPromise,
- async close() {
- if (server.closed) {
- return;
- }
- server.closed = true;
- await core.opAsync("op_flash_close_server", serverId);
- await server.finished;
- },
- async serve() {
- let offset = 0;
- while (true) {
+ const serverId = opFn(listenOpts);
+ const serverPromise = core.opAsync("op_flash_drive_server", serverId);
+
+ PromisePrototypeCatch(
+ PromisePrototypeThen(
+ core.opAsync("op_flash_wait_for_listening", serverId),
+ (port) => {
+ onListen({ hostname: listenOpts.hostname, port });
+ },
+ ),
+ () => {},
+ );
+ const finishedPromise = PromisePrototypeCatch(serverPromise, () => {});
+
+ const server = {
+ id: serverId,
+ transport: listenOpts.cert && listenOpts.key ? "https" : "http",
+ hostname: listenOpts.hostname,
+ port: listenOpts.port,
+ closed: false,
+ finished: finishedPromise,
+ async close() {
if (server.closed) {
- break;
+ return;
}
-
- let tokens = nextRequestSync();
- if (tokens === 0) {
- tokens = await core.opAsync("op_flash_next_async", serverId);
+ server.closed = true;
+ await core.opAsync("op_flash_close_server", serverId);
+ await server.finished;
+ },
+ async serve() {
+ let offset = 0;
+ while (true) {
if (server.closed) {
break;
}
- }
- for (let i = offset; i < offset + tokens; i++) {
- let body = null;
- // There might be a body, but we don't expose it for GET/HEAD requests.
- // It will be closed automatically once the request has been handled and
- // the response has been sent.
- const method = getMethodSync(i);
- let hasBody = method > 2; // Not GET/HEAD/CONNECT
- if (hasBody) {
- body = createRequestBodyStream(serverId, i);
- if (body === null) {
- hasBody = false;
+ let tokens = nextRequestSync();
+ if (tokens === 0) {
+ tokens = await core.opAsync("op_flash_next_async", serverId);
+ if (server.closed) {
+ break;
}
}
- const req = fromFlashRequest(
- serverId,
- /* streamRid */
- i,
- body,
- /* methodCb */
- () => methods[method],
- /* urlCb */
- () => {
- const path = core.ops.op_flash_path(serverId, i);
- return `${server.transport}://${server.hostname}:${server.port}${path}`;
- },
- /* headersCb */
- () => core.ops.op_flash_headers(serverId, i),
- );
+ for (let i = offset; i < offset + tokens; i++) {
+ let body = null;
+ // There might be a body, but we don't expose it for GET/HEAD requests.
+ // It will be closed automatically once the request has been handled and
+ // the response has been sent.
+ const method = getMethodSync(i);
+ let hasBody = method > 2; // Not GET/HEAD/CONNECT
+ if (hasBody) {
+ body = createRequestBodyStream(serverId, i);
+ if (body === null) {
+ hasBody = false;
+ }
+ }
- let resp;
- try {
- resp = handler(req);
- if (resp instanceof Promise) {
- PromisePrototypeCatch(
- PromisePrototypeThen(
- resp,
- (resp) =>
- handleResponse(
- req,
- resp,
- body,
- hasBody,
- method,
- serverId,
- i,
- respondFast,
- respondChunked,
- ),
- ),
- onError,
- );
- continue;
- } else if (typeof resp?.then === "function") {
- resp.then((resp) =>
- handleResponse(
- req,
- resp,
- body,
- hasBody,
- method,
- serverId,
- i,
- respondFast,
- respondChunked,
- )
- ).catch(onError);
- continue;
+ const req = fromFlashRequest(
+ serverId,
+ /* streamRid */
+ i,
+ body,
+ /* methodCb */
+ () => methods[method],
+ /* urlCb */
+ () => {
+ const path = core.ops.op_flash_path(serverId, i);
+ return `${server.transport}://${server.hostname}:${server.port}${path}`;
+ },
+ /* headersCb */
+ () => core.ops.op_flash_headers(serverId, i),
+ );
+
+ let resp;
+ try {
+ resp = handler(req);
+ if (resp instanceof Promise) {
+ PromisePrototypeCatch(
+ PromisePrototypeThen(
+ resp,
+ (resp) =>
+ handleResponse(
+ req,
+ resp,
+ body,
+ hasBody,
+ method,
+ serverId,
+ i,
+ respondFast,
+ respondChunked,
+ ),
+ ),
+ onError,
+ );
+ continue;
+ } else if (typeof resp?.then === "function") {
+ resp.then((resp) =>
+ handleResponse(
+ req,
+ resp,
+ body,
+ hasBody,
+ method,
+ serverId,
+ i,
+ respondFast,
+ respondChunked,
+ )
+ ).catch(onError);
+ continue;
+ }
+ } catch (e) {
+ resp = await onError(e);
}
- } catch (e) {
- resp = await onError(e);
+
+ handleResponse(
+ req,
+ resp,
+ body,
+ hasBody,
+ method,
+ serverId,
+ i,
+ respondFast,
+ respondChunked,
+ );
}
- handleResponse(
- req,
- resp,
- body,
- hasBody,
- method,
- serverId,
- i,
- respondFast,
- respondChunked,
- );
+ offset += tokens;
}
+ await server.finished;
+ },
+ };
+
+ signal?.addEventListener("abort", () => {
+ clearInterval(dateInterval);
+ PromisePrototypeThen(server.close(), () => {}, () => {});
+ }, {
+ once: true,
+ });
+
+ function respondChunked(token, chunk, shutdown) {
+ return core.opAsync(
+ "op_flash_respond_chuncked",
+ serverId,
+ token,
+ chunk,
+ shutdown,
+ );
+ }
- offset += tokens;
- }
- await server.finished;
- },
- };
-
- signal?.addEventListener("abort", () => {
- clearInterval(dateInterval);
- PromisePrototypeThen(server.close(), () => {}, () => {});
- }, {
- once: true,
- });
-
- function respondChunked(token, chunk, shutdown) {
- return core.opAsync(
- "op_flash_respond_chuncked",
- serverId,
- token,
- chunk,
- shutdown,
- );
- }
-
- const fastOp = prepareFastCalls();
- let nextRequestSync = () => fastOp.nextRequest();
- let getMethodSync = (token) => fastOp.getMethod(token);
- let respondFast = (token, response, shutdown) =>
- fastOp.respond(token, response, shutdown);
- if (serverId > 0) {
- nextRequestSync = () => core.ops.op_flash_next_server(serverId);
- getMethodSync = (token) => core.ops.op_flash_method(serverId, token);
- respondFast = (token, response, shutdown) =>
- core.ops.op_flash_respond(serverId, token, response, null, shutdown);
- }
+ const fastOp = prepareFastCalls();
+ let nextRequestSync = () => fastOp.nextRequest();
+ let getMethodSync = (token) => fastOp.getMethod(token);
+ let respondFast = (token, response, shutdown) =>
+ fastOp.respond(token, response, shutdown);
+ if (serverId > 0) {
+ nextRequestSync = () => core.ops.op_flash_next_server(serverId);
+ getMethodSync = (token) => core.ops.op_flash_method(serverId, token);
+ respondFast = (token, response, shutdown) =>
+ core.ops.op_flash_respond(serverId, token, response, null, shutdown);
+ }
- if (!dateInterval) {
- date = new Date().toUTCString();
- dateInterval = setInterval(() => {
+ if (!dateInterval) {
date = new Date().toUTCString();
- }, 1000);
- }
+ dateInterval = setInterval(() => {
+ date = new Date().toUTCString();
+ }, 1000);
+ }
- await SafePromiseAll([
- PromisePrototypeCatch(server.serve(), console.error),
- serverPromise,
- ]);
+ await SafePromiseAll([
+ PromisePrototypeCatch(server.serve(), console.error),
+ serverPromise,
+ ]);
+ };
}
function createRequestBodyStream(serverId, token) {
@@ -722,7 +724,7 @@
}
window.__bootstrap.flash = {
- serve,
+ createServe,
upgradeHttpRaw,
};
})(this);
diff --git a/ext/flash/lib.rs b/ext/flash/lib.rs
index 1f3686760..b077b8d21 100644
--- a/ext/flash/lib.rs
+++ b/ext/flash/lib.rs
@@ -1171,15 +1171,13 @@ pub fn resolve_addr_sync(
Ok(result)
}
-#[op]
-fn op_flash_serve<P>(
+fn flash_serve<P>(
state: &mut OpState,
opts: ListenOpts,
) -> Result<u32, AnyError>
where
P: FlashPermissions + 'static,
{
- check_unstable(state, "Deno.serve");
state
.borrow_mut::<P>()
.check_net(&(&opts.hostname, Some(opts.port)), "Deno.serve()")?;
@@ -1224,6 +1222,29 @@ where
}
#[op]
+fn op_flash_serve<P>(
+ state: &mut OpState,
+ opts: ListenOpts,
+) -> Result<u32, AnyError>
+where
+ P: FlashPermissions + 'static,
+{
+ check_unstable(state, "Deno.serve");
+ flash_serve::<P>(state, opts)
+}
+
+#[op]
+fn op_node_unstable_flash_serve<P>(
+ state: &mut OpState,
+ opts: ListenOpts,
+) -> Result<u32, AnyError>
+where
+ P: FlashPermissions + 'static,
+{
+ flash_serve::<P>(state, opts)
+}
+
+#[op]
fn op_flash_wait_for_listening(
state: &mut OpState,
server_id: u32,
@@ -1445,6 +1466,7 @@ pub fn init<P: FlashPermissions + 'static>(unstable: bool) -> Extension {
))
.ops(vec![
op_flash_serve::decl::<P>(),
+ op_node_unstable_flash_serve::decl::<P>(),
op_flash_respond::decl(),
op_flash_respond_async::decl(),
op_flash_respond_chuncked::decl(),
diff --git a/ext/net/01_net.js b/ext/net/01_net.js
index 765b94035..971ec2e8b 100644
--- a/ext/net/01_net.js
+++ b/ext/net/01_net.js
@@ -319,30 +319,32 @@
}
}
- function listenDatagram(args) {
- switch (args.transport) {
- case "udp": {
- const [rid, addr] = ops.op_net_listen_udp(
- {
- hostname: args.hostname ?? "127.0.0.1",
- port: args.port,
- },
- args.reuseAddress ?? false,
- );
- addr.transport = "udp";
- return new Datagram(rid, addr);
- }
- case "unixpacket": {
- const [rid, path] = ops.op_net_listen_unixpacket(args.path);
- const addr = {
- transport: "unixpacket",
- path,
- };
- return new Datagram(rid, addr);
+ function createListenDatagram(udpOpFn, unixOpFn) {
+ return function listenDatagram(args) {
+ switch (args.transport) {
+ case "udp": {
+ const [rid, addr] = udpOpFn(
+ {
+ hostname: args.hostname ?? "127.0.0.1",
+ port: args.port,
+ },
+ args.reuseAddress ?? false,
+ );
+ addr.transport = "udp";
+ return new Datagram(rid, addr);
+ }
+ case "unixpacket": {
+ const [rid, path] = unixOpFn(args.path);
+ const addr = {
+ transport: "unixpacket",
+ path,
+ };
+ return new Datagram(rid, addr);
+ }
+ default:
+ throw new TypeError(`Unsupported transport: '${transport}'`);
}
- default:
- throw new TypeError(`Unsupported transport: '${transport}'`);
- }
+ };
}
async function connect(args) {
@@ -389,7 +391,7 @@
TcpConn,
UnixConn,
listen,
- listenDatagram,
+ createListenDatagram,
Listener,
shutdown,
Datagram,
diff --git a/ext/net/ops.rs b/ext/net/ops.rs
index e6420bf9e..96de8cff1 100644
--- a/ext/net/ops.rs
+++ b/ext/net/ops.rs
@@ -53,10 +53,13 @@ pub fn init<P: NetPermissions + 'static>() -> Vec<OpDecl> {
crate::ops_unix::op_net_connect_unix::decl::<P>(),
op_net_listen_tcp::decl::<P>(),
op_net_listen_udp::decl::<P>(),
+ op_node_unstable_net_listen_udp::decl::<P>(),
#[cfg(unix)]
crate::ops_unix::op_net_listen_unix::decl::<P>(),
#[cfg(unix)]
crate::ops_unix::op_net_listen_unixpacket::decl::<P>(),
+ #[cfg(unix)]
+ crate::ops_unix::op_node_unstable_net_listen_unixpacket::decl::<P>(),
op_net_recv_udp::decl(),
#[cfg(unix)]
crate::ops_unix::op_net_recv_unixpacket::decl(),
@@ -288,8 +291,7 @@ where
Ok((rid, IpAddr::from(local_addr)))
}
-#[op]
-fn op_net_listen_udp<NP>(
+fn net_listen_udp<NP>(
state: &mut OpState,
addr: IpAddr,
reuse_address: bool,
@@ -297,7 +299,6 @@ fn op_net_listen_udp<NP>(
where
NP: NetPermissions + 'static,
{
- super::check_unstable(state, "Deno.listenDatagram");
state
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()")?;
@@ -343,6 +344,32 @@ where
Ok((rid, IpAddr::from(local_addr)))
}
+#[op]
+fn op_net_listen_udp<NP>(
+ state: &mut OpState,
+ addr: IpAddr,
+ reuse_address: bool,
+) -> Result<(ResourceId, IpAddr), AnyError>
+where
+ NP: NetPermissions + 'static,
+{
+ super::check_unstable(state, "Deno.listenDatagram");
+ net_listen_udp::<NP>(state, addr, reuse_address)
+}
+
+#[op]
+fn op_node_unstable_net_listen_udp<NP>(
+ state: &mut OpState,
+ addr: IpAddr,
+ reuse_address: bool,
+) -> Result<(ResourceId, IpAddr), AnyError>
+where
+ NP: NetPermissions + 'static,
+{
+ super::check_unstable(state, "Deno.listenDatagram");
+ net_listen_udp::<NP>(state, addr, reuse_address)
+}
+
#[derive(Serialize, Eq, PartialEq, Debug)]
#[serde(untagged)]
pub enum DnsReturnRecord {
diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs
index b45b02343..bf03f4015 100644
--- a/ext/net/ops_unix.rs
+++ b/ext/net/ops_unix.rs
@@ -209,8 +209,7 @@ where
Ok((rid, pathname))
}
-#[op]
-pub fn op_net_listen_unixpacket<NP>(
+pub fn net_listen_unixpacket<NP>(
state: &mut OpState,
path: String,
) -> Result<(ResourceId, Option<String>), AnyError>
@@ -218,7 +217,6 @@ where
NP: NetPermissions + 'static,
{
let address_path = Path::new(&path);
- super::check_unstable(state, "Deno.listenDatagram");
let permissions = state.borrow_mut::<NP>();
permissions.check_read(address_path, "Deno.listenDatagram()")?;
permissions.check_write(address_path, "Deno.listenDatagram()")?;
@@ -233,6 +231,29 @@ where
Ok((rid, pathname))
}
+#[op]
+pub fn op_net_listen_unixpacket<NP>(
+ state: &mut OpState,
+ path: String,
+) -> Result<(ResourceId, Option<String>), AnyError>
+where
+ NP: NetPermissions + 'static,
+{
+ super::check_unstable(state, "Deno.listenDatagram");
+ net_listen_unixpacket::<NP>(state, path)
+}
+
+#[op]
+pub fn op_node_unstable_net_listen_unixpacket<NP>(
+ state: &mut OpState,
+ path: String,
+) -> Result<(ResourceId, Option<String>), AnyError>
+where
+ NP: NetPermissions + 'static,
+{
+ net_listen_unixpacket::<NP>(state, path)
+}
+
pub fn pathstring(pathname: &Path) -> Result<String, AnyError> {
into_string(pathname.into())
}
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
+ },
+ })
+}
diff --git a/test_util/std b/test_util/std
-Subproject 241e7eb8f91a06e75d8e7108b1b35ca202adf92
+Subproject c56a8c4e3245dd9ea1a892d2f2150ddba57f29c