refactor: allow lazy main module (#28929)

implement lazy(?) mode. an unconfigured jsruntime is created if
DENO_UNSTABLE_CONTROL_SOCK is present, and later passed into deno_runtime to be
configured and used.
This commit is contained in:
snek 2025-04-30 21:59:20 +02:00 committed by GitHub
parent 8c57929058
commit bc1ced8260
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 764 additions and 257 deletions

1
Cargo.lock generated
View file

@ -1563,6 +1563,7 @@ dependencies = [
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tokio-vsock",
"tower 0.5.2",
"tracing",
"tracing-opentelemetry",

View file

@ -192,6 +192,7 @@ winapi = { workspace = true, features = ["knownfolders", "mswsock", "objbase", "
[target.'cfg(unix)'.dependencies]
nix.workspace = true
shell-escape = "=0.1.5"
tokio-vsock.workspace = true
[dev-dependencies]
deno_bench_util.workspace = true

View file

@ -29,6 +29,7 @@ use deno_lib::npm::NpmRegistryReadPermissionChecker;
use deno_lib::npm::NpmRegistryReadPermissionCheckerMode;
use deno_lib::worker::LibMainWorkerFactory;
use deno_lib::worker::LibMainWorkerOptions;
use deno_lib::worker::LibWorkerFactoryRoots;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm_cache::NpmCacheSetting;
use deno_resolver::cjs::IsCjsResolutionMode;
@ -1203,6 +1204,15 @@ impl CliFactory {
pub async fn create_cli_main_worker_factory(
&self,
) -> Result<CliMainWorkerFactory, AnyError> {
self
.create_cli_main_worker_factory_with_roots(Default::default())
.await
}
pub async fn create_cli_main_worker_factory_with_roots(
&self,
roots: LibWorkerFactoryRoots,
) -> Result<CliMainWorkerFactory, AnyError> {
let cli_options = self.cli_options()?;
let fs = self.fs();
@ -1286,6 +1296,7 @@ impl CliFactory {
cli_options.resolve_storage_key_resolver(),
self.sys(),
self.create_lib_main_worker_options()?,
roots,
);
Ok(CliMainWorkerFactory::new(

View file

@ -190,6 +190,12 @@ pub struct LibMainWorkerOptions {
pub serve_host: Option<String>,
}
#[derive(Default, Clone)]
pub struct LibWorkerFactoryRoots {
pub compiled_wasm_module_store: CompiledWasmModuleStore,
pub shared_array_buffer_store: SharedArrayBufferStore,
}
struct LibWorkerFactorySharedState<TSys: DenoLibSys> {
blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel,
@ -370,13 +376,14 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
storage_key_resolver: StorageKeyResolver,
sys: TSys,
options: LibMainWorkerOptions,
roots: LibWorkerFactoryRoots,
) -> Self {
Self {
shared: Arc::new(LibWorkerFactorySharedState {
blob_store,
broadcast_channel: Default::default(),
code_cache,
compiled_wasm_module_store: Default::default(),
compiled_wasm_module_store: roots.compiled_wasm_module_store,
deno_rt_native_addon_loader,
feature_checker,
fs,
@ -386,7 +393,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
npm_process_state_provider,
pkg_json_resolver,
root_cert_store_provider,
shared_array_buffer_store: Default::default(),
shared_array_buffer_store: roots.shared_array_buffer_store,
storage_key_resolver,
sys,
options,
@ -406,6 +413,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
permissions,
vec![],
Default::default(),
None,
)
}
@ -416,6 +424,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
permissions: PermissionsContainer,
custom_extensions: Vec<Extension>,
stdio: deno_runtime::deno_io::Stdio,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
) -> Result<LibMainWorker, CoreError> {
let shared = &self.shared;
let CreateModuleLoaderResult {
@ -516,6 +525,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
stdio,
skip_op_registration: shared.options.skip_op_registration,
enable_stack_trace_arg_in_ops: has_trace_permissions_enabled(),
unconfigured_runtime,
};
let worker =

View file

@ -44,10 +44,12 @@ use deno_core::error::CoreError;
use deno_core::futures::FutureExt;
use deno_core::unsync::JoinHandle;
use deno_lib::util::result::any_and_jserrorbox_downcast_ref;
use deno_lib::worker::LibWorkerFactoryRoots;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
use deno_runtime::UnconfiguredRuntime;
use deno_runtime::WorkerExecutionMode;
pub use deno_runtime::UNSTABLE_FEATURES;
use deno_telemetry::OtelConfig;
@ -106,7 +108,11 @@ fn spawn_subcommand<F: Future<Output = T> + 'static, T: SubcommandOutput>(
)
}
async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
async fn run_subcommand(
flags: Arc<Flags>,
unconfigured_runtime: Option<UnconfiguredRuntime>,
roots: LibWorkerFactoryRoots,
) -> Result<i32, AnyError> {
let handle = match flags.subcommand.clone() {
DenoSubcommand::Add(add_flags) => spawn_subcommand(async {
tools::pm::add(flags, add_flags, tools::pm::AddCommandName::Add).await
@ -217,11 +223,11 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
DenoSubcommand::Run(run_flags) => spawn_subcommand(async move {
if run_flags.is_stdin() {
// these futures are boxed to prevent stack overflows on Windows
tools::run::run_from_stdin(flags.clone()).boxed_local().await
tools::run::run_from_stdin(flags.clone(), unconfigured_runtime, roots).boxed_local().await
} else if flags.eszip {
tools::run::run_eszip(flags, run_flags).boxed_local().await
tools::run::run_eszip(flags, run_flags, unconfigured_runtime, roots).boxed_local().await
} else {
let result = tools::run::run_script(WorkerExecutionMode::Run, flags.clone(), run_flags.watch).await;
let result = tools::run::run_script(WorkerExecutionMode::Run, flags.clone(), run_flags.watch, unconfigured_runtime, roots.clone()).await;
match result {
Ok(v) => Ok(v),
Err(script_err) => {
@ -237,7 +243,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
if flags.frozen_lockfile.is_none() {
flags.internal.lockfile_skip_write = true;
}
return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).boxed_local().await;
return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch, None, roots).boxed_local().await;
}
}
let script_err_msg = script_err.to_string();
@ -451,11 +457,38 @@ pub fn main() {
let args: Vec<_> = env::args_os().collect();
let future = async move {
let roots = LibWorkerFactoryRoots::default();
#[cfg(unix)]
let (waited_unconfigured_runtime, waited_args) =
match wait_for_start(&args, roots.clone()) {
Some(f) => match f.await {
Ok(v) => match v {
Some((u, a)) => (Some(u), Some(a)),
None => (None, None),
},
Err(e) => {
panic!("Failure from control sock: {e}");
}
},
None => (None, None),
};
#[cfg(not(unix))]
let (waited_unconfigured_runtime, waited_args) = (None, None);
let args = waited_args.unwrap_or(args);
// NOTE(lucacasonato): due to new PKU feature introduced in V8 11.6 we need to
// initialize the V8 platform on a parent thread of all threads that will spawn
// V8 isolates.
let flags = resolve_flags_and_init(args)?;
run_subcommand(Arc::new(flags)).await
if waited_unconfigured_runtime.is_none() {
init_v8(&flags);
}
run_subcommand(Arc::new(flags), waited_unconfigured_runtime, roots).await
};
let result = create_and_run_current_thread_with_maybe_metrics(future);
@ -505,6 +538,10 @@ fn resolve_flags_and_init(
);
}
Ok(flags)
}
fn init_v8(flags: &Flags) {
let default_v8_flags = match flags.subcommand {
DenoSubcommand::Lsp => vec![
"--stack-size=1024".to_string(),
@ -527,13 +564,12 @@ fn resolve_flags_and_init(
} else {
None
};
// TODO(bartlomieju): remove last argument once Deploy no longer needs it
deno_core::JsRuntime::init_platform(
v8_platform,
/* import assertions enabled */ false,
);
Ok(flags)
}
fn init_logging(
@ -551,3 +587,103 @@ fn init_logging(
on_log_end: DrawThread::show,
})
}
#[cfg(unix)]
#[allow(clippy::type_complexity)]
fn wait_for_start(
args: &[std::ffi::OsString],
roots: LibWorkerFactoryRoots,
) -> Option<
impl Future<
Output = Result<
Option<(UnconfiguredRuntime, Vec<std::ffi::OsString>)>,
AnyError,
>,
>,
> {
let startup_snapshot = deno_snapshots::CLI_SNAPSHOT?;
let addr = std::env::var("DENO_UNSTABLE_CONTROL_SOCK").ok()?;
std::env::remove_var("DENO_UNSTABLE_CONTROL_SOCK");
let argv0 = args[0].clone();
Some(async move {
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncRead;
use tokio::io::BufReader;
use tokio::net::TcpListener;
use tokio::net::UnixSocket;
use tokio_vsock::VsockAddr;
use tokio_vsock::VsockListener;
init_v8(&Flags::default());
let unconfigured = deno_runtime::UnconfiguredRuntime::new::<
deno_resolver::npm::DenoInNpmPackageChecker,
crate::npm::CliNpmResolver,
crate::sys::CliSys,
>(
startup_snapshot,
deno_lib::worker::create_isolate_create_params(),
Some(roots.shared_array_buffer_store.clone()),
Some(roots.compiled_wasm_module_store.clone()),
vec![],
);
let stream: Box<dyn AsyncRead + Unpin> = match addr.split_once(':') {
Some(("tcp", addr)) => {
let listener = TcpListener::bind(addr).await?;
let (stream, _) = listener.accept().await?;
Box::new(stream)
}
Some(("unix", path)) => {
let socket = UnixSocket::new_stream()?;
socket.bind(path)?;
let listener = socket.listen(1)?;
let (stream, _) = listener.accept().await?;
Box::new(stream)
}
Some(("vsock", addr)) => {
let Some((cid, port)) = addr.split_once(':') else {
deno_core::anyhow::bail!("invalid vsock addr");
};
let cid = if cid == "-1" { u32::MAX } else { cid.parse()? };
let port = port.parse()?;
let addr = VsockAddr::new(cid, port);
let listener = VsockListener::bind(addr)?;
let (stream, _) = listener.accept().await?;
Box::new(stream)
}
_ => {
deno_core::anyhow::bail!("invalid control sock");
}
};
let mut stream = BufReader::new(stream);
let mut buf = Vec::with_capacity(1024);
stream.read_until(b'\n', &mut buf).await?;
#[derive(deno_core::serde::Deserialize)]
struct Start {
cwd: String,
args: Vec<String>,
env: Vec<(String, String)>,
}
let cmd: Start = deno_core::serde_json::from_slice(&buf)?;
std::env::set_current_dir(cmd.cwd)?;
for (k, v) in cmd.env {
std::env::set_var(k, v);
}
let args = [argv0]
.into_iter()
.chain(cmd.args.into_iter().map(Into::into))
.collect();
Ok(Some((unconfigured, args)))
})
}

View file

@ -984,6 +984,7 @@ pub async fn run(
StorageKeyResolver::empty(),
sys.clone(),
lib_main_worker_options,
Default::default(),
);
// Initialize v8 once from the main thread.

View file

@ -192,6 +192,7 @@ async fn bench_specifier_inner(
permissions_container,
vec![ops::bench::deno_bench::init(sender.clone())],
Default::default(),
None,
)
.await?;

View file

@ -344,6 +344,8 @@ async fn init_npm(name: &str, args: Vec<String>) -> Result<i32, AnyError> {
WorkerExecutionMode::Run,
new_flags.into(),
None,
None,
Default::default(),
)
.await
}

View file

@ -109,6 +109,7 @@ pub async fn kernel(
stdout: StdioPipe::file(stdout),
stderr: StdioPipe::file(stderr),
},
None,
)
.await?;
worker.setup_repl().await?;

View file

@ -166,6 +166,7 @@ async fn create_plugin_runner_inner(
permissions,
vec![crate::ops::lint::deno_lint_ext::init(logger.clone())],
Default::default(),
None,
)
.await?;

View file

@ -192,6 +192,7 @@ pub async fn run(
permissions.clone(),
vec![crate::ops::testing::deno_test::init(test_event_sender)],
Default::default(),
None,
)
.await?;
worker.setup_repl().await?;

View file

@ -10,6 +10,7 @@ use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::resolve_url_or_path;
use deno_lib::standalone::binary::SerializedWorkspaceResolverImportMap;
use deno_lib::worker::LibWorkerFactoryRoots;
use deno_runtime::WorkerExecutionMode;
use eszip::EszipV2;
use jsonc_parser::ParseOptions;
@ -52,6 +53,8 @@ pub async fn run_script(
mode: WorkerExecutionMode,
flags: Arc<Flags>,
watch: Option<WatchFlagsWithPaths>,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
roots: LibWorkerFactoryRoots,
) -> Result<i32, AnyError> {
check_permission_before_script(&flags);
@ -82,16 +85,26 @@ pub async fn run_script(
maybe_npm_install(&factory).await?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let worker_factory = factory
.create_cli_main_worker_factory_with_roots(roots)
.await?;
let mut worker = worker_factory
.create_main_worker(mode, main_module.clone())
.create_main_worker_with_unconfigured_runtime(
mode,
main_module.clone(),
unconfigured_runtime,
)
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
pub async fn run_from_stdin(
flags: Arc<Flags>,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
roots: LibWorkerFactoryRoots,
) -> Result<i32, AnyError> {
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?;
@ -99,7 +112,9 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
maybe_npm_install(&factory).await?;
let file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let worker_factory = factory
.create_cli_main_worker_factory_with_roots(roots)
.await?;
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
// Save a fake file into file fetcher cache
@ -111,7 +126,11 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
});
let mut worker = worker_factory
.create_main_worker(WorkerExecutionMode::Run, main_module.clone())
.create_main_worker_with_unconfigured_runtime(
WorkerExecutionMode::Run,
main_module.clone(),
unconfigured_runtime,
)
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
@ -232,6 +251,8 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
pub async fn run_eszip(
flags: Arc<Flags>,
run_flags: RunFlags,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
roots: LibWorkerFactoryRoots,
) -> Result<i32, AnyError> {
// TODO(bartlomieju): actually I think it will also fail if there's an import
// map specified and bare specifier is used on the command line
@ -246,9 +267,15 @@ pub async fn run_eszip(
let mode = WorkerExecutionMode::Run;
let main_module = resolve_url_or_path(entrypoint, cli_options.initial_cwd())?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let worker_factory = factory
.create_cli_main_worker_factory_with_roots(roots)
.await?;
let mut worker = worker_factory
.create_main_worker(mode, main_module.clone())
.create_main_worker_with_unconfigured_runtime(
mode,
main_module.clone(),
unconfigured_runtime,
)
.await?;
let exit_code = worker.run().await?;

View file

@ -640,6 +640,7 @@ async fn configure_main_worker(
stdout: StdioPipe::file(worker_sender.stdout),
stderr: StdioPipe::file(worker_sender.stderr),
},
None,
)
.await?;
let coverage_collector = worker.maybe_setup_coverage_collector().await?;

View file

@ -365,6 +365,25 @@ impl CliMainWorkerFactory {
self.root_permissions.clone(),
vec![],
Default::default(),
None,
)
.await
}
pub async fn create_main_worker_with_unconfigured_runtime(
&self,
mode: WorkerExecutionMode,
main_module: ModuleSpecifier,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
) -> Result<CliMainWorker, CreateCustomWorkerError> {
self
.create_custom_worker(
mode,
main_module,
self.root_permissions.clone(),
vec![],
Default::default(),
unconfigured_runtime,
)
.await
}
@ -376,6 +395,7 @@ impl CliMainWorkerFactory {
permissions: PermissionsContainer,
custom_extensions: Vec<Extension>,
stdio: deno_runtime::deno_io::Stdio,
unconfigured_runtime: Option<deno_runtime::UnconfiguredRuntime>,
) -> Result<CliMainWorker, CreateCustomWorkerError> {
let main_module = if let Ok(package_ref) =
NpmPackageReqReference::from_specifier(&main_module)
@ -432,6 +452,7 @@ impl CliMainWorkerFactory {
permissions,
custom_extensions,
stdio,
unconfigured_runtime,
)?;
if self.needs_test_modules {

View file

@ -21,8 +21,8 @@ Object.defineProperty(globalThis, "console", {
});
```
Then from rust, provide `deno_console::deno_console::init_ops_and_esm()` in the
`extensions` field of your `RuntimeOptions`
Then from rust, provide `deno_console::deno_console::init()` in the `extensions`
field of your `RuntimeOptions`
## Provided ops

View file

@ -41,9 +41,8 @@ Object.defineProperty(globalThis, "SubtleCrypto", {
});
```
Then from rust, provide:
`deno_crypto::deno_crypto::init_ops_and_esm(Option<u64>)` in the `extensions`
field of your `RuntimeOptions`
Then from rust, provide: `deno_crypto::deno_crypto::init(Option<u64>)` in the
`extensions` field of your `RuntimeOptions`
Where the `Option<u64>` represents an optional seed for initialization.

View file

@ -57,8 +57,8 @@ Object.defineProperty(globalThis, "FormData", {
```
Then from rust, provide
`deno_fetch::deno_fetch::init_ops_and_esm<Permissions>(Default::default())` in
the `extensions` field of your `RuntimeOptions`
`deno_fetch::deno_fetch::init<Permissions>(Default::default())` in the
`extensions` field of your `RuntimeOptions`
Where:

View file

@ -11,9 +11,8 @@ From javascript, include the extension's source:
import * as io from "ext:deno_io/12_io.js";
```
Then from rust, provide:
`deno_io::deno_io::init_ops_and_esm(Option<deno_io::Stdio>)` in the `extensions`
field of your `RuntimeOptions`
Then from rust, provide: `deno_io::deno_io::init(Option<deno_io::Stdio>)` in the
`extensions` field of your `RuntimeOptions`
Where `deno_io::Stdio` implements `Default`, and can therefore be provided as
`Some(deno_io::Stdio::default())`

View file

@ -13,7 +13,7 @@ import * as tls from "ext:deno_net/02_tls.js";
```
Then from rust, provide:
`deno_net::deno_net::init_ops_and_esm::<Permissions>(root_cert_store_provider, unsafely_ignore_certificate_errors)`
`deno_net::deno_net::init::<Permissions>(root_cert_store_provider, unsafely_ignore_certificate_errors)`
Where:

View file

@ -36,8 +36,8 @@ Object.defineProperty(globalThis, "URLSearchParams", {
});
```
Then from rust, provide `deno_url::deno_url::init_ops_and_esm()` in the
`extensions` field of your `RuntimeOptions`
Then from rust, provide `deno_url::deno_url::init()` in the `extensions` field
of your `RuntimeOptions`
## Dependencies

View file

@ -98,8 +98,8 @@ Object.defineProperty(globalThis, "AbortController", {
| structuredClone | messagePort.structuredClone | true | true | true |
Then from rust, provide:
`deno_web::deno_web::init_ops_and_esm::<Permissions>(Arc<BlobStore>, Option<Url>)`
in the `extensions` field of your `RuntimeOptions`
`deno_web::deno_web::init::<Permissions>(Arc<BlobStore>, Option<Url>)` in the
`extensions` field of your `RuntimeOptions`
Where:

View file

@ -20,5 +20,5 @@ Object.defineProperty(globalThis, webidl.brand, {
});
```
Then from rust, provide `init_webidl::init_webidl::init_ops_and_esm()` in the
`extensions` field of your `RuntimeOptions`
Then from rust, provide `init_webidl::init_webidl::init()` in the `extensions`
field of your `RuntimeOptions`

View file

@ -8,6 +8,7 @@ import { core, internals, primordials } from "ext:core/mod.js";
const ops = core.ops;
import {
op_bootstrap_args,
op_bootstrap_is_from_unconfigured_runtime,
op_bootstrap_no_color,
op_bootstrap_pid,
op_bootstrap_stderr_no_color,
@ -116,6 +117,8 @@ ObjectDefineProperties(Symbol, {
},
});
internals.isFromUnconfiguredRuntime = op_bootstrap_is_from_unconfigured_runtime;
// https://docs.rs/log/latest/log/enum.Level.html
const LOG_LEVELS = {
error: 1,

View file

@ -46,6 +46,7 @@ pub mod web_worker;
pub mod worker;
mod worker_bootstrap;
pub use worker::UnconfiguredRuntime;
pub use worker_bootstrap::BootstrapOptions;
pub use worker_bootstrap::WorkerExecutionMode;
pub use worker_bootstrap::WorkerLogLevel;

View file

@ -21,15 +21,18 @@ deno_core::extension!(
op_bootstrap_stdout_no_color,
op_bootstrap_stderr_no_color,
op_bootstrap_unstable_args,
op_bootstrap_is_from_unconfigured_runtime,
op_snapshot_options,
],
options = {
snapshot_options: Option<SnapshotOptions>,
is_from_unconfigured_runtime: bool,
},
state = |state, options| {
if let Some(snapshot_options) = options.snapshot_options {
state.put::<SnapshotOptions>(snapshot_options);
}
state.put(IsFromUnconfiguredRuntime(options.is_from_unconfigured_runtime));
},
);
@ -41,6 +44,8 @@ pub struct SnapshotOptions {
pub target: String,
}
struct IsFromUnconfiguredRuntime(bool);
impl Default for SnapshotOptions {
fn default() -> Self {
let arch = std::env::consts::ARCH;
@ -151,3 +156,8 @@ pub fn op_bootstrap_stderr_no_color(_state: &mut OpState) -> bool {
!deno_terminal::is_stderr_tty() || !deno_terminal::colors::use_color()
}
#[op2(fast)]
pub fn op_bootstrap_is_from_unconfigured_runtime(state: &mut OpState) -> bool {
state.borrow::<IsFromUnconfiguredRuntime>().0
}

View file

@ -3,7 +3,6 @@
use std::io::Write;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use deno_core::snapshot::*;
use deno_core::v8;
@ -25,62 +24,49 @@ pub fn create_runtime_snapshot(
// NOTE(bartlomieju): ordering is important here, keep it in sync with
// `runtime/worker.rs`, `runtime/web_worker.rs`, `runtime/snapshot_info.rs`
// and `runtime/snapshot.rs`!
let fs = std::sync::Arc::new(deno_fs::RealFs);
let mut extensions: Vec<Extension> = vec![
deno_telemetry::deno_telemetry::init(),
deno_webidl::deno_webidl::init(),
deno_console::deno_console::init(),
deno_url::deno_url::init(),
deno_web::deno_web::init::<Permissions>(
Default::default(),
Default::default(),
),
deno_webgpu::deno_webgpu::init(),
deno_canvas::deno_canvas::init(),
deno_fetch::deno_fetch::init::<Permissions>(Default::default()),
deno_cache::deno_cache::init(None),
deno_websocket::deno_websocket::init::<Permissions>(
"".to_owned(),
None,
None,
),
deno_webstorage::deno_webstorage::init(None),
deno_crypto::deno_crypto::init(None),
deno_broadcast_channel::deno_broadcast_channel::init(
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
),
deno_ffi::deno_ffi::init::<Permissions>(None),
deno_net::deno_net::init::<Permissions>(None, None),
deno_tls::deno_tls::init(),
deno_kv::deno_kv::init(
deno_kv::sqlite::SqliteDbHandler::<Permissions>::new(None, None),
deno_kv::KvConfig::builder().build(),
deno_telemetry::deno_telemetry::lazy_init(),
deno_webidl::deno_webidl::lazy_init(),
deno_console::deno_console::lazy_init(),
deno_url::deno_url::lazy_init(),
deno_web::deno_web::lazy_init::<Permissions>(),
deno_webgpu::deno_webgpu::lazy_init(),
deno_canvas::deno_canvas::lazy_init(),
deno_fetch::deno_fetch::lazy_init::<Permissions>(),
deno_cache::deno_cache::lazy_init(),
deno_websocket::deno_websocket::lazy_init::<Permissions>(),
deno_webstorage::deno_webstorage::lazy_init(),
deno_crypto::deno_crypto::lazy_init(),
deno_broadcast_channel::deno_broadcast_channel::lazy_init::<
deno_broadcast_channel::InMemoryBroadcastChannel,
>(),
deno_ffi::deno_ffi::lazy_init::<Permissions>(),
deno_net::deno_net::lazy_init::<Permissions>(),
deno_tls::deno_tls::lazy_init(),
deno_kv::deno_kv::lazy_init::<deno_kv::sqlite::SqliteDbHandler<Permissions>>(
),
deno_cron::deno_cron::init(deno_cron::local::LocalCronHandler::new()),
deno_napi::deno_napi::init::<Permissions>(None),
deno_http::deno_http::init(deno_http::Options::default()),
deno_io::deno_io::init(Default::default()),
deno_fs::deno_fs::init::<Permissions>(fs.clone()),
deno_os::deno_os::init(Default::default()),
deno_process::deno_process::init(Default::default()),
deno_node::deno_node::init::<
deno_napi::deno_napi::lazy_init::<Permissions>(),
deno_http::deno_http::lazy_init(),
deno_io::deno_io::lazy_init(),
deno_fs::deno_fs::lazy_init::<Permissions>(),
deno_os::deno_os::lazy_init(),
deno_process::deno_process::lazy_init(),
deno_node::deno_node::lazy_init::<
Permissions,
DenoInNpmPackageChecker,
NpmResolver<sys_traits::impls::RealSys>,
sys_traits::impls::RealSys,
>(None, fs.clone()),
ops::runtime::deno_runtime::init("deno:runtime".parse().unwrap()),
ops::worker_host::deno_worker_host::init(
Arc::new(|_| unreachable!("not used in snapshot.")),
None,
),
ops::fs_events::deno_fs_events::init(),
ops::permissions::deno_permissions::init(),
ops::tty::deno_tty::init(),
ops::http::deno_http_runtime::init(),
ops::bootstrap::deno_bootstrap::init(Some(snapshot_options)),
runtime::init(),
ops::web_worker::deno_web_worker::init(),
>(),
ops::runtime::deno_runtime::lazy_init(),
ops::worker_host::deno_worker_host::lazy_init(),
ops::fs_events::deno_fs_events::lazy_init(),
ops::permissions::deno_permissions::lazy_init(),
ops::tty::deno_tty::lazy_init(),
ops::http::deno_http_runtime::lazy_init(),
ops::bootstrap::deno_bootstrap::init(Some(snapshot_options), false),
runtime::lazy_init(),
ops::web_worker::deno_web_worker::lazy_init(),
];
extensions.extend(custom_extensions);

View file

@ -321,7 +321,7 @@ pub fn get_extensions_in_snapshot() -> Vec<Extension> {
ops::permissions::deno_permissions::init(),
ops::tty::deno_tty::init(),
ops::http::deno_http_runtime::init(),
ops::bootstrap::deno_bootstrap::init(None),
ops::bootstrap::deno_bootstrap::init(None, false),
runtime::init(),
ops::web_worker::deno_web_worker::init(),
]

View file

@ -568,11 +568,8 @@ impl WebWorker {
ops::tty::deno_tty::init(),
ops::http::deno_http_runtime::init(),
ops::bootstrap::deno_bootstrap::init(
if options.startup_snapshot.is_some() {
None
} else {
Some(Default::default())
},
options.startup_snapshot.and_then(|_| Default::default()),
false,
),
runtime::init(),
ops::web_worker::deno_web_worker::init(),

View file

@ -96,6 +96,17 @@ pub fn validate_import_attributes_callback(
}
}
pub fn make_wait_for_inspector_disconnect_callback() -> Box<dyn Fn()> {
let has_notified_of_inspector_disconnect = AtomicBool::new(false);
Box::new(move || {
if !has_notified_of_inspector_disconnect
.swap(true, std::sync::atomic::Ordering::SeqCst)
{
log::info!("Program finished. Waiting for inspector to disconnect to exit the process...");
}
})
}
/// This worker is created and used by almost all
/// subcommands in Deno executable.
///
@ -204,6 +215,8 @@ pub struct WorkerOptions {
pub origin_storage_dir: Option<std::path::PathBuf>,
pub stdio: Stdio,
pub enable_stack_trace_arg_in_ops: bool,
pub unconfigured_runtime: Option<UnconfiguredRuntime>,
}
impl Default for WorkerOptions {
@ -228,6 +241,7 @@ impl Default for WorkerOptions {
bootstrap: Default::default(),
stdio: Default::default(),
enable_stack_trace_arg_in_ops: false,
unconfigured_runtime: None,
}
}
}
@ -375,112 +389,13 @@ impl MainWorker {
let enable_testing_features = options.bootstrap.enable_testing_features;
let exit_code = ExitCode::default();
// NOTE(bartlomieju): ordering is important here, keep it in sync with
// `runtime/worker.rs`, `runtime/web_worker.rs`, `runtime/snapshot_info.rs`
// and `runtime/snapshot.rs`!
let mut extensions = vec![
deno_telemetry::deno_telemetry::init(),
// Web APIs
deno_webidl::deno_webidl::init(),
deno_console::deno_console::init(),
deno_url::deno_url::init(),
deno_web::deno_web::init::<PermissionsContainer>(
services.blob_store.clone(),
options.bootstrap.location.clone(),
),
deno_webgpu::deno_webgpu::init(),
deno_canvas::deno_canvas::init(),
deno_fetch::deno_fetch::init::<PermissionsContainer>(
deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(),
root_cert_store_provider: services.root_cert_store_provider.clone(),
unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors
.clone(),
file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler),
resolver: services.fetch_dns_resolver,
..Default::default()
},
),
deno_cache::deno_cache::init(create_cache),
deno_websocket::deno_websocket::init::<PermissionsContainer>(
options.bootstrap.user_agent.clone(),
services.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(),
),
deno_webstorage::deno_webstorage::init(
options.origin_storage_dir.clone(),
),
deno_crypto::deno_crypto::init(options.seed),
deno_broadcast_channel::deno_broadcast_channel::init(
services.broadcast_channel.clone(),
),
deno_ffi::deno_ffi::init::<PermissionsContainer>(
services.deno_rt_native_addon_loader.clone(),
),
deno_net::deno_net::init::<PermissionsContainer>(
services.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(),
),
deno_tls::deno_tls::init(),
deno_kv::deno_kv::init(
MultiBackendDbHandler::remote_or_sqlite::<PermissionsContainer>(
options.origin_storage_dir.clone(),
options.seed,
deno_kv::remote::HttpOptions {
user_agent: options.bootstrap.user_agent.clone(),
root_cert_store_provider: services.root_cert_store_provider.clone(),
unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors
.clone(),
client_cert_chain_and_key: TlsKeys::Null,
proxy: None,
},
),
deno_kv::KvConfig::builder().build(),
),
deno_cron::deno_cron::init(LocalCronHandler::new()),
deno_napi::deno_napi::init::<PermissionsContainer>(
services.deno_rt_native_addon_loader.clone(),
),
deno_http::deno_http::init(deno_http::Options {
no_legacy_abort: options.bootstrap.no_legacy_abort,
..Default::default()
}),
deno_io::deno_io::init(Some(options.stdio)),
deno_fs::deno_fs::init::<PermissionsContainer>(services.fs.clone()),
deno_os::deno_os::init(Some(exit_code.clone())),
deno_process::deno_process::init(services.npm_process_state_provider),
deno_node::deno_node::init::<
PermissionsContainer,
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TExtNodeSys,
>(services.node_services, services.fs),
// Ops from this crate
ops::runtime::deno_runtime::init(main_module.clone()),
ops::worker_host::deno_worker_host::init(
options.create_web_worker_cb.clone(),
options.format_js_error_fn.clone(),
),
ops::fs_events::deno_fs_events::init(),
ops::permissions::deno_permissions::init(),
ops::tty::deno_tty::init(),
ops::http::deno_http_runtime::init(),
ops::bootstrap::deno_bootstrap::init(
if options.startup_snapshot.is_some() {
None
} else {
Some(Default::default())
},
),
runtime::init(),
// NOTE(bartlomieju): this is done, just so that ops from this extension
// are available and importing them in `99_main.js` doesn't cause an
// error because they're not defined. Trying to use these ops in non-worker
// context will cause a panic.
ops::web_worker::deno_web_worker::init().disable(),
];
// check options that require configuring a new jsruntime
if options.unconfigured_runtime.is_some()
&& (options.enable_stack_trace_arg_in_ops
|| op_metrics_factory_fn.is_some())
{
options.unconfigured_runtime = None;
}
#[cfg(feature = "hmr")]
assert!(
@ -488,56 +403,35 @@ impl MainWorker {
"'hmr' is incompatible with 'only_snapshotted_js_sources'."
);
for extension in &mut extensions {
if options.startup_snapshot.is_some() {
extension.js_files = std::borrow::Cow::Borrowed(&[]);
extension.esm_files = std::borrow::Cow::Borrowed(&[]);
extension.esm_entry_point = None;
}
}
extensions.extend(std::mem::take(&mut options.extensions));
#[cfg(feature = "only_snapshotted_js_sources")]
options.startup_snapshot.as_ref().expect("A user snapshot was not provided, even though 'only_snapshotted_js_sources' is used.");
let has_notified_of_inspector_disconnect = AtomicBool::new(false);
let wait_for_inspector_disconnect_callback = Box::new(move || {
if !has_notified_of_inspector_disconnect
.swap(true, std::sync::atomic::Ordering::SeqCst)
{
log::info!("Program finished. Waiting for inspector to disconnect to exit the process...");
}
});
let mut js_runtime = if let Some(u) = options.unconfigured_runtime {
u.hydrate(services.module_loader)
} else {
let mut extensions = common_extensions::<
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TExtNodeSys,
>(options.startup_snapshot.is_some(), false);
let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(services.module_loader.clone()),
startup_snapshot: options.startup_snapshot,
create_params: options.create_params,
skip_op_registration: options.skip_op_registration,
shared_array_buffer_store: services.shared_array_buffer_store.clone(),
compiled_wasm_module_store: services.compiled_wasm_module_store.clone(),
extensions,
#[cfg(feature = "transpile")]
extension_transpiler: Some(Rc::new(|specifier, source| {
crate::transpile::maybe_transpile_source(specifier, source)
})),
#[cfg(not(feature = "transpile"))]
extension_transpiler: None,
inspector: true,
is_main: true,
op_metrics_factory_fn,
wait_for_inspector_disconnect_callback: Some(
wait_for_inspector_disconnect_callback,
),
import_meta_resolve_callback: Some(Box::new(
import_meta_resolve_callback,
)),
validate_import_attributes_cb: Some(Box::new(
validate_import_attributes_callback,
)),
import_assertions_support: deno_core::ImportAssertionsSupport::Error,
eval_context_code_cache_cbs: services.v8_code_cache.map(|cache| {
extensions.extend(std::mem::take(&mut options.extensions));
common_runtime(
services.module_loader.clone(),
options.startup_snapshot,
options.create_params,
options.skip_op_registration,
services.shared_array_buffer_store,
services.compiled_wasm_module_store,
extensions,
op_metrics_factory_fn,
options.enable_stack_trace_arg_in_ops,
)
};
js_runtime.set_eval_context_code_cache_cbs(services.v8_code_cache.map(
|cache| {
let cache_clone = cache.clone();
(
Box::new(move |specifier: &ModuleSpecifier, code: &v8::String| {
@ -549,12 +443,14 @@ impl MainWorker {
hasher.finish()
};
let data = cache
.get_sync(specifier, CodeCacheType::Script, source_hash)
.inspect(|_| {
// This log line is also used by tests.
log::debug!("V8 code cache hit for script: {specifier}, [{source_hash}]");
})
.map(Cow::Owned);
.get_sync(specifier, CodeCacheType::Script, source_hash)
.inspect(|_| {
// This log line is also used by tests.
log::debug!(
"V8 code cache hit for script: {specifier}, [{source_hash}]"
);
})
.map(Cow::Owned);
Ok(SourceCodeCacheInfo {
data,
hash: source_hash,
@ -563,7 +459,9 @@ impl MainWorker {
Box::new(
move |specifier: ModuleSpecifier, source_hash: u64, data: &[u8]| {
// This log line is also used by tests.
log::debug!("Updating V8 code cache for script: {specifier}, [{source_hash}]");
log::debug!(
"Updating V8 code cache for script: {specifier}, [{source_hash}]"
);
cache_clone.set_sync(
specifier,
CodeCacheType::Script,
@ -573,14 +471,89 @@ impl MainWorker {
},
) as Box<dyn Fn(_, _, &_)>,
)
}),
maybe_op_stack_trace_callback: if options.enable_stack_trace_arg_in_ops {
Some(Box::new(|stack| {
deno_permissions::prompter::set_current_stacktrace(stack)
}))
} else { None },
..Default::default()
});
},
));
js_runtime
.lazy_init_extensions(vec![
deno_web::deno_web::args::<PermissionsContainer>(
services.blob_store.clone(),
options.bootstrap.location.clone(),
),
deno_fetch::deno_fetch::args::<PermissionsContainer>(
deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(),
root_cert_store_provider: services.root_cert_store_provider.clone(),
unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors
.clone(),
file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler),
resolver: services.fetch_dns_resolver,
..Default::default()
},
),
deno_cache::deno_cache::args(create_cache),
deno_websocket::deno_websocket::args::<PermissionsContainer>(
options.bootstrap.user_agent.clone(),
services.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(),
),
deno_webstorage::deno_webstorage::args(
options.origin_storage_dir.clone(),
),
deno_crypto::deno_crypto::args(options.seed),
deno_broadcast_channel::deno_broadcast_channel::args(
services.broadcast_channel.clone(),
),
deno_ffi::deno_ffi::args::<PermissionsContainer>(
services.deno_rt_native_addon_loader.clone(),
),
deno_net::deno_net::args::<PermissionsContainer>(
services.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(),
),
deno_kv::deno_kv::args(
MultiBackendDbHandler::remote_or_sqlite::<PermissionsContainer>(
options.origin_storage_dir.clone(),
options.seed,
deno_kv::remote::HttpOptions {
user_agent: options.bootstrap.user_agent.clone(),
root_cert_store_provider: services
.root_cert_store_provider
.clone(),
unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors
.clone(),
client_cert_chain_and_key: TlsKeys::Null,
proxy: None,
},
),
deno_kv::KvConfig::builder().build(),
),
deno_napi::deno_napi::args::<PermissionsContainer>(
services.deno_rt_native_addon_loader.clone(),
),
deno_http::deno_http::args(deno_http::Options {
no_legacy_abort: options.bootstrap.no_legacy_abort,
..Default::default()
}),
deno_io::deno_io::args(Some(options.stdio)),
deno_fs::deno_fs::args::<PermissionsContainer>(services.fs.clone()),
deno_os::deno_os::args(Some(exit_code.clone())),
deno_process::deno_process::args(services.npm_process_state_provider),
deno_node::deno_node::args::<
PermissionsContainer,
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TExtNodeSys,
>(services.node_services, services.fs.clone()),
ops::runtime::deno_runtime::args(main_module.clone()),
ops::worker_host::deno_worker_host::args(
options.create_web_worker_cb.clone(),
options.format_js_error_fn.clone(),
),
])
.unwrap();
if let Some(op_summary_metrics) = op_summary_metrics {
js_runtime.op_state().borrow_mut().put(op_summary_metrics);
@ -958,3 +931,265 @@ impl MainWorker {
Ok(ret_val.is_true())
}
}
fn common_extensions<
TInNpmPackageChecker: InNpmPackageChecker + 'static,
TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
TExtNodeSys: ExtNodeSys + 'static,
>(
has_snapshot: bool,
unconfigured_runtime: bool,
) -> Vec<Extension> {
// NOTE(bartlomieju): ordering is important here, keep it in sync with
// `runtime/worker.rs`, `runtime/web_worker.rs`, `runtime/snapshot_info.rs`
// and `runtime/snapshot.rs`!
vec![
deno_telemetry::deno_telemetry::init(),
// Web APIs
deno_webidl::deno_webidl::init(),
deno_console::deno_console::init(),
deno_url::deno_url::init(),
deno_web::deno_web::lazy_init::<PermissionsContainer>(),
deno_webgpu::deno_webgpu::init(),
deno_canvas::deno_canvas::init(),
deno_fetch::deno_fetch::lazy_init::<PermissionsContainer>(),
deno_cache::deno_cache::lazy_init(),
deno_websocket::deno_websocket::lazy_init::<PermissionsContainer>(),
deno_webstorage::deno_webstorage::lazy_init(),
deno_crypto::deno_crypto::lazy_init(),
deno_broadcast_channel::deno_broadcast_channel::lazy_init::<
InMemoryBroadcastChannel,
>(),
deno_ffi::deno_ffi::lazy_init::<PermissionsContainer>(),
deno_net::deno_net::lazy_init::<PermissionsContainer>(),
deno_tls::deno_tls::init(),
deno_kv::deno_kv::lazy_init::<MultiBackendDbHandler>(),
deno_cron::deno_cron::init(LocalCronHandler::new()),
deno_napi::deno_napi::lazy_init::<PermissionsContainer>(),
deno_http::deno_http::lazy_init(),
deno_io::deno_io::lazy_init(),
deno_fs::deno_fs::lazy_init::<PermissionsContainer>(),
deno_os::deno_os::lazy_init(),
deno_process::deno_process::lazy_init(),
deno_node::deno_node::lazy_init::<
PermissionsContainer,
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TExtNodeSys,
>(),
// Ops from this crate
ops::runtime::deno_runtime::lazy_init(),
ops::worker_host::deno_worker_host::lazy_init(),
ops::fs_events::deno_fs_events::init(),
ops::permissions::deno_permissions::init(),
ops::tty::deno_tty::init(),
ops::http::deno_http_runtime::init(),
ops::bootstrap::deno_bootstrap::init(
has_snapshot.then(Default::default),
unconfigured_runtime,
),
runtime::init(),
// NOTE(bartlomieju): this is done, just so that ops from this extension
// are available and importing them in `99_main.js` doesn't cause an
// error because they're not defined. Trying to use these ops in non-worker
// context will cause a panic.
ops::web_worker::deno_web_worker::init().disable(),
]
}
#[allow(clippy::too_many_arguments)]
fn common_runtime(
module_loader: Rc<dyn ModuleLoader>,
startup_snapshot: Option<&'static [u8]>,
create_params: Option<v8::CreateParams>,
skip_op_registration: bool,
shared_array_buffer_store: Option<SharedArrayBufferStore>,
compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
extensions: Vec<Extension>,
op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
enable_stack_trace_arg_in_ops: bool,
) -> JsRuntime {
JsRuntime::new(RuntimeOptions {
module_loader: Some(module_loader),
startup_snapshot,
create_params,
skip_op_registration,
shared_array_buffer_store,
compiled_wasm_module_store,
extensions,
#[cfg(feature = "transpile")]
extension_transpiler: Some(Rc::new(|specifier, source| {
crate::transpile::maybe_transpile_source(specifier, source)
})),
#[cfg(not(feature = "transpile"))]
extension_transpiler: None,
inspector: true,
is_main: true,
op_metrics_factory_fn,
wait_for_inspector_disconnect_callback: Some(
make_wait_for_inspector_disconnect_callback(),
),
import_meta_resolve_callback: Some(Box::new(import_meta_resolve_callback)),
validate_import_attributes_cb: Some(Box::new(
validate_import_attributes_callback,
)),
import_assertions_support: deno_core::ImportAssertionsSupport::Error,
maybe_op_stack_trace_callback: enable_stack_trace_arg_in_ops.then(|| {
Box::new(|stack| {
deno_permissions::prompter::set_current_stacktrace(stack)
}) as _
}),
..Default::default()
})
}
pub struct UnconfiguredRuntime {
module_loader: Rc<PlaceholderModuleLoader>,
js_runtime: JsRuntime,
}
impl UnconfiguredRuntime {
pub fn new<
TInNpmPackageChecker: InNpmPackageChecker + 'static,
TNpmPackageFolderResolver: NpmPackageFolderResolver + 'static,
TExtNodeSys: ExtNodeSys + 'static,
>(
startup_snapshot: &'static [u8],
create_params: Option<v8::CreateParams>,
shared_array_buffer_store: Option<SharedArrayBufferStore>,
compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
additional_extensions: Vec<Extension>,
) -> Self {
let mut extensions = common_extensions::<
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TExtNodeSys,
>(true, true);
extensions.extend(additional_extensions);
let module_loader =
Rc::new(PlaceholderModuleLoader(std::cell::RefCell::new(None)));
let js_runtime = common_runtime(
module_loader.clone(),
Some(startup_snapshot),
create_params,
true,
shared_array_buffer_store,
compiled_wasm_module_store,
extensions,
None,
false,
);
UnconfiguredRuntime {
module_loader,
js_runtime,
}
}
fn hydrate(self, module_loader: Rc<dyn ModuleLoader>) -> JsRuntime {
let _ = self.module_loader.0.borrow_mut().insert(module_loader);
self.js_runtime
}
}
struct PlaceholderModuleLoader(
std::cell::RefCell<Option<Rc<dyn ModuleLoader>>>,
);
impl ModuleLoader for PlaceholderModuleLoader {
fn resolve(
&self,
specifier: &str,
referrer: &str,
kind: deno_core::ResolutionKind,
) -> Result<ModuleSpecifier, deno_core::error::ModuleLoaderError> {
self
.0
.borrow_mut()
.clone()
.unwrap()
.resolve(specifier, referrer, kind)
}
fn load(
&self,
module_specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
is_dyn_import: bool,
requested_module_type: deno_core::RequestedModuleType,
) -> deno_core::ModuleLoadResponse {
self.0.borrow_mut().clone().unwrap().load(
module_specifier,
maybe_referrer,
is_dyn_import,
requested_module_type,
)
}
fn prepare_load(
&self,
module_specifier: &ModuleSpecifier,
maybe_referrer: Option<String>,
is_dyn_import: bool,
) -> std::pin::Pin<
Box<
dyn std::prelude::rust_2024::Future<
Output = Result<(), deno_core::error::ModuleLoaderError>,
>,
>,
> {
self.0.borrow_mut().clone().unwrap().prepare_load(
module_specifier,
maybe_referrer,
is_dyn_import,
)
}
fn finish_load(&self) {
self.0.borrow_mut().clone().unwrap().finish_load()
}
fn purge_and_prevent_code_cache(&self, module_specifier: &str) {
self
.0
.borrow_mut()
.clone()
.unwrap()
.purge_and_prevent_code_cache(module_specifier)
}
fn get_source_map(&self, file_name: &str) -> Option<Cow<[u8]>> {
let v = self.0.borrow_mut().clone().unwrap();
let v = v.get_source_map(file_name);
v.map(|c| Cow::from(c.into_owned()))
}
fn get_source_mapped_source_line(
&self,
file_name: &str,
line_number: usize,
) -> Option<String> {
self
.0
.borrow_mut()
.clone()
.unwrap()
.get_source_mapped_source_line(file_name, line_number)
}
fn get_host_defined_options<'s>(
&self,
scope: &mut v8::HandleScope<'s>,
name: &str,
) -> Option<v8::Local<'s, v8::Data>> {
self
.0
.borrow_mut()
.clone()
.unwrap()
.get_host_defined_options(scope, name)
}
}

View file

@ -0,0 +1,9 @@
{
"tests": {
"unconfigured": {
"args": "run -A main.ts",
"output": "main.out",
"if": "unix"
}
}
}

View file

@ -0,0 +1,3 @@
true
hello world
[Object: null prototype] { success: true, code: 0, signal: null }

View file

@ -0,0 +1,50 @@
const tempDirPath = await Deno.makeTempDir();
const sockPath = `${tempDirPath}/control.sock`;
const testPath = `${tempDirPath}/test.ts`;
const command = new Deno.Command(Deno.execPath(), {
env: {
DENO_UNSTABLE_CONTROL_SOCK: `unix:${sockPath}`,
},
});
const child = command.spawn();
let i = 0;
while (true) {
try {
await Deno.lstat(sockPath);
break;
} catch {}
i += 1;
if (i > 100) {
throw new Error(`${sockPath} did not exist`);
}
await new Promise((r) => setTimeout(r, 10));
}
const sock = await Deno.connect({
transport: "unix",
path: sockPath,
});
Deno.writeTextFile(
testPath,
`
console.log(Deno[Deno.internal].isFromUnconfiguredRuntime());
console.log(Deno.env.get('A'));
`,
);
const data = JSON.stringify({
cwd: tempDirPath,
args: ["run", "-A", "test.ts"],
env: [["A", "hello world"]],
});
await sock.write(new TextEncoder().encode(data + "\n"));
console.log(await child.status);