summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/build.rs96
-rw-r--r--runtime/js.rs40
-rw-r--r--runtime/js/40_testing.js1408
-rw-r--r--runtime/js/90_deno_ns.js2
-rw-r--r--runtime/js/98_global_scope.js352
-rw-r--r--runtime/js/99_main.js343
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/web_worker.rs9
-rw-r--r--runtime/worker.rs198
9 files changed, 415 insertions, 2034 deletions
diff --git a/runtime/build.rs b/runtime/build.rs
index 3feca3e76..bab7745cc 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -9,63 +9,8 @@ use std::path::PathBuf;
mod not_docs {
use super::*;
use deno_cache::SqliteBackedCache;
+ use deno_core::snapshot_util::*;
use deno_core::Extension;
- use deno_core::JsRuntime;
- use deno_core::RuntimeOptions;
-
- // TODO(bartlomieju): this module contains a lot of duplicated
- // logic with `cli/build.rs`, factor out to `deno_core`.
- fn create_snapshot(
- mut js_runtime: JsRuntime,
- snapshot_path: &Path,
- files: Vec<PathBuf>,
- ) {
- // TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the
- // workspace root.
- let display_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
- for file in files {
- println!("cargo:rerun-if-changed={}", file.display());
- let display_path = file.strip_prefix(display_root).unwrap();
- let display_path_str = display_path.display().to_string();
- js_runtime
- .execute_script(
- &("deno:".to_string() + &display_path_str.replace('\\', "/")),
- &std::fs::read_to_string(&file).unwrap(),
- )
- .unwrap();
- }
-
- let snapshot = js_runtime.snapshot();
- let snapshot_slice: &[u8] = &snapshot;
- println!("Snapshot size: {}", snapshot_slice.len());
-
- let compressed_snapshot_with_size = {
- let mut vec = vec![];
-
- vec.extend_from_slice(
- &u32::try_from(snapshot.len())
- .expect("snapshot larger than 4gb")
- .to_le_bytes(),
- );
-
- lzzzz::lz4_hc::compress_to_vec(
- snapshot_slice,
- &mut vec,
- lzzzz::lz4_hc::CLEVEL_MAX,
- )
- .expect("snapshot compression failed");
-
- vec
- };
-
- println!(
- "Snapshot compressed size: {}",
- compressed_snapshot_with_size.len()
- );
-
- std::fs::write(snapshot_path, compressed_snapshot_with_size).unwrap();
- println!("Snapshot written to: {} ", snapshot_path.display());
- }
struct Permissions;
@@ -174,7 +119,7 @@ mod not_docs {
}
}
- fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
+ fn create_runtime_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
let extensions: Vec<Extension> = vec![
deno_webidl::init(),
deno_console::init(),
@@ -205,31 +150,26 @@ mod not_docs {
deno_flash::init::<Permissions>(false), // No --unstable
];
- let js_runtime = JsRuntime::new(RuntimeOptions {
- will_snapshot: true,
+ create_snapshot(CreateSnapshotOptions {
+ cargo_manifest_dir: env!("CARGO_MANIFEST_DIR"),
+ snapshot_path,
+ startup_snapshot: None,
extensions,
- ..Default::default()
+ additional_files: files,
+ compression_cb: Some(Box::new(|vec, snapshot_slice| {
+ lzzzz::lz4_hc::compress_to_vec(
+ snapshot_slice,
+ vec,
+ lzzzz::lz4_hc::CLEVEL_MAX,
+ )
+ .expect("snapshot compression failed");
+ })),
});
- create_snapshot(js_runtime, snapshot_path, files);
- }
-
- fn get_js_files(d: &str) -> Vec<PathBuf> {
- let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
- let mut js_files = std::fs::read_dir(d)
- .unwrap()
- .map(|dir_entry| {
- let file = dir_entry.unwrap();
- manifest_dir.join(file.path())
- })
- .filter(|path| path.extension().unwrap_or_default() == "js")
- .collect::<Vec<PathBuf>>();
- js_files.sort();
- js_files
}
pub fn build_snapshot(runtime_snapshot_path: PathBuf) {
- let js_files = get_js_files("js");
- create_runtime_snapshot(&runtime_snapshot_path, js_files);
+ let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
+ create_runtime_snapshot(runtime_snapshot_path, js_files);
}
}
@@ -242,7 +182,7 @@ fn main() {
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// Main snapshot
- let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin");
+ let runtime_snapshot_path = o.join("RUNTIME_SNAPSHOT.bin");
// If we're building on docs.rs we just create
// and empty snapshot file and return, because `rusty_v8`
diff --git a/runtime/js.rs b/runtime/js.rs
index 54a08d9b4..3ad241670 100644
--- a/runtime/js.rs
+++ b/runtime/js.rs
@@ -2,17 +2,18 @@
use deno_core::Snapshot;
use log::debug;
use once_cell::sync::Lazy;
+use std::path::PathBuf;
-pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
+pub static RUNTIME_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
#[allow(clippy::uninit_vec)]
#[cold]
#[inline(never)]
|| {
- static COMPRESSED_CLI_SNAPSHOT: &[u8] =
- include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin"));
+ static COMPRESSED_RUNTIME_SNAPSHOT: &[u8] =
+ include_bytes!(concat!(env!("OUT_DIR"), "/RUNTIME_SNAPSHOT.bin"));
let size =
- u32::from_le_bytes(COMPRESSED_CLI_SNAPSHOT[0..4].try_into().unwrap())
+ u32::from_le_bytes(COMPRESSED_RUNTIME_SNAPSHOT[0..4].try_into().unwrap())
as usize;
let mut vec = Vec::with_capacity(size);
@@ -22,7 +23,8 @@ pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
vec.set_len(size);
}
- lzzzz::lz4::decompress(&COMPRESSED_CLI_SNAPSHOT[4..], &mut vec).unwrap();
+ lzzzz::lz4::decompress(&COMPRESSED_RUNTIME_SNAPSHOT[4..], &mut vec)
+ .unwrap();
vec.into_boxed_slice()
},
@@ -30,29 +32,11 @@ pub static CLI_SNAPSHOT: Lazy<Box<[u8]>> = Lazy::new(
pub fn deno_isolate_init() -> Snapshot {
debug!("Deno isolate init with snapshots.");
- Snapshot::Static(&CLI_SNAPSHOT)
+ Snapshot::Static(&RUNTIME_SNAPSHOT)
}
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn cli_snapshot() {
- let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
- startup_snapshot: Some(deno_isolate_init()),
- ..Default::default()
- });
- js_runtime
- .execute_script(
- "<anon>",
- r#"
- if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) {
- throw Error("bad");
- }
- console.log("we have console.log!!!");
- "#,
- )
- .unwrap();
- }
+pub fn get_99_main() -> PathBuf {
+ let manifest = env!("CARGO_MANIFEST_DIR");
+ let path = PathBuf::from(manifest);
+ path.join("js").join("99_main.js")
}
diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js
deleted file mode 100644
index e8590faf1..000000000
--- a/runtime/js/40_testing.js
+++ /dev/null
@@ -1,1408 +0,0 @@
-// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
- const { setExitHandler } = window.__bootstrap.os;
- const { Console } = window.__bootstrap.console;
- const { serializePermissions } = window.__bootstrap.permissions;
- const { assert } = window.__bootstrap.infra;
- const {
- ArrayFrom,
- ArrayPrototypeFilter,
- ArrayPrototypeJoin,
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ArrayPrototypeShift,
- ArrayPrototypeSort,
- BigInt,
- DateNow,
- Error,
- FunctionPrototype,
- Map,
- MapPrototypeGet,
- MapPrototypeHas,
- MapPrototypeSet,
- MathCeil,
- ObjectKeys,
- ObjectPrototypeIsPrototypeOf,
- Promise,
- SafeArrayIterator,
- Set,
- SymbolToStringTag,
- TypeError,
- } = window.__bootstrap.primordials;
-
- const opSanitizerDelayResolveQueue = [];
-
- // Even if every resource is closed by the end of a test, there can be a delay
- // until the pending ops have all finished. This function returns a promise
- // that resolves when it's (probably) fine to run the op sanitizer.
- //
- // This is implemented by adding a macrotask callback that runs after the
- // timer macrotasks, so we can guarantee that a currently running interval
- // will have an associated op. An additional `setTimeout` of 0 is needed
- // before that, though, in order to give time for worker message ops to finish
- // (since timeouts of 0 don't queue tasks in the timer queue immediately).
- function opSanitizerDelay() {
- return new Promise((resolve) => {
- setTimeout(() => {
- ArrayPrototypePush(opSanitizerDelayResolveQueue, resolve);
- }, 0);
- });
- }
-
- function handleOpSanitizerDelayMacrotask() {
- ArrayPrototypeShift(opSanitizerDelayResolveQueue)?.();
- return opSanitizerDelayResolveQueue.length === 0;
- }
-
- // An async operation to $0 was started in this test, but never completed. This is often caused by not $1.
- // An async operation to $0 was started in this test, but never completed. Async operations should not complete in a test if they were not started in that test.
- // deno-fmt-ignore
- const OP_DETAILS = {
- "op_blob_read_part": ["read from a Blob or File", "awaiting the result of a Blob or File read"],
- "op_broadcast_recv": ["receive a message from a BroadcastChannel", "closing the BroadcastChannel"],
- "op_broadcast_send": ["send a message to a BroadcastChannel", "closing the BroadcastChannel"],
- "op_chmod_async": ["change the permissions of a file", "awaiting the result of a `Deno.chmod` call"],
- "op_chown_async": ["change the owner of a file", "awaiting the result of a `Deno.chown` call"],
- "op_copy_file_async": ["copy a file", "awaiting the result of a `Deno.copyFile` call"],
- "op_crypto_decrypt": ["decrypt data", "awaiting the result of a `crypto.subtle.decrypt` call"],
- "op_crypto_derive_bits": ["derive bits from a key", "awaiting the result of a `crypto.subtle.deriveBits` call"],
- "op_crypto_encrypt": ["encrypt data", "awaiting the result of a `crypto.subtle.encrypt` call"],
- "op_crypto_generate_key": ["generate a key", "awaiting the result of a `crypto.subtle.generateKey` call"],
- "op_crypto_sign_key": ["sign data", "awaiting the result of a `crypto.subtle.sign` call"],
- "op_crypto_subtle_digest": ["digest data", "awaiting the result of a `crypto.subtle.digest` call"],
- "op_crypto_verify_key": ["verify data", "awaiting the result of a `crypto.subtle.verify` call"],
- "op_net_recv_udp": ["receive a datagram message via UDP", "awaiting the result of `Deno.DatagramConn#receive` call, or not breaking out of a for await loop looping over a `Deno.DatagramConn`"],
- "op_net_recv_unixpacket": ["receive a datagram message via Unixpacket", "awaiting the result of `Deno.DatagramConn#receive` call, or not breaking out of a for await loop looping over a `Deno.DatagramConn`"],
- "op_net_send_udp": ["send a datagram message via UDP", "awaiting the result of `Deno.DatagramConn#send` call"],
- "op_net_send_unixpacket": ["send a datagram message via Unixpacket", "awaiting the result of `Deno.DatagramConn#send` call"],
- "op_dns_resolve": ["resolve a DNS name", "awaiting the result of a `Deno.resolveDns` call"],
- "op_fdatasync_async": ["flush pending data operations for a file to disk", "awaiting the result of a `Deno.fdatasync` call"],
- "op_fetch_send": ["send a HTTP request", "awaiting the result of a `fetch` call"],
- "op_ffi_call_nonblocking": ["do a non blocking ffi call", "awaiting the returned promise"] ,
- "op_ffi_call_ptr_nonblocking": ["do a non blocking ffi call", "awaiting the returned promise"],
- "op_flock_async": ["lock a file", "awaiting the result of a `Deno.flock` call"],
- "op_fs_events_poll": ["get the next file system event", "breaking out of a for await loop looping over `Deno.FsEvents`"],
- "op_fstat_async": ["get file metadata", "awaiting the result of a `Deno.File#fstat` call"],
- "op_fsync_async": ["flush pending data operations for a file to disk", "awaiting the result of a `Deno.fsync` call"],
- "op_ftruncate_async": ["truncate a file", "awaiting the result of a `Deno.ftruncate` call"],
- "op_funlock_async": ["unlock a file", "awaiting the result of a `Deno.funlock` call"],
- "op_futime_async": ["change file timestamps", "awaiting the result of a `Deno.futime` call"],
- "op_http_accept": ["accept a HTTP request", "closing a `Deno.HttpConn`"],
- "op_http_shutdown": ["shutdown a HTTP connection", "awaiting `Deno.HttpEvent#respondWith`"],
- "op_http_upgrade_websocket": ["upgrade a HTTP connection to a WebSocket", "awaiting `Deno.HttpEvent#respondWith`"],
- "op_http_write_headers": ["write HTTP response headers", "awaiting `Deno.HttpEvent#respondWith`"],
- "op_http_write": ["write HTTP response body", "awaiting `Deno.HttpEvent#respondWith`"],
- "op_link_async": ["create a hard link", "awaiting the result of a `Deno.link` call"],
- "op_make_temp_dir_async": ["create a temporary directory", "awaiting the result of a `Deno.makeTempDir` call"],
- "op_make_temp_file_async": ["create a temporary file", "awaiting the result of a `Deno.makeTempFile` call"],
- "op_message_port_recv_message": ["receive a message from a MessagePort", "awaiting the result of not closing a `MessagePort`"],
- "op_mkdir_async": ["create a directory", "awaiting the result of a `Deno.mkdir` call"],
- "op_net_accept_tcp": ["accept a TCP stream", "closing a `Deno.Listener`"],
- "op_net_accept_unix": ["accept a Unix stream", "closing a `Deno.Listener`"],
- "op_net_connect_tcp": ["connect to a TCP server", "awaiting a `Deno.connect` call"],
- "op_net_connect_unix": ["connect to a Unix server", "awaiting a `Deno.connect` call"],
- "op_open_async": ["open a file", "awaiting the result of a `Deno.open` call"],
- "op_read_dir_async": ["read a directory", "collecting all items in the async iterable returned from a `Deno.readDir` call"],
- "op_read_link_async": ["read a symlink", "awaiting the result of a `Deno.readLink` call"],
- "op_realpath_async": ["resolve a path", "awaiting the result of a `Deno.realpath` call"],
- "op_remove_async": ["remove a file or directory", "awaiting the result of a `Deno.remove` call"],
- "op_rename_async": ["rename a file or directory", "awaiting the result of a `Deno.rename` call"],
- "op_run_status": ["get the status of a subprocess", "awaiting the result of a `Deno.Process#status` call"],
- "op_seek_async": ["seek in a file", "awaiting the result of a `Deno.File#seek` call"],
- "op_signal_poll": ["get the next signal", "un-registering a OS signal handler"],
- "op_sleep": ["sleep for a duration", "cancelling a `setTimeout` or `setInterval` call"],
- "op_stat_async": ["get file metadata", "awaiting the result of a `Deno.stat` call"],
- "op_symlink_async": ["create a symlink", "awaiting the result of a `Deno.symlink` call"],
- "op_net_accept_tls": ["accept a TLS stream", "closing a `Deno.TlsListener`"],
- "op_net_connect_tls": ["connect to a TLS server", "awaiting a `Deno.connectTls` call"],
- "op_tls_handshake": ["perform a TLS handshake", "awaiting a `Deno.TlsConn#handshake` call"],
- "op_tls_start": ["start a TLS connection", "awaiting a `Deno.startTls` call"],
- "op_truncate_async": ["truncate a file", "awaiting the result of a `Deno.truncate` call"],
- "op_utime_async": ["change file timestamps", "awaiting the result of a `Deno.utime` call"],
- "op_webgpu_buffer_get_map_async": ["map a WebGPU buffer", "awaiting the result of a `GPUBuffer#mapAsync` call"],
- "op_webgpu_request_adapter": ["request a WebGPU adapter", "awaiting the result of a `navigator.gpu.requestAdapter` call"],
- "op_webgpu_request_device": ["request a WebGPU device", "awaiting the result of a `GPUAdapter#requestDevice` call"],
- "op_worker_recv_message": ["receive a message from a web worker", "terminating a `Worker`"],
- "op_ws_close": ["close a WebSocket", "awaiting until the `close` event is emitted on a `WebSocket`, or the `WebSocketStream#closed` promise resolves"],
- "op_ws_create": ["create a WebSocket", "awaiting until the `open` event is emitted on a `WebSocket`, or the result of a `WebSocketStream#connection` promise"],
- "op_ws_next_event": ["receive the next message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
- "op_ws_send": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
- };
-
- // Wrap test function in additional assertion that makes sure
- // the test case does not leak async "ops" - ie. number of async
- // completed ops after the test is the same as number of dispatched
- // ops. Note that "unref" ops are ignored since in nature that are
- // optional.
- function assertOps(fn) {
- /** @param desc {TestDescription | TestStepDescription} */
- return async function asyncOpSanitizer(desc) {
- const pre = core.metrics();
- const preTraces = new Map(core.opCallTraces);
- try {
- await fn(desc);
- } finally {
- // Defer until next event loop turn - that way timeouts and intervals
- // cleared can actually be removed from resource table, otherwise
- // false positives may occur (https://github.com/denoland/deno/issues/4591)
- await opSanitizerDelay();
- await opSanitizerDelay();
- }
-
- if (shouldSkipSanitizers(desc)) return;
-
- const post = core.metrics();
- const postTraces = new Map(core.opCallTraces);
-
- // We're checking diff because one might spawn HTTP server in the background
- // that will be a pending async op before test starts.
- const dispatchedDiff = post.opsDispatchedAsync - pre.opsDispatchedAsync;
- const completedDiff = post.opsCompletedAsync - pre.opsCompletedAsync;
-
- if (dispatchedDiff === completedDiff) return;
-
- const details = [];
- for (const key in post.ops) {
- const preOp = pre.ops[key] ??
- { opsDispatchedAsync: 0, opsCompletedAsync: 0 };
- const postOp = post.ops[key];
- const dispatchedDiff = postOp.opsDispatchedAsync -
- preOp.opsDispatchedAsync;
- const completedDiff = postOp.opsCompletedAsync -
- preOp.opsCompletedAsync;
-
- if (dispatchedDiff > completedDiff) {
- const [name, hint] = OP_DETAILS[key] || [key, null];
- const count = dispatchedDiff - completedDiff;
- let message = `${count} async operation${
- count === 1 ? "" : "s"
- } to ${name} ${
- count === 1 ? "was" : "were"
- } started in this test, but never completed.`;
- if (hint) {
- message += ` This is often caused by not ${hint}.`;
- }
- const traces = [];
- for (const [id, { opName, stack }] of postTraces) {
- if (opName !== key) continue;
- if (MapPrototypeHas(preTraces, id)) continue;
- ArrayPrototypePush(traces, stack);
- }
- if (traces.length === 1) {
- message += " The operation was started here:\n";
- message += traces[0];
- } else if (traces.length > 1) {
- message += " The operations were started here:\n";
- message += ArrayPrototypeJoin(traces, "\n\n");
- }
- ArrayPrototypePush(details, message);
- } else if (dispatchedDiff < completedDiff) {
- const [name, hint] = OP_DETAILS[key] || [key, null];
- const count = completedDiff - dispatchedDiff;
- ArrayPrototypePush(
- details,
- `${count} async operation${count === 1 ? "" : "s"} to ${name} ${
- count === 1 ? "was" : "were"
- } started before this test, but ${
- count === 1 ? "was" : "were"
- } completed during the test. Async operations should not complete in a test if they were not started in that test.
- ${hint ? `This is often caused by not ${hint}.` : ""}`,
- );
- }
- }
-
- let msg = `Test case is leaking async ops.
-
- - ${ArrayPrototypeJoin(details, "\n - ")}`;
-
- if (!core.isOpCallTracingEnabled()) {
- msg +=
- `\n\nTo get more details where ops were leaked, run again with --trace-ops flag.`;
- } else {
- msg += "\n";
- }
-
- throw assert(false, msg);
- };
- }
-
- function prettyResourceNames(name) {
- switch (name) {
- case "fsFile":
- return ["A file", "opened", "closed"];
- case "fetchRequest":
- return ["A fetch request", "started", "finished"];
- case "fetchRequestBody":
- return ["A fetch request body", "created", "closed"];
- case "fetchResponseBody":
- return ["A fetch response body", "created", "consumed"];
- case "httpClient":
- return ["An HTTP client", "created", "closed"];
- case "dynamicLibrary":
- return ["A dynamic library", "loaded", "unloaded"];
- case "httpConn":
- return ["An inbound HTTP connection", "accepted", "closed"];
- case "httpStream":
- return ["An inbound HTTP request", "accepted", "closed"];
- case "tcpStream":
- return ["A TCP connection", "opened/accepted", "closed"];
- case "unixStream":
- return ["A Unix connection", "opened/accepted", "closed"];
- case "tlsStream":
- return ["A TLS connection", "opened/accepted", "closed"];
- case "tlsListener":
- return ["A TLS listener", "opened", "closed"];
- case "unixListener":
- return ["A Unix listener", "opened", "closed"];
- case "unixDatagram":
- return ["A Unix datagram", "opened", "closed"];
- case "tcpListener":
- return ["A TCP listener", "opened", "closed"];
- case "udpSocket":
- return ["A UDP socket", "opened", "closed"];
- case "timer":
- return ["A timer", "started", "fired/cleared"];
- case "textDecoder":
- return ["A text decoder", "created", "finished"];
- case "messagePort":
- return ["A message port", "created", "closed"];
- case "webSocketStream":
- return ["A WebSocket", "opened", "closed"];
- case "fsEvents":
- return ["A file system watcher", "created", "closed"];
- case "childStdin":
- return ["A child process stdin", "opened", "closed"];
- case "childStdout":
- return ["A child process stdout", "opened", "closed"];
- case "childStderr":
- return ["A child process stderr", "opened", "closed"];
- case "child":
- return ["A child process", "started", "closed"];
- case "signal":
- return ["A signal listener", "created", "fired/cleared"];
- case "stdin":
- return ["The stdin pipe", "opened", "closed"];
- case "stdout":
- return ["The stdout pipe", "opened", "closed"];
- case "stderr":
- return ["The stderr pipe", "opened", "closed"];
- case "compression":
- return ["A CompressionStream", "created", "closed"];
- default:
- return [`A "${name}" resource`, "created", "cleaned up"];
- }
- }
-
- function resourceCloseHint(name) {
- switch (name) {
- case "fsFile":
- return "Close the file handle by calling `file.close()`.";
- case "fetchRequest":
- return "Await the promise returned from `fetch()` or abort the fetch with an abort signal.";
- case "fetchRequestBody":
- return "Terminate the request body `ReadableStream` by closing or erroring it.";
- case "fetchResponseBody":
- return "Consume or close the response body `ReadableStream`, e.g `await resp.text()` or `await resp.body.cancel()`.";
- case "httpClient":
- return "Close the HTTP client by calling `httpClient.close()`.";
- case "dynamicLibrary":
- return "Unload the dynamic library by calling `dynamicLibrary.close()`.";
- case "httpConn":
- return "Close the inbound HTTP connection by calling `httpConn.close()`.";
- case "httpStream":
- return "Close the inbound HTTP request by responding with `e.respondWith().` or closing the HTTP connection.";
- case "tcpStream":
- return "Close the TCP connection by calling `tcpConn.close()`.";
- case "unixStream":
- return "Close the Unix socket connection by calling `unixConn.close()`.";
- case "tlsStream":
- return "Close the TLS connection by calling `tlsConn.close()`.";
- case "tlsListener":
- return "Close the TLS listener by calling `tlsListener.close()`.";
- case "unixListener":
- return "Close the Unix socket listener by calling `unixListener.close()`.";
- case "unixDatagram":
- return "Close the Unix datagram socket by calling `unixDatagram.close()`.";
- case "tcpListener":
- return "Close the TCP listener by calling `tcpListener.close()`.";
- case "udpSocket":
- return "Close the UDP socket by calling `udpSocket.close()`.";
- case "timer":
- return "Clear the timer by calling `clearInterval` or `clearTimeout`.";
- case "textDecoder":
- return "Close the text decoder by calling `textDecoder.decode('')` or `await textDecoderStream.readable.cancel()`.";
- case "messagePort":
- return "Close the message port by calling `messagePort.close()`.";
- case "webSocketStream":
- return "Close the WebSocket by calling `webSocket.close()`.";
- case "fsEvents":
- return "Close the file system watcher by calling `watcher.close()`.";
- case "childStdin":
- return "Close the child process stdin by calling `proc.stdin.close()`.";
- case "childStdout":
- return "Close the child process stdout by calling `proc.stdout.close()`.";
- case "childStderr":
- return "Close the child process stderr by calling `proc.stderr.close()`.";
- case "child":
- return "Close the child process by calling `proc.kill()` or `proc.close()`.";
- case "signal":
- return "Clear the signal listener by calling `Deno.removeSignalListener`.";
- case "stdin":
- return "Close the stdin pipe by calling `Deno.stdin.close()`.";
- case "stdout":
- return "Close the stdout pipe by calling `Deno.stdout.close()`.";
- case "stderr":
- return "Close the stderr pipe by calling `Deno.stderr.close()`.";
- case "compression":
- return "Close the compression stream by calling `await stream.writable.close()`.";
- default:
- return "Close the resource before the end of the test.";
- }
- }
-
- // Wrap test function in additional assertion that makes sure
- // the test case does not "leak" resources - ie. resource table after
- // the test has exactly the same contents as before the test.
- function assertResources(fn) {
- /** @param desc {TestDescription | TestStepDescription} */
- return async function resourceSanitizer(desc) {
- const pre = core.resources();
- await fn(desc);
-
- if (shouldSkipSanitizers(desc)) {
- return;
- }
-
- const post = core.resources();
-
- const allResources = new Set([
- ...new SafeArrayIterator(ObjectKeys(pre)),
- ...new SafeArrayIterator(ObjectKeys(post)),
- ]);
-
- const details = [];
- for (const resource of allResources) {
- const preResource = pre[resource];
- const postResource = post[resource];
- if (preResource === postResource) continue;
-
- if (preResource === undefined) {
- const [name, action1, action2] = prettyResourceNames(postResource);
- const hint = resourceCloseHint(postResource);
- const detail =
- `${name} (rid ${resource}) was ${action1} during the test, but not ${action2} during the test. ${hint}`;
- ArrayPrototypePush(details, detail);
- } else {
- const [name, action1, action2] = prettyResourceNames(preResource);
- const detail =
- `${name} (rid ${resource}) was ${action1} before the test started, but was ${action2} during the test. Do not close resources in a test that were not created during that test.`;
- ArrayPrototypePush(details, detail);
- }
- }
-
- const message = `Test case is leaking ${details.length} resource${
- details.length === 1 ? "" : "s"
- }:
-
- - ${details.join("\n - ")}
-`;
- assert(details.length === 0, message);
- };
- }
-
- // Wrap test function in additional assertion that makes sure
- // that the test case does not accidentally exit prematurely.
- function assertExit(fn, isTest) {
- return async function exitSanitizer(...params) {
- setExitHandler((exitCode) => {
- assert(
- false,
- `${
- isTest ? "Test case" : "Bench"
- } attempted to exit with exit code: ${exitCode}`,
- );
- });
-
- try {
- await fn(...new SafeArrayIterator(params));
- } catch (err) {
- throw err;
- } finally {
- setExitHandler(null);
- }
- };
- }
-
- function assertTestStepScopes(fn) {
- /** @param desc {TestDescription | TestStepDescription} */
- return async function testStepSanitizer(desc) {
- preValidation();
- // only report waiting after pre-validation
- if (canStreamReporting(desc) && "parent" in desc) {
- stepReportWait(desc);
- }
- await fn(MapPrototypeGet(testStates, desc.id).context);
- testStepPostValidation(desc);
-
- function preValidation() {
- const runningStepDescs = getRunningStepDescs();
- const runningStepDescsWithSanitizers = ArrayPrototypeFilter(
- runningStepDescs,
- (d) => usesSanitizer(d),
- );
-
- if (runningStepDescsWithSanitizers.length > 0) {
- throw new Error(
- "Cannot start test step while another test step with sanitizers is running.\n" +
- runningStepDescsWithSanitizers
- .map((d) => ` * ${getFullName(d)}`)
- .join("\n"),
- );
- }
-
- if (usesSanitizer(desc) && runningStepDescs.length > 0) {
- throw new Error(
- "Cannot start test step with sanitizers while another test step is running.\n" +
- runningStepDescs.map((d) => ` * ${getFullName(d)}`).join("\n"),
- );
- }
-
- function getRunningStepDescs() {
- const results = [];
- let childDesc = desc;
- while (childDesc.parent != null) {
- const state = MapPrototypeGet(testStates, childDesc.parent.id);
- for (const siblingDesc of state.children) {
- if (siblingDesc.id == childDesc.id) {
- continue;
- }
- const siblingState = MapPrototypeGet(testStates, siblingDesc.id);
- if (!siblingState.finalized) {
- ArrayPrototypePush(results, siblingDesc);
- }
- }
- childDesc = childDesc.parent;
- }
- return results;
- }
- }
- };
- }
-
- function testStepPostValidation(desc) {
- // check for any running steps
- for (const childDesc of MapPrototypeGet(testStates, desc.id).children) {
- if (MapPrototypeGet(testStates, childDesc.id).status == "pending") {
- throw new Error(
- "There were still test steps running after the current scope finished execution. Ensure all steps are awaited (ex. `await t.step(...)`).",
- );
- }
- }
-
- // check if an ancestor already completed
- let currentDesc = desc.parent;
- while (currentDesc != null) {
- if (MapPrototypeGet(testStates, currentDesc.id).finalized) {
- throw new Error(
- "Parent scope completed before test step finished execution. Ensure all steps are awaited (ex. `await t.step(...)`).",
- );
- }
- currentDesc = currentDesc.parent;
- }
- }
-
- function pledgePermissions(permissions) {
- return ops.op_pledge_test_permissions(
- serializePermissions(permissions),
- );
- }
-
- function restorePermissions(token) {
- ops.op_restore_test_permissions(token);
- }
-
- function withPermissions(fn, permissions) {
- return async function applyPermissions(...params) {
- const token = pledgePermissions(permissions);
-
- try {
- await fn(...new SafeArrayIterator(params));
- } finally {
- restorePermissions(token);
- }
- };
- }
-
- /**
- * @typedef {{
- * id: number,
- * name: string,
- * fn: TestFunction
- * origin: string,
- * location: TestLocation,
- * filteredOut: boolean,
- * ignore: boolean,
- * only: boolean.
- * sanitizeOps: boolean,
- * sanitizeResources: boolean,
- * sanitizeExit: boolean,
- * permissions: PermissionOptions,
- * }} TestDescription
- *
- * @typedef {{
- * id: number,
- * name: string,
- * fn: TestFunction
- * origin: string,
- * location: TestLocation,
- * ignore: boolean,
- * level: number,
- * parent: TestDescription | TestStepDescription,
- * rootId: number,
- * rootName: String,
- * sanitizeOps: boolean,
- * sanitizeResources: boolean,
- * sanitizeExit: boolean,
- * }} TestStepDescription
- *
- * @typedef {{
- * context: TestContext,
- * children: TestStepDescription[],
- * finalized: boolean,
- * }} TestState
- *
- * @typedef {{
- * context: TestContext,
- * children: TestStepDescription[],
- * finalized: boolean,
- * status: "pending" | "ok" | ""failed" | ignored",
- * error: unknown,
- * elapsed: number | null,
- * reportedWait: boolean,
- * reportedResult: boolean,
- * }} TestStepState
- *
- * @typedef {{
- * id: number,
- * name: string,
- * fn: BenchFunction
- * origin: string,
- * filteredOut: boolean,
- * ignore: boolean,
- * only: boolean.
- * sanitizeExit: boolean,
- * permissions: PermissionOptions,
- * }} BenchDescription
- */
-
- /** @type {TestDescription[]} */
- const testDescs = [];
- /** @type {Map<number, TestState | TestStepState>} */
- const testStates = new Map();
- /** @type {BenchDescription[]} */
- const benchDescs = [];
- let isTestSubcommand = false;
- let isBenchSubcommand = false;
-
- // Main test function provided by Deno.
- function test(
- nameOrFnOrOptions,
- optionsOrFn,
- maybeFn,
- ) {
- if (!isTestSubcommand) {
- return;
- }
-
- let testDesc;
- const defaults = {
- ignore: false,
- only: false,
- sanitizeOps: true,
- sanitizeResources: true,
- sanitizeExit: true,
- permissions: null,
- };
-
- if (typeof nameOrFnOrOptions === "string") {
- if (!nameOrFnOrOptions) {
- throw new TypeError("The test name can't be empty");
- }
- if (typeof optionsOrFn === "function") {
- testDesc = { fn: optionsOrFn, name: nameOrFnOrOptions, ...defaults };
- } else {
- if (!maybeFn || typeof maybeFn !== "function") {
- throw new TypeError("Missing test function");
- }
- if (optionsOrFn.fn != undefined) {
- throw new TypeError(
- "Unexpected 'fn' field in options, test function is already provided as the third argument.",
- );
- }
- if (optionsOrFn.name != undefined) {
- throw new TypeError(
- "Unexpected 'name' field in options, test name is already provided as the first argument.",
- );
- }
- testDesc = {
- ...defaults,
- ...optionsOrFn,
- fn: maybeFn,
- name: nameOrFnOrOptions,
- };
- }
- } else if (typeof nameOrFnOrOptions === "function") {
- if (!nameOrFnOrOptions.name) {
- throw new TypeError("The test function must have a name");
- }
- if (optionsOrFn != undefined) {
- throw new TypeError("Unexpected second argument to Deno.test()");
- }
- if (maybeFn != undefined) {
- throw new TypeError("Unexpected third argument to Deno.test()");
- }
- testDesc = {
- ...defaults,
- fn: nameOrFnOrOptions,
- name: nameOrFnOrOptions.name,
- };
- } else {
- let fn;
- let name;
- if (typeof optionsOrFn === "function") {
- fn = optionsOrFn;
- if (nameOrFnOrOptions.fn != undefined) {
- throw new TypeError(
- "Unexpected 'fn' field in options, test function is already provided as the second argument.",
- );
- }
- name = nameOrFnOrOptions.name ?? fn.name;
- } else {
- if (
- !nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function"
- ) {
- throw new TypeError(
- "Expected 'fn' field in the first argument to be a test function.",
- );
- }
- fn = nameOrFnOrOptions.fn;
- name = nameOrFnOrOptions.name ?? fn.name;
- }
- if (!name) {
- throw new TypeError("The test name can't be empty");
- }
- testDesc = { ...defaults, ...nameOrFnOrOptions, fn, name };
- }
-
- // Delete this prop in case the user passed it. It's used to detect steps.
- delete testDesc.parent;
- testDesc.fn = wrapTestFnWithSanitizers(testDesc.fn, testDesc);
- if (testDesc.permissions) {
- testDesc.fn = withPermissions(
- testDesc.fn,
- testDesc.permissions,
- );
- }
- testDesc.origin = getTestOrigin();
- const jsError = Deno.core.destructureError(new Error());
- testDesc.location = {
- fileName: jsError.frames[1].fileName,
- lineNumber: jsError.frames[1].lineNumber,
- columnNumber: jsError.frames[1].columnNumber,
- };
-
- const { id, filteredOut } = ops.op_register_test(testDesc);
- testDesc.id = id;
- testDesc.filteredOut = filteredOut;
-
- ArrayPrototypePush(testDescs, testDesc);
- MapPrototypeSet(testStates, testDesc.id, {
- context: createTestContext(testDesc),
- children: [],
- finalized: false,
- });
- }
-
- // Main bench function provided by Deno.
- function bench(
- nameOrFnOrOptions,
- optionsOrFn,
- maybeFn,
- ) {
- if (!isBenchSubcommand) {
- return;
- }
-
- let benchDesc;
- const defaults = {
- ignore: false,
- baseline: false,
- only: false,
- sanitizeExit: true,
- permissions: null,
- };
-
- if (typeof nameOrFnOrOptions === "string") {
- if (!nameOrFnOrOptions) {
- throw new TypeError("The bench name can't be empty");
- }
- if (typeof optionsOrFn === "function") {
- benchDesc = { fn: optionsOrFn, name: nameOrFnOrOptions, ...defaults };
- } else {
- if (!maybeFn || typeof maybeFn !== "function") {
- throw new TypeError("Missing bench function");
- }
- if (optionsOrFn.fn != undefined) {
- throw new TypeError(
- "Unexpected 'fn' field in options, bench function is already provided as the third argument.",
- );
- }
- if (optionsOrFn.name != undefined) {
- throw new TypeError(
- "Unexpected 'name' field in options, bench name is already provided as the first argument.",
- );
- }
- benchDesc = {
- ...defaults,
- ...optionsOrFn,
- fn: maybeFn,
- name: nameOrFnOrOptions,
- };
- }
- } else if (typeof nameOrFnOrOptions === "function") {
- if (!nameOrFnOrOptions.name) {
- throw new TypeError("The bench function must have a name");
- }
- if (optionsOrFn != undefined) {
- throw new TypeError("Unexpected second argument to Deno.bench()");
- }
- if (maybeFn != undefined) {
- throw new TypeError("Unexpected third argument to Deno.bench()");
- }
- benchDesc = {
- ...defaults,
- fn: nameOrFnOrOptions,
- name: nameOrFnOrOptions.name,
- };
- } else {
- let fn;
- let name;
- if (typeof optionsOrFn === "function") {
- fn = optionsOrFn;
- if (nameOrFnOrOptions.fn != undefined) {
- throw new TypeError(
- "Unexpected 'fn' field in options, bench function is already provided as the second argument.",
- );
- }
- name = nameOrFnOrOptions.name ?? fn.name;
- } else {
- if (
- !nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function"
- ) {
- throw new TypeError(
- "Expected 'fn' field in the first argument to be a bench function.",
- );
- }
- fn = nameOrFnOrOptions.fn;
- name = nameOrFnOrOptions.name ?? fn.name;
- }
- if (!name) {
- throw new TypeError("The bench name can't be empty");
- }
- benchDesc = { ...defaults, ...nameOrFnOrOptions, fn, name };
- }
-
- benchDesc.origin = getBenchOrigin();
- const AsyncFunction = (async () => {}).constructor;
- benchDesc.async = AsyncFunction === benchDesc.fn.constructor;
-
- const { id, filteredOut } = ops.op_register_bench(benchDesc);
- benchDesc.id = id;
- benchDesc.filteredOut = filteredOut;
-
- ArrayPrototypePush(benchDescs, benchDesc);
- }
-
- async function runTest(desc) {
- if (desc.ignore) {
- return "ignored";
- }
-
- try {
- await desc.fn(desc);
- const failCount = failedChildStepsCount(desc);
- return failCount === 0 ? "ok" : {
- "failed": core.destructureError(
- new Error(
- `${failCount} test step${failCount === 1 ? "" : "s"} failed.`,
- ),
- ),
- };
- } catch (error) {
- return {
- "failed": core.destructureError(error),
- };
- } finally {
- const state = MapPrototypeGet(testStates, desc.id);
- state.finalized = true;
- // ensure the children report their result
- for (const childDesc of state.children) {
- stepReportResult(childDesc);
- }
- }
- }
-
- function compareMeasurements(a, b) {
- if (a > b) return 1;
- if (a < b) return -1;
-
- return 0;
- }
-
- function benchStats(n, highPrecision, avg, min, max, all) {
- return {
- n,
- min,
- max,
- p75: all[MathCeil(n * (75 / 100)) - 1],
- p99: all[MathCeil(n * (99 / 100)) - 1],
- p995: all[MathCeil(n * (99.5 / 100)) - 1],
- p999: all[MathCeil(n * (99.9 / 100)) - 1],
- avg: !highPrecision ? (avg / n) : MathCeil(avg / n),
- };
- }
-
- async function benchMeasure(timeBudget, desc) {
- const fn = desc.fn;
- let n = 0;
- let avg = 0;
- let wavg = 0;
- const all = [];
- let min = Infinity;
- let max = -Infinity;
- const lowPrecisionThresholdInNs = 1e4;
-
- // warmup step
- let c = 0;
- let iterations = 20;
- let budget = 10 * 1e6;
-
- if (!desc.async) {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
-
- fn();
- const iterationTime = benchNow() - t1;
-
- c++;
- wavg += iterationTime;
- budget -= iterationTime;
- }
- } else {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
-
- await fn();
- const iterationTime = benchNow() - t1;
-
- c++;
- wavg += iterationTime;
- budget -= iterationTime;
- }
- }
-
- wavg /= c;
-
- // measure step
- if (wavg > lowPrecisionThresholdInNs) {
- let iterations = 10;
- let budget = timeBudget * 1e6;
-
- if (!desc.async) {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
-
- fn();
- const iterationTime = benchNow() - t1;
-
- n++;
- avg += iterationTime;
- budget -= iterationTime;
- ArrayPrototypePush(all, iterationTime);
- if (iterationTime < min) min = iterationTime;
- if (iterationTime > max) max = iterationTime;
- }
- } else {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
-
- await fn();
- const iterationTime = benchNow() - t1;
-
- n++;
- avg += iterationTime;
- budget -= iterationTime;
- ArrayPrototypePush(all, iterationTime);
- if (iterationTime < min) min = iterationTime;
- if (iterationTime > max) max = iterationTime;
- }
- }
- } else {
- let iterations = 10;
- let budget = timeBudget * 1e6;
-
- if (!desc.async) {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
- for (let c = 0; c < lowPrecisionThresholdInNs; c++) fn();
- const iterationTime = (benchNow() - t1) / lowPrecisionThresholdInNs;
-
- n++;
- avg += iterationTime;
- ArrayPrototypePush(all, iterationTime);
- if (iterationTime < min) min = iterationTime;
- if (iterationTime > max) max = iterationTime;
- budget -= iterationTime * lowPrecisionThresholdInNs;
- }
- } else {
- while (budget > 0 || iterations-- > 0) {
- const t1 = benchNow();
- for (let c = 0; c < lowPrecisionThresholdInNs; c++) await fn();
- const iterationTime = (benchNow() - t1) / lowPrecisionThresholdInNs;
-
- n++;
- avg += iterationTime;
- ArrayPrototypePush(all, iterationTime);
- if (iterationTime < min) min = iterationTime;
- if (iterationTime > max) max = iterationTime;
- budget -= iterationTime * lowPrecisionThresholdInNs;
- }
- }
- }
-
- all.sort(compareMeasurements);
- return benchStats(n, wavg > lowPrecisionThresholdInNs, avg, min, max, all);
- }
-
- async function runBench(desc) {
- let token = null;
-
- try {
- if (desc.permissions) {
- token = pledgePermissions(desc.permissions);
- }
-
- if (desc.sanitizeExit) {
- setExitHandler((exitCode) => {
- assert(
- false,
- `Bench attempted to exit with exit code: ${exitCode}`,
- );
- });
- }
-
- const benchTimeInMs = 500;
- const stats = await benchMeasure(benchTimeInMs, desc);
-
- return { ok: stats };
- } catch (error) {
- return { failed: core.destructureError(error) };
- } finally {
- if (bench.sanitizeExit) setExitHandler(null);
- if (token !== null) restorePermissions(token);
- }
- }
-
- let origin = null;
-
- function getTestOrigin() {
- if (origin == null) {
- origin = ops.op_get_test_origin();
- }
- return origin;
- }
-
- function getBenchOrigin() {
- if (origin == null) {
- origin = ops.op_get_bench_origin();
- }
- return origin;
- }
-
- function benchNow() {
- return ops.op_bench_now();
- }
-
- function enableTest() {
- isTestSubcommand = true;
- }
-
- function enableBench() {
- isBenchSubcommand = true;
- }
-
- async function runTests({
- shuffle = null,
- } = {}) {
- core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
-
- const origin = getTestOrigin();
- const only = ArrayPrototypeFilter(testDescs, (test) => test.only);
- const filtered = ArrayPrototypeFilter(
- only.length > 0 ? only : testDescs,
- (desc) => !desc.filteredOut,
- );
-
- ops.op_dispatch_test_event({
- plan: {
- origin,
- total: filtered.length,
- filteredOut: testDescs.length - filtered.length,
- usedOnly: only.length > 0,
- },
- });
-
- if (shuffle !== null) {
- // http://en.wikipedia.org/wiki/Linear_congruential_generator
- // Use BigInt for everything because the random seed is u64.
- const nextInt = (function (state) {
- const m = 0x80000000n;
- const a = 1103515245n;
- const c = 12345n;
-
- return function (max) {
- return state = ((a * state + c) % m) % BigInt(max);
- };
- }(BigInt(shuffle)));
-
- for (let i = filtered.length - 1; i > 0; i--) {
- const j = nextInt(i);
- [filtered[i], filtered[j]] = [filtered[j], filtered[i]];
- }
- }
-
- for (const desc of filtered) {
- ops.op_dispatch_test_event({ wait: desc.id });
- const earlier = DateNow();
- const result = await runTest(desc);
- const elapsed = DateNow() - earlier;
- ops.op_dispatch_test_event({
- result: [desc.id, result, elapsed],
- });
- }
- }
-
- async function runBenchmarks() {
- core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
-
- const origin = getBenchOrigin();
- const originalConsole = globalThis.console;
-
- globalThis.console = new Console((s) => {
- ops.op_dispatch_bench_event({ output: s });
- });
-
- const only = ArrayPrototypeFilter(benchDescs, (bench) => bench.only);
- const filtered = ArrayPrototypeFilter(
- only.length > 0 ? only : benchDescs,
- (desc) => !desc.filteredOut && !desc.ignore,
- );
-
- let groups = new Set();
- // make sure ungrouped benchmarks are placed above grouped
- groups.add(undefined);
-
- for (const desc of filtered) {
- desc.group ||= undefined;
- groups.add(desc.group);
- }
-
- groups = ArrayFrom(groups);
- ArrayPrototypeSort(
- filtered,
- (a, b) => groups.indexOf(a.group) - groups.indexOf(b.group),
- );
-
- ops.op_dispatch_bench_event({
- plan: {
- origin,
- total: filtered.length,
- usedOnly: only.length > 0,
- names: ArrayPrototypeMap(filtered, (desc) => desc.name),
- },
- });
-
- for (const desc of filtered) {
- desc.baseline = !!desc.baseline;
- ops.op_dispatch_bench_event({ wait: desc.id });
- ops.op_dispatch_bench_event({
- result: [desc.id, await runBench(desc)],
- });
- }
-
- globalThis.console = originalConsole;
- }
-
- function getFullName(desc) {
- if ("parent" in desc) {
- return `${desc.parent.name} > ${desc.name}`;
- }
- return desc.name;
- }
-
- function usesSanitizer(desc) {
- return desc.sanitizeResources || desc.sanitizeOps || desc.sanitizeExit;
- }
-
- function canStreamReporting(desc) {
- let currentDesc = desc;
- while (currentDesc != null) {
- if (!usesSanitizer(currentDesc)) {
- return false;
- }
- currentDesc = currentDesc.parent;
- }
- for (const childDesc of MapPrototypeGet(testStates, desc.id).children) {
- const state = MapPrototypeGet(testStates, childDesc.id);
- if (!usesSanitizer(childDesc) && !state.finalized) {
- return false;
- }
- }
- return true;
- }
-
- function stepReportWait(desc) {
- const state = MapPrototypeGet(testStates, desc.id);
- if (state.reportedWait) {
- return;
- }
- ops.op_dispatch_test_event({ stepWait: desc.id });
- state.reportedWait = true;
- }
-
- function stepReportResult(desc) {
- const state = MapPrototypeGet(testStates, desc.id);
- if (state.reportedResult) {
- return;
- }
- stepReportWait(desc);
- for (const childDesc of state.children) {
- stepReportResult(childDesc);
- }
- let result;
- if (state.status == "pending" || state.status == "failed") {
- result = {
- [state.status]: state.error && core.destructureError(state.error),
- };
- } else {
- result = state.status;
- }
- ops.op_dispatch_test_event({
- stepResult: [desc.id, result, state.elapsed],
- });
- state.reportedResult = true;
- }
-
- function failedChildStepsCount(desc) {
- return ArrayPrototypeFilter(
- MapPrototypeGet(testStates, desc.id).children,
- (d) => MapPrototypeGet(testStates, d.id).status === "failed",
- ).length;
- }
-
- /** If a test validation error already occurred then don't bother checking
- * the sanitizers as that will create extra noise.
- */
- function shouldSkipSanitizers(desc) {
- try {
- testStepPostValidation(desc);
- return false;
- } catch {
- return true;
- }
- }
-
- /** @param desc {TestDescription | TestStepDescription} */
- function createTestContext(desc) {
- let parent;
- let level;
- let rootId;
- let rootName;
- if ("parent" in desc) {
- parent = MapPrototypeGet(testStates, desc.parent.id).context;
- level = desc.level;
- rootId = desc.rootId;
- rootName = desc.rootName;
- } else {
- parent = undefined;
- level = 0;
- rootId = desc.id;
- rootName = desc.name;
- }
- return {
- [SymbolToStringTag]: "TestContext",
- /**
- * The current test name.
- */
- name: desc.name,
- /**
- * Parent test context.
- */
- parent,
- /**
- * File Uri of the test code.
- */
- origin: desc.origin,
- /**
- * @param nameOrTestDefinition {string | TestStepDefinition}
- * @param fn {(t: TestContext) => void | Promise<void>}
- */
- async step(nameOrTestDefinition, fn) {
- if (MapPrototypeGet(testStates, desc.id).finalized) {
- throw new Error(
- "Cannot run test step after parent scope has finished execution. " +
- "Ensure any `.step(...)` calls are executed before their parent scope completes execution.",
- );
- }
-
- let stepDesc;
- if (typeof nameOrTestDefinition === "string") {
- if (!(ObjectPrototypeIsPrototypeOf(FunctionPrototype, fn))) {
- throw new TypeError("Expected function for second argument.");
- }
- stepDesc = {
- name: nameOrTestDefinition,
- fn,
- };
- } else if (typeof nameOrTestDefinition === "object") {
- stepDesc = nameOrTestDefinition;
- } else {
- throw new TypeError(
- "Expected a test definition or name and function.",
- );
- }
- stepDesc.ignore ??= false;
- stepDesc.sanitizeOps ??= desc.sanitizeOps;
- stepDesc.sanitizeResources ??= desc.sanitizeResources;
- stepDesc.sanitizeExit ??= desc.sanitizeExit;
- stepDesc.origin = getTestOrigin();
- const jsError = Deno.core.destructureError(new Error());
- stepDesc.location = {
- fileName: jsError.frames[1].fileName,
- lineNumber: jsError.frames[1].lineNumber,
- columnNumber: jsError.frames[1].columnNumber,
- };
- stepDesc.level = level + 1;
- stepDesc.parent = desc;
- stepDesc.rootId = rootId;
- stepDesc.rootName = rootName;
- const { id } = ops.op_register_test_step(stepDesc);
- stepDesc.id = id;
- const state = {
- context: createTestContext(stepDesc),
- children: [],
- finalized: false,
- status: "pending",
- error: null,
- elapsed: null,
- reportedWait: false,
- reportedResult: false,
- };
- MapPrototypeSet(testStates, stepDesc.id, state);
- ArrayPrototypePush(
- MapPrototypeGet(testStates, stepDesc.parent.id).children,
- stepDesc,
- );
-
- try {
- if (stepDesc.ignore) {
- state.status = "ignored";
- state.finalized = true;
- if (canStreamReporting(stepDesc)) {
- stepReportResult(stepDesc);
- }
- return false;
- }
-
- const testFn = wrapTestFnWithSanitizers(stepDesc.fn, stepDesc);
- const start = DateNow();
-
- try {
- await testFn(stepDesc);
-
- if (failedChildStepsCount(stepDesc) > 0) {
- state.status = "failed";
- } else {
- state.status = "ok";
- }
- } catch (error) {
- state.error = error;
- state.status = "failed";
- }
-
- state.elapsed = DateNow() - start;
-
- if (MapPrototypeGet(testStates, stepDesc.parent.id).finalized) {
- // always point this test out as one that was still running
- // if the parent step finalized
- state.status = "pending";
- }
-
- state.finalized = true;
-
- if (state.reportedWait && canStreamReporting(stepDesc)) {
- stepReportResult(stepDesc);
- }
-
- return state.status === "ok";
- } finally {
- if (canStreamReporting(stepDesc.parent)) {
- const parentState = MapPrototypeGet(testStates, stepDesc.parent.id);
- // flush any buffered steps
- for (const childDesc of parentState.children) {
- stepReportResult(childDesc);
- }
- }
- }
- },
- };
- }
-
- /**
- * @template T {Function}
- * @param testFn {T}
- * @param opts {{
- * sanitizeOps: boolean,
- * sanitizeResources: boolean,
- * sanitizeExit: boolean,
- * }}
- * @returns {T}
- */
- function wrapTestFnWithSanitizers(testFn, opts) {
- testFn = assertTestStepScopes(testFn);
-
- if (opts.sanitizeOps) {
- testFn = assertOps(testFn);
- }
- if (opts.sanitizeResources) {
- testFn = assertResources(testFn);
- }
- if (opts.sanitizeExit) {
- testFn = assertExit(testFn, true);
- }
- return testFn;
- }
-
- window.__bootstrap.testing = {
- bench,
- enableBench,
- enableTest,
- runBenchmarks,
- runTests,
- test,
- };
-})(this);
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 16dd5c72f..cd6c07464 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -7,8 +7,6 @@
__bootstrap.denoNs = {
metrics: core.metrics,
- test: __bootstrap.testing.test,
- bench: __bootstrap.testing.bench,
Process: __bootstrap.process.Process,
run: __bootstrap.process.run,
isatty: __bootstrap.tty.isatty,
diff --git a/runtime/js/98_global_scope.js b/runtime/js/98_global_scope.js
new file mode 100644
index 000000000..7afb4472c
--- /dev/null
+++ b/runtime/js/98_global_scope.js
@@ -0,0 +1,352 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+"use strict";
+
+((window) => {
+ const core = Deno.core;
+ const {
+ ObjectDefineProperties,
+ SymbolFor,
+ } = window.__bootstrap.primordials;
+
+ const util = window.__bootstrap.util;
+ const location = window.__bootstrap.location;
+ const event = window.__bootstrap.event;
+ const eventTarget = window.__bootstrap.eventTarget;
+ const timers = window.__bootstrap.timers;
+ const base64 = window.__bootstrap.base64;
+ const encoding = window.__bootstrap.encoding;
+ const Console = window.__bootstrap.console.Console;
+ const caches = window.__bootstrap.caches;
+ const compression = window.__bootstrap.compression;
+ const worker = window.__bootstrap.worker;
+ const performance = window.__bootstrap.performance;
+ const crypto = window.__bootstrap.crypto;
+ const url = window.__bootstrap.url;
+ const urlPattern = window.__bootstrap.urlPattern;
+ const headers = window.__bootstrap.headers;
+ const streams = window.__bootstrap.streams;
+ const fileReader = window.__bootstrap.fileReader;
+ const webgpu = window.__bootstrap.webgpu;
+ const webSocket = window.__bootstrap.webSocket;
+ const broadcastChannel = window.__bootstrap.broadcastChannel;
+ const file = window.__bootstrap.file;
+ const formData = window.__bootstrap.formData;
+ const fetch = window.__bootstrap.fetch;
+ const messagePort = window.__bootstrap.messagePort;
+ const webidl = window.__bootstrap.webidl;
+ const domException = window.__bootstrap.domException;
+ const abortSignal = window.__bootstrap.abortSignal;
+ const globalInterfaces = window.__bootstrap.globalInterfaces;
+ const webStorage = window.__bootstrap.webStorage;
+ const prompt = window.__bootstrap.prompt;
+
+ // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
+ const windowOrWorkerGlobalScope = {
+ AbortController: util.nonEnumerable(abortSignal.AbortController),
+ AbortSignal: util.nonEnumerable(abortSignal.AbortSignal),
+ Blob: util.nonEnumerable(file.Blob),
+ ByteLengthQueuingStrategy: util.nonEnumerable(
+ streams.ByteLengthQueuingStrategy,
+ ),
+ CloseEvent: util.nonEnumerable(event.CloseEvent),
+ CompressionStream: util.nonEnumerable(compression.CompressionStream),
+ CountQueuingStrategy: util.nonEnumerable(
+ streams.CountQueuingStrategy,
+ ),
+ CryptoKey: util.nonEnumerable(crypto.CryptoKey),
+ CustomEvent: util.nonEnumerable(event.CustomEvent),
+ DecompressionStream: util.nonEnumerable(compression.DecompressionStream),
+ DOMException: util.nonEnumerable(domException.DOMException),
+ ErrorEvent: util.nonEnumerable(event.ErrorEvent),
+ Event: util.nonEnumerable(event.Event),
+ EventTarget: util.nonEnumerable(eventTarget.EventTarget),
+ File: util.nonEnumerable(file.File),
+ FileReader: util.nonEnumerable(fileReader.FileReader),
+ FormData: util.nonEnumerable(formData.FormData),
+ Headers: util.nonEnumerable(headers.Headers),
+ MessageEvent: util.nonEnumerable(event.MessageEvent),
+ Performance: util.nonEnumerable(performance.Performance),
+ PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
+ PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
+ PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
+ PromiseRejectionEvent: util.nonEnumerable(event.PromiseRejectionEvent),
+ ProgressEvent: util.nonEnumerable(event.ProgressEvent),
+ ReadableStream: util.nonEnumerable(streams.ReadableStream),
+ ReadableStreamDefaultReader: util.nonEnumerable(
+ streams.ReadableStreamDefaultReader,
+ ),
+ Request: util.nonEnumerable(fetch.Request),
+ Response: util.nonEnumerable(fetch.Response),
+ TextDecoder: util.nonEnumerable(encoding.TextDecoder),
+ TextEncoder: util.nonEnumerable(encoding.TextEncoder),
+ TextDecoderStream: util.nonEnumerable(encoding.TextDecoderStream),
+ TextEncoderStream: util.nonEnumerable(encoding.TextEncoderStream),
+ TransformStream: util.nonEnumerable(streams.TransformStream),
+ URL: util.nonEnumerable(url.URL),
+ URLPattern: util.nonEnumerable(urlPattern.URLPattern),
+ URLSearchParams: util.nonEnumerable(url.URLSearchParams),
+ WebSocket: util.nonEnumerable(webSocket.WebSocket),
+ MessageChannel: util.nonEnumerable(messagePort.MessageChannel),
+ MessagePort: util.nonEnumerable(messagePort.MessagePort),
+ Worker: util.nonEnumerable(worker.Worker),
+ WritableStream: util.nonEnumerable(streams.WritableStream),
+ WritableStreamDefaultWriter: util.nonEnumerable(
+ streams.WritableStreamDefaultWriter,
+ ),
+ WritableStreamDefaultController: util.nonEnumerable(
+ streams.WritableStreamDefaultController,
+ ),
+ ReadableByteStreamController: util.nonEnumerable(
+ streams.ReadableByteStreamController,
+ ),
+ ReadableStreamBYOBReader: util.nonEnumerable(
+ streams.ReadableStreamBYOBReader,
+ ),
+ ReadableStreamBYOBRequest: util.nonEnumerable(
+ streams.ReadableStreamBYOBRequest,
+ ),
+ ReadableStreamDefaultController: util.nonEnumerable(
+ streams.ReadableStreamDefaultController,
+ ),
+ TransformStreamDefaultController: util.nonEnumerable(
+ streams.TransformStreamDefaultController,
+ ),
+ atob: util.writable(base64.atob),
+ btoa: util.writable(base64.btoa),
+ clearInterval: util.writable(timers.clearInterval),
+ clearTimeout: util.writable(timers.clearTimeout),
+ caches: {
+ enumerable: true,
+ configurable: true,
+ get: caches.cacheStorage,
+ },
+ CacheStorage: util.nonEnumerable(caches.CacheStorage),
+ Cache: util.nonEnumerable(caches.Cache),
+ console: util.nonEnumerable(
+ new Console((msg, level) => core.print(msg, level > 1)),
+ ),
+ crypto: util.readOnly(crypto.crypto),
+ Crypto: util.nonEnumerable(crypto.Crypto),
+ SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
+ fetch: util.writable(fetch.fetch),
+ performance: util.writable(performance.performance),
+ reportError: util.writable(event.reportError),
+ setInterval: util.writable(timers.setInterval),
+ setTimeout: util.writable(timers.setTimeout),
+ structuredClone: util.writable(messagePort.structuredClone),
+ // Branding as a WebIDL object
+ [webidl.brand]: util.nonEnumerable(webidl.brand),
+ };
+
+ const unstableWindowOrWorkerGlobalScope = {
+ BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
+ WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
+
+ GPU: util.nonEnumerable(webgpu.GPU),
+ GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
+ GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),
+ GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),
+ GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
+ GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
+ GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
+ GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
+ GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
+ GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
+ GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
+ GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
+ GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
+ GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
+ GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
+ GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
+ GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
+ GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
+ GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
+ GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
+ GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
+ GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
+ GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
+ GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
+ GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
+ GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
+ GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
+ GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
+ GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
+ GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
+ };
+
+ class Navigator {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return `${this.constructor.name} ${inspect({})}`;
+ }
+ }
+
+ const navigator = webidl.createBranded(Navigator);
+
+ let numCpus, userAgent, language;
+
+ function setNumCpus(val) {
+ numCpus = val;
+ }
+
+ function setUserAgent(val) {
+ userAgent = val;
+ }
+
+ function setLanguage(val) {
+ language = val;
+ }
+
+ ObjectDefineProperties(Navigator.prototype, {
+ gpu: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ return webgpu.gpu;
+ },
+ },
+ hardwareConcurrency: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ return numCpus;
+ },
+ },
+ userAgent: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ return userAgent;
+ },
+ },
+ language: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ return language;
+ },
+ },
+ languages: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ return [language];
+ },
+ },
+ });
+ const NavigatorPrototype = Navigator.prototype;
+
+ class WorkerNavigator {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return `${this.constructor.name} ${inspect({})}`;
+ }
+ }
+
+ const workerNavigator = webidl.createBranded(WorkerNavigator);
+
+ ObjectDefineProperties(WorkerNavigator.prototype, {
+ gpu: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, WorkerNavigatorPrototype);
+ return webgpu.gpu;
+ },
+ },
+ hardwareConcurrency: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, WorkerNavigatorPrototype);
+ return numCpus;
+ },
+ language: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, WorkerNavigatorPrototype);
+ return language;
+ },
+ },
+ languages: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, WorkerNavigatorPrototype);
+ return [language];
+ },
+ },
+ },
+ });
+ const WorkerNavigatorPrototype = WorkerNavigator.prototype;
+
+ const mainRuntimeGlobalProperties = {
+ Location: location.locationConstructorDescriptor,
+ location: location.locationDescriptor,
+ Window: globalInterfaces.windowConstructorDescriptor,
+ window: util.readOnly(globalThis),
+ self: util.writable(globalThis),
+ Navigator: util.nonEnumerable(Navigator),
+ navigator: {
+ configurable: true,
+ enumerable: true,
+ get: () => navigator,
+ },
+ alert: util.writable(prompt.alert),
+ confirm: util.writable(prompt.confirm),
+ prompt: util.writable(prompt.prompt),
+ localStorage: {
+ configurable: true,
+ enumerable: true,
+ get: webStorage.localStorage,
+ // Makes this reassignable to make astro work
+ set: () => {},
+ },
+ sessionStorage: {
+ configurable: true,
+ enumerable: true,
+ get: webStorage.sessionStorage,
+ // Makes this reassignable to make astro work
+ set: () => {},
+ },
+ Storage: util.nonEnumerable(webStorage.Storage),
+ };
+
+ const workerRuntimeGlobalProperties = {
+ WorkerLocation: location.workerLocationConstructorDescriptor,
+ location: location.workerLocationDescriptor,
+ WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
+ DedicatedWorkerGlobalScope:
+ globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
+ WorkerNavigator: util.nonEnumerable(WorkerNavigator),
+ navigator: {
+ configurable: true,
+ enumerable: true,
+ get: () => workerNavigator,
+ },
+ self: util.readOnly(globalThis),
+ };
+
+ window.__bootstrap.globalScope = {
+ windowOrWorkerGlobalScope,
+ unstableWindowOrWorkerGlobalScope,
+ mainRuntimeGlobalProperties,
+ workerRuntimeGlobalProperties,
+
+ setNumCpus,
+ setUserAgent,
+ setLanguage,
+ };
+})(this);
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index c7faefcdd..2ea3504e2 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -41,48 +41,37 @@ delete Intl.v8BreakIterator;
const util = window.__bootstrap.util;
const event = window.__bootstrap.event;
const eventTarget = window.__bootstrap.eventTarget;
- const globalInterfaces = window.__bootstrap.globalInterfaces;
const location = window.__bootstrap.location;
const build = window.__bootstrap.build;
const version = window.__bootstrap.version;
const os = window.__bootstrap.os;
const timers = window.__bootstrap.timers;
- const base64 = window.__bootstrap.base64;
- const encoding = window.__bootstrap.encoding;
const colors = window.__bootstrap.colors;
- const Console = window.__bootstrap.console.Console;
- const caches = window.__bootstrap.caches;
const inspectArgs = window.__bootstrap.console.inspectArgs;
const quoteString = window.__bootstrap.console.quoteString;
- const compression = window.__bootstrap.compression;
- const worker = window.__bootstrap.worker;
const internals = window.__bootstrap.internals;
const performance = window.__bootstrap.performance;
const net = window.__bootstrap.net;
- const crypto = window.__bootstrap.crypto;
const url = window.__bootstrap.url;
- const urlPattern = window.__bootstrap.urlPattern;
- const headers = window.__bootstrap.headers;
- const streams = window.__bootstrap.streams;
- const fileReader = window.__bootstrap.fileReader;
- const webgpu = window.__bootstrap.webgpu;
- const webSocket = window.__bootstrap.webSocket;
- const webStorage = window.__bootstrap.webStorage;
- const broadcastChannel = window.__bootstrap.broadcastChannel;
- const file = window.__bootstrap.file;
- const formData = window.__bootstrap.formData;
const fetch = window.__bootstrap.fetch;
- const prompt = window.__bootstrap.prompt;
const messagePort = window.__bootstrap.messagePort;
const denoNs = window.__bootstrap.denoNs;
const denoNsUnstable = window.__bootstrap.denoNsUnstable;
const errors = window.__bootstrap.errors.errors;
const webidl = window.__bootstrap.webidl;
const domException = window.__bootstrap.domException;
- const abortSignal = window.__bootstrap.abortSignal;
const { defineEventHandler, reportException } = window.__bootstrap.event;
const { deserializeJsMessageData, serializeJsMessageData } =
window.__bootstrap.messagePort;
+ const {
+ windowOrWorkerGlobalScope,
+ unstableWindowOrWorkerGlobalScope,
+ workerRuntimeGlobalProperties,
+ mainRuntimeGlobalProperties,
+ setNumCpus,
+ setUserAgent,
+ setLanguage,
+ } = window.__bootstrap.globalScope;
let windowIsClosing = false;
@@ -326,298 +315,6 @@ delete Intl.v8BreakIterator;
);
}
- class Navigator {
- constructor() {
- webidl.illegalConstructor();
- }
-
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return `${this.constructor.name} ${inspect({})}`;
- }
- }
-
- const navigator = webidl.createBranded(Navigator);
-
- let numCpus, userAgent, language;
-
- ObjectDefineProperties(Navigator.prototype, {
- gpu: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, NavigatorPrototype);
- return webgpu.gpu;
- },
- },
- hardwareConcurrency: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, NavigatorPrototype);
- return numCpus;
- },
- },
- userAgent: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, NavigatorPrototype);
- return userAgent;
- },
- },
- language: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, NavigatorPrototype);
- return language;
- },
- },
- languages: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, NavigatorPrototype);
- return [language];
- },
- },
- });
- const NavigatorPrototype = Navigator.prototype;
-
- class WorkerNavigator {
- constructor() {
- webidl.illegalConstructor();
- }
-
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return `${this.constructor.name} ${inspect({})}`;
- }
- }
-
- const workerNavigator = webidl.createBranded(WorkerNavigator);
-
- ObjectDefineProperties(WorkerNavigator.prototype, {
- gpu: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, WorkerNavigatorPrototype);
- return webgpu.gpu;
- },
- },
- hardwareConcurrency: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, WorkerNavigatorPrototype);
- return numCpus;
- },
- language: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, WorkerNavigatorPrototype);
- return language;
- },
- },
- languages: {
- configurable: true,
- enumerable: true,
- get() {
- webidl.assertBranded(this, WorkerNavigatorPrototype);
- return [language];
- },
- },
- },
- });
- const WorkerNavigatorPrototype = WorkerNavigator.prototype;
-
- // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
- const windowOrWorkerGlobalScope = {
- AbortController: util.nonEnumerable(abortSignal.AbortController),
- AbortSignal: util.nonEnumerable(abortSignal.AbortSignal),
- Blob: util.nonEnumerable(file.Blob),
- ByteLengthQueuingStrategy: util.nonEnumerable(
- streams.ByteLengthQueuingStrategy,
- ),
- CloseEvent: util.nonEnumerable(event.CloseEvent),
- CompressionStream: util.nonEnumerable(compression.CompressionStream),
- CountQueuingStrategy: util.nonEnumerable(
- streams.CountQueuingStrategy,
- ),
- CryptoKey: util.nonEnumerable(crypto.CryptoKey),
- CustomEvent: util.nonEnumerable(event.CustomEvent),
- DecompressionStream: util.nonEnumerable(compression.DecompressionStream),
- DOMException: util.nonEnumerable(domException.DOMException),
- ErrorEvent: util.nonEnumerable(event.ErrorEvent),
- Event: util.nonEnumerable(event.Event),
- EventTarget: util.nonEnumerable(eventTarget.EventTarget),
- File: util.nonEnumerable(file.File),
- FileReader: util.nonEnumerable(fileReader.FileReader),
- FormData: util.nonEnumerable(formData.FormData),
- Headers: util.nonEnumerable(headers.Headers),
- MessageEvent: util.nonEnumerable(event.MessageEvent),
- Performance: util.nonEnumerable(performance.Performance),
- PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
- PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
- PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
- PromiseRejectionEvent: util.nonEnumerable(event.PromiseRejectionEvent),
- ProgressEvent: util.nonEnumerable(event.ProgressEvent),
- ReadableStream: util.nonEnumerable(streams.ReadableStream),
- ReadableStreamDefaultReader: util.nonEnumerable(
- streams.ReadableStreamDefaultReader,
- ),
- Request: util.nonEnumerable(fetch.Request),
- Response: util.nonEnumerable(fetch.Response),
- TextDecoder: util.nonEnumerable(encoding.TextDecoder),
- TextEncoder: util.nonEnumerable(encoding.TextEncoder),
- TextDecoderStream: util.nonEnumerable(encoding.TextDecoderStream),
- TextEncoderStream: util.nonEnumerable(encoding.TextEncoderStream),
- TransformStream: util.nonEnumerable(streams.TransformStream),
- URL: util.nonEnumerable(url.URL),
- URLPattern: util.nonEnumerable(urlPattern.URLPattern),
- URLSearchParams: util.nonEnumerable(url.URLSearchParams),
- WebSocket: util.nonEnumerable(webSocket.WebSocket),
- MessageChannel: util.nonEnumerable(messagePort.MessageChannel),
- MessagePort: util.nonEnumerable(messagePort.MessagePort),
- Worker: util.nonEnumerable(worker.Worker),
- WritableStream: util.nonEnumerable(streams.WritableStream),
- WritableStreamDefaultWriter: util.nonEnumerable(
- streams.WritableStreamDefaultWriter,
- ),
- WritableStreamDefaultController: util.nonEnumerable(
- streams.WritableStreamDefaultController,
- ),
- ReadableByteStreamController: util.nonEnumerable(
- streams.ReadableByteStreamController,
- ),
- ReadableStreamBYOBReader: util.nonEnumerable(
- streams.ReadableStreamBYOBReader,
- ),
- ReadableStreamBYOBRequest: util.nonEnumerable(
- streams.ReadableStreamBYOBRequest,
- ),
- ReadableStreamDefaultController: util.nonEnumerable(
- streams.ReadableStreamDefaultController,
- ),
- TransformStreamDefaultController: util.nonEnumerable(
- streams.TransformStreamDefaultController,
- ),
- atob: util.writable(base64.atob),
- btoa: util.writable(base64.btoa),
- clearInterval: util.writable(timers.clearInterval),
- clearTimeout: util.writable(timers.clearTimeout),
- caches: {
- enumerable: true,
- configurable: true,
- get: caches.cacheStorage,
- },
- CacheStorage: util.nonEnumerable(caches.CacheStorage),
- Cache: util.nonEnumerable(caches.Cache),
- console: util.nonEnumerable(
- new Console((msg, level) => core.print(msg, level > 1)),
- ),
- crypto: util.readOnly(crypto.crypto),
- Crypto: util.nonEnumerable(crypto.Crypto),
- SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
- fetch: util.writable(fetch.fetch),
- performance: util.writable(performance.performance),
- reportError: util.writable(event.reportError),
- setInterval: util.writable(timers.setInterval),
- setTimeout: util.writable(timers.setTimeout),
- structuredClone: util.writable(messagePort.structuredClone),
- // Branding as a WebIDL object
- [webidl.brand]: util.nonEnumerable(webidl.brand),
- };
-
- const unstableWindowOrWorkerGlobalScope = {
- BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
- WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
-
- GPU: util.nonEnumerable(webgpu.GPU),
- GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
- GPUSupportedLimits: util.nonEnumerable(webgpu.GPUSupportedLimits),
- GPUSupportedFeatures: util.nonEnumerable(webgpu.GPUSupportedFeatures),
- GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
- GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
- GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
- GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
- GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
- GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
- GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
- GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
- GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
- GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
- GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
- GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
- GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
- GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
- GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
- GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
- GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
- GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
- GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
- GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
- GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
- GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
- GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
- GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
- GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
- GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
- };
-
- const mainRuntimeGlobalProperties = {
- Location: location.locationConstructorDescriptor,
- location: location.locationDescriptor,
- Window: globalInterfaces.windowConstructorDescriptor,
- window: util.readOnly(globalThis),
- self: util.writable(globalThis),
- Navigator: util.nonEnumerable(Navigator),
- navigator: {
- configurable: true,
- enumerable: true,
- get: () => navigator,
- },
- close: util.writable(windowClose),
- closed: util.getterOnly(() => windowIsClosing),
- alert: util.writable(prompt.alert),
- confirm: util.writable(prompt.confirm),
- prompt: util.writable(prompt.prompt),
- localStorage: {
- configurable: true,
- enumerable: true,
- get: webStorage.localStorage,
- // Makes this reassignable to make astro work
- set: () => {},
- },
- sessionStorage: {
- configurable: true,
- enumerable: true,
- get: webStorage.sessionStorage,
- // Makes this reassignable to make astro work
- set: () => {},
- },
- Storage: util.nonEnumerable(webStorage.Storage),
- };
-
- const workerRuntimeGlobalProperties = {
- WorkerLocation: location.workerLocationConstructorDescriptor,
- location: location.workerLocationDescriptor,
- WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
- DedicatedWorkerGlobalScope:
- globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
- WorkerNavigator: util.nonEnumerable(WorkerNavigator),
- navigator: {
- configurable: true,
- enumerable: true,
- get: () => workerNavigator,
- },
- self: util.readOnly(globalThis),
- // TODO(bartlomieju): should be readonly?
- close: util.nonEnumerable(workerClose),
- postMessage: util.writable(postMessage),
- };
-
const pendingRejections = [];
const pendingRejectionsReasons = new SafeWeakMap();
@@ -726,6 +423,10 @@ delete Intl.v8BreakIterator;
ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope);
}
ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
+ ObjectDefineProperties(globalThis, {
+ close: util.writable(windowClose),
+ closed: util.getterOnly(() => windowIsClosing),
+ });
ObjectSetPrototypeOf(globalThis, Window.prototype);
if (runtimeOptions.inspectFlag) {
@@ -754,9 +455,9 @@ delete Intl.v8BreakIterator;
runtimeStart(runtimeOptions);
- numCpus = runtimeOptions.cpuCount;
- userAgent = runtimeOptions.userAgent;
- language = runtimeOptions.locale;
+ setNumCpus(runtimeOptions.cpuCount);
+ setUserAgent(runtimeOptions.userAgent);
+ setLanguage(runtimeOptions.locale);
const internalSymbol = Symbol("Deno.internal");
@@ -849,7 +550,12 @@ delete Intl.v8BreakIterator;
ObjectDefineProperties(globalThis, unstableWindowOrWorkerGlobalScope);
}
ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
- ObjectDefineProperties(globalThis, { name: util.writable(name) });
+ ObjectDefineProperties(globalThis, {
+ name: util.writable(name),
+ // TODO(bartlomieju): should be readonly?
+ close: util.nonEnumerable(workerClose),
+ postMessage: util.writable(postMessage),
+ });
if (runtimeOptions.enableTestingFeaturesFlag) {
ObjectDefineProperty(
globalThis,
@@ -882,8 +588,9 @@ delete Intl.v8BreakIterator;
);
location.setLocationHref(runtimeOptions.location);
- numCpus = runtimeOptions.cpuCount;
- language = runtimeOptions.locale;
+
+ setNumCpus(runtimeOptions.cpuCount);
+ setLanguage(runtimeOptions.locale);
globalThis.pollForMessages = pollForMessages;
diff --git a/runtime/lib.rs b/runtime/lib.rs
index d0d27b71c..de104f2ca 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -7,6 +7,7 @@ pub use deno_core;
pub use deno_crypto;
pub use deno_fetch;
pub use deno_ffi;
+pub use deno_flash;
pub use deno_http;
pub use deno_napi;
pub use deno_net;
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 10d7e440e..a8e0842f2 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -17,7 +17,6 @@ use deno_core::futures::channel::mpsc;
use deno_core::futures::future::poll_fn;
use deno_core::futures::stream::StreamExt;
use deno_core::futures::task::AtomicWaker;
-use deno_core::located_script_name;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde_json::json;
@@ -33,6 +32,7 @@ use deno_core::ModuleSpecifier;
use deno_core::RuntimeOptions;
use deno_core::SharedArrayBufferStore;
use deno_core::SourceMapGetter;
+use deno_core::{located_script_name, Snapshot};
use deno_node::RequireNpmResolver;
use deno_tls::rustls::RootCertStore;
use deno_web::create_entangled_message_port;
@@ -322,6 +322,7 @@ pub struct WebWorker {
pub struct WebWorkerOptions {
pub bootstrap: BootstrapOptions,
pub extensions: Vec<Extension>,
+ pub startup_snapshot: Option<Snapshot>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub root_cert_store: Option<RootCertStore>,
pub seed: Option<u64>,
@@ -451,7 +452,11 @@ impl WebWorker {
let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()),
- startup_snapshot: Some(js::deno_isolate_init()),
+ startup_snapshot: Some(
+ options
+ .startup_snapshot
+ .unwrap_or_else(js::deno_isolate_init),
+ ),
source_map_getter: options.source_map_getter,
get_error_class_fn: options.get_error_class_fn,
shared_array_buffer_store: options.shared_array_buffer_store.clone(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index c7226b76b..0c439c703 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -13,9 +13,6 @@ use deno_core::error::AnyError;
use deno_core::error::JsError;
use deno_core::futures::Future;
use deno_core::located_script_name;
-use deno_core::serde_json::json;
-use deno_core::serde_v8;
-use deno_core::v8;
use deno_core::CompiledWasmModuleStore;
use deno_core::Extension;
use deno_core::FsModuleLoader;
@@ -66,10 +63,6 @@ pub struct MainWorker {
pub js_runtime: JsRuntime,
should_break_on_first_statement: bool,
exit_code: ExitCode,
- js_run_tests_callback: v8::Global<v8::Function>,
- js_run_benchmarks_callback: v8::Global<v8::Function>,
- js_enable_test_callback: v8::Global<v8::Function>,
- js_enable_bench_callback: v8::Global<v8::Function>,
}
pub struct WorkerOptions {
@@ -99,15 +92,6 @@ pub struct WorkerOptions {
pub stdio: Stdio,
}
-fn grab_cb(
- scope: &mut v8::HandleScope,
- path: &str,
-) -> v8::Global<v8::Function> {
- let cb = JsRuntime::eval::<v8::Function>(scope, path)
- .unwrap_or_else(|| panic!("{} must be defined", path));
- v8::Global::new(scope, cb)
-}
-
impl Default for WorkerOptions {
fn default() -> Self {
Self {
@@ -267,29 +251,10 @@ impl MainWorker {
);
}
- let (
- js_run_tests_callback,
- js_run_benchmarks_callback,
- js_enable_test_callback,
- js_enable_bench_callback,
- ) = {
- let scope = &mut js_runtime.handle_scope();
- (
- grab_cb(scope, "__bootstrap.testing.runTests"),
- grab_cb(scope, "__bootstrap.testing.runBenchmarks"),
- grab_cb(scope, "__bootstrap.testing.enableTest"),
- grab_cb(scope, "__bootstrap.testing.enableBench"),
- )
- };
-
Self {
js_runtime,
should_break_on_first_statement: options.should_break_on_first_statement,
exit_code,
- js_run_tests_callback,
- js_run_benchmarks_callback,
- js_enable_test_callback,
- js_enable_bench_callback,
}
}
@@ -377,65 +342,6 @@ impl MainWorker {
self.evaluate_module(id).await
}
- /// Run tests declared with `Deno.test()`. Test events will be dispatched
- /// by calling ops which are currently only implemented in the CLI crate.
- // TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide.
- #[doc(hidden)]
- pub async fn run_tests(
- &mut self,
- shuffle: &Option<u64>,
- ) -> Result<(), AnyError> {
- let promise = {
- let scope = &mut self.js_runtime.handle_scope();
- let cb = self.js_run_tests_callback.open(scope);
- let this = v8::undefined(scope).into();
- let options =
- serde_v8::to_v8(scope, json!({ "shuffle": shuffle })).unwrap();
- let promise = cb.call(scope, this, &[options]).unwrap();
- v8::Global::new(scope, promise)
- };
- self.js_runtime.resolve_value(promise).await?;
- Ok(())
- }
-
- /// Run benches declared with `Deno.bench()`. Bench events will be dispatched
- /// by calling ops which are currently only implemented in the CLI crate.
- // TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide.
- #[doc(hidden)]
- pub async fn run_benchmarks(&mut self) -> Result<(), AnyError> {
- let promise = {
- let scope = &mut self.js_runtime.handle_scope();
- let cb = self.js_run_benchmarks_callback.open(scope);
- let this = v8::undefined(scope).into();
- let promise = cb.call(scope, this, &[]).unwrap();
- v8::Global::new(scope, promise)
- };
- self.js_runtime.resolve_value(promise).await?;
- Ok(())
- }
-
- /// Enable `Deno.test()`. If this isn't called before executing user code,
- /// `Deno.test()` calls will noop.
- // TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide.
- #[doc(hidden)]
- pub fn enable_test(&mut self) {
- let scope = &mut self.js_runtime.handle_scope();
- let cb = self.js_enable_test_callback.open(scope);
- let this = v8::undefined(scope).into();
- cb.call(scope, this, &[]).unwrap();
- }
-
- /// Enable `Deno.bench()`. If this isn't called before executing user code,
- /// `Deno.bench()` calls will noop.
- // TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide.
- #[doc(hidden)]
- pub fn enable_bench(&mut self) {
- let scope = &mut self.js_runtime.handle_scope();
- let cb = self.js_enable_bench_callback.open(scope);
- let this = v8::undefined(scope).into();
- cb.call(scope, this, &[]).unwrap();
- }
-
fn wait_for_inspector_session(&mut self) {
if self.should_break_on_first_statement {
self
@@ -542,107 +448,3 @@ impl MainWorker {
Ok(local_value.is_false())
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use deno_core::resolve_url_or_path;
-
- fn create_test_worker() -> MainWorker {
- let main_module = resolve_url_or_path("./hello.js").unwrap();
- let permissions = Permissions::default();
-
- let options = WorkerOptions {
- bootstrap: BootstrapOptions {
- args: vec![],
- cpu_count: 1,
- debug_flag: false,
- enable_testing_features: false,
- locale: deno_core::v8::icu::get_language_tag(),
- location: None,
- no_color: true,
- is_tty: false,
- runtime_version: "x".to_string(),
- ts_version: "x".to_string(),
- unstable: false,
- user_agent: "x".to_string(),
- inspect: false,
- },
- extensions: vec![],
- startup_snapshot: None,
- unsafely_ignore_certificate_errors: None,
- root_cert_store: None,
- seed: None,
- format_js_error_fn: None,
- source_map_getter: None,
- web_worker_preload_module_cb: Arc::new(|_| unreachable!()),
- web_worker_pre_execute_module_cb: Arc::new(|_| unreachable!()),
- create_web_worker_cb: Arc::new(|_| unreachable!()),
- maybe_inspector_server: None,
- should_break_on_first_statement: false,
- module_loader: Rc::new(FsModuleLoader),
- npm_resolver: None,
- get_error_class_fn: None,
- cache_storage_dir: None,
- origin_storage_dir: None,
- blob_store: BlobStore::default(),
- broadcast_channel: InMemoryBroadcastChannel::default(),
- shared_array_buffer_store: None,
- compiled_wasm_module_store: None,
- stdio: Default::default(),
- };
-
- MainWorker::bootstrap_from_options(main_module, permissions, options)
- }
-
- #[tokio::test]
- async fn execute_mod_esm_imports_a() {
- let p = test_util::testdata_path().join("runtime/esm_imports_a.js");
- let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
- let mut worker = create_test_worker();
- let result = worker.execute_main_module(&module_specifier).await;
- if let Err(err) = result {
- eprintln!("execute_mod err {:?}", err);
- }
- if let Err(e) = worker.run_event_loop(false).await {
- panic!("Future got unexpected error: {:?}", e);
- }
- }
-
- #[tokio::test]
- async fn execute_mod_circular() {
- let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent()
- .unwrap()
- .join("tests/circular1.js");
- let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
- let mut worker = create_test_worker();
- let result = worker.execute_main_module(&module_specifier).await;
- if let Err(err) = result {
- eprintln!("execute_mod err {:?}", err);
- }
- if let Err(e) = worker.run_event_loop(false).await {
- panic!("Future got unexpected error: {:?}", e);
- }
- }
-
- #[tokio::test]
- async fn execute_mod_resolve_error() {
- // "foo" is not a valid module specifier so this should return an error.
- let mut worker = create_test_worker();
- let module_specifier = resolve_url_or_path("does-not-exist").unwrap();
- let result = worker.execute_main_module(&module_specifier).await;
- assert!(result.is_err());
- }
-
- #[tokio::test]
- async fn execute_mod_002_hello() {
- // This assumes cwd is project root (an assumption made throughout the
- // tests).
- let mut worker = create_test_worker();
- let p = test_util::testdata_path().join("run/001_hello.js");
- let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap();
- let result = worker.execute_main_module(&module_specifier).await;
- assert!(result.is_ok());
- }
-}