mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 13:00:36 -05:00
RecursiveLoad shouldn't own the Isolate (#2453)
This patch makes it so that RecursiveLoad doesn't own the Isolate, so Worker::execute_mod_async does not consume itself. Previously Worker implemented Loader, but now ThreadSafeState does. This is necessary preparation work for dynamic import (#1789) and import maps (#1921)
This commit is contained in:
parent
6fa4d2e759
commit
e152dae006
5 changed files with 251 additions and 249 deletions
14
cli/main.rs
14
cli/main.rs
|
@ -92,10 +92,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(ry) Move this to main.rs
|
||||
pub fn print_file_info(worker: &Worker, url: &str) {
|
||||
let maybe_out =
|
||||
worker::fetch_module_meta_data_and_maybe_compile(&worker.state, url, ".");
|
||||
state::fetch_module_meta_data_and_maybe_compile(&worker.state, url, ".");
|
||||
if let Err(err) = maybe_out {
|
||||
println!("{}", err);
|
||||
return;
|
||||
|
@ -126,7 +125,8 @@ pub fn print_file_info(worker: &Worker, url: &str) {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(deps) = worker.modules.deps(&out.module_name) {
|
||||
let modules = worker.modules.lock().unwrap();
|
||||
if let Some(deps) = modules.deps(&out.module_name) {
|
||||
println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name);
|
||||
if let Some(ref depsdeps) = deps.deps {
|
||||
for d in depsdeps {
|
||||
|
@ -193,7 +193,7 @@ fn fetch_or_info_command(
|
|||
|
||||
worker
|
||||
.execute_mod_async(&main_url, true)
|
||||
.and_then(move |worker| {
|
||||
.and_then(move |()| {
|
||||
if print_info {
|
||||
print_file_info(&worker, &main_module);
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ fn fetch_or_info_command(
|
|||
js_check(result);
|
||||
Ok(())
|
||||
})
|
||||
}).map_err(|(err, _worker)| print_err_and_exit(err))
|
||||
}).map_err(print_err_and_exit)
|
||||
});
|
||||
tokio_util::run(main_future);
|
||||
}
|
||||
|
@ -289,12 +289,12 @@ fn run_script(flags: DenoFlags, argv: Vec<String>) {
|
|||
|
||||
worker
|
||||
.execute_mod_async(&main_url, false)
|
||||
.and_then(move |worker| {
|
||||
.and_then(move |()| {
|
||||
worker.then(|result| {
|
||||
js_check(result);
|
||||
Ok(())
|
||||
})
|
||||
}).map_err(|(err, _worker)| print_err_and_exit(err))
|
||||
}).map_err(print_err_and_exit)
|
||||
});
|
||||
tokio_util::run(main_future);
|
||||
}
|
||||
|
|
|
@ -2067,7 +2067,7 @@ fn op_create_worker(
|
|||
// TODO(ry) Use execute_mod_async here.
|
||||
let result = worker.execute_mod(&specifier_url, false);
|
||||
match result {
|
||||
Ok(worker) => {
|
||||
Ok(()) => {
|
||||
let mut workers_tl = parent_state.workers.lock().unwrap();
|
||||
workers_tl.insert(rid, worker.shared());
|
||||
let builder = &mut FlatBufferBuilder::new();
|
||||
|
@ -2085,10 +2085,8 @@ fn op_create_worker(
|
|||
},
|
||||
))
|
||||
}
|
||||
Err((errors::RustOrJsError::Js(_), _worker)) => {
|
||||
Err(errors::worker_init_failed())
|
||||
}
|
||||
Err((errors::RustOrJsError::Rust(err), _worker)) => Err(err),
|
||||
Err(errors::RustOrJsError::Js(_)) => Err(errors::worker_init_failed()),
|
||||
Err(errors::RustOrJsError::Rust(err)) => Err(err),
|
||||
}
|
||||
}()))
|
||||
}
|
||||
|
|
85
cli/state.rs
85
cli/state.rs
|
@ -1,18 +1,27 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use crate::compiler::compile_async;
|
||||
use crate::compiler::ModuleMetaData;
|
||||
use crate::deno_dir;
|
||||
use crate::errors::DenoError;
|
||||
use crate::errors::DenoResult;
|
||||
use crate::flags;
|
||||
use crate::global_timer::GlobalTimer;
|
||||
use crate::msg;
|
||||
use crate::ops;
|
||||
use crate::permissions::DenoPermissions;
|
||||
use crate::progress::Progress;
|
||||
use crate::resources;
|
||||
use crate::resources::ResourceId;
|
||||
use crate::tokio_util;
|
||||
use crate::worker::resolve_module_spec;
|
||||
use crate::worker::Worker;
|
||||
use deno::Buf;
|
||||
use deno::Loader;
|
||||
use deno::Op;
|
||||
use deno::PinnedBuf;
|
||||
use futures::future::Either;
|
||||
use futures::future::Shared;
|
||||
use futures::Future;
|
||||
use std;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
@ -94,6 +103,82 @@ impl ThreadSafeState {
|
|||
}
|
||||
}
|
||||
|
||||
fn fetch_module_meta_data_and_maybe_compile_async(
|
||||
state: &ThreadSafeState,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> impl Future<Item = ModuleMetaData, Error = DenoError> {
|
||||
let state_ = state.clone();
|
||||
let specifier = specifier.to_string();
|
||||
let referrer = referrer.to_string();
|
||||
|
||||
let f =
|
||||
futures::future::result(ThreadSafeState::resolve(&specifier, &referrer));
|
||||
f.and_then(move |module_id| {
|
||||
let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
|
||||
let no_fetch = state_.flags.no_fetch;
|
||||
|
||||
state_
|
||||
.dir
|
||||
.fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch)
|
||||
.and_then(move |out| {
|
||||
if out.media_type == msg::MediaType::TypeScript
|
||||
&& !out.has_output_code_and_source_map()
|
||||
{
|
||||
debug!(">>>>> compile_sync START");
|
||||
Either::A(
|
||||
compile_async(state_.clone(), &specifier, &referrer, &out)
|
||||
.map_err(|e| {
|
||||
debug!("compiler error exiting!");
|
||||
eprintln!("\n{}", e.to_string());
|
||||
std::process::exit(1);
|
||||
}).and_then(move |out| {
|
||||
debug!(">>>>> compile_sync END");
|
||||
Ok(out)
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
Either::B(futures::future::ok(out))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fetch_module_meta_data_and_maybe_compile(
|
||||
state: &ThreadSafeState,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> Result<ModuleMetaData, DenoError> {
|
||||
tokio_util::block_on(fetch_module_meta_data_and_maybe_compile_async(
|
||||
state, specifier, referrer,
|
||||
))
|
||||
}
|
||||
|
||||
impl Loader for ThreadSafeState {
|
||||
type Error = DenoError;
|
||||
|
||||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error> {
|
||||
resolve_module_spec(specifier, referrer).map_err(DenoError::from)
|
||||
}
|
||||
|
||||
/// Given an absolute url, load its source code.
|
||||
fn load(&self, url: &str) -> Box<deno::SourceCodeInfoFuture<Self::Error>> {
|
||||
self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst);
|
||||
Box::new(
|
||||
fetch_module_meta_data_and_maybe_compile_async(self, url, ".")
|
||||
.map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
err
|
||||
}).map(|module_meta_data| deno::SourceCodeInfo {
|
||||
// Real module name, might be different from initial URL
|
||||
// due to redirections.
|
||||
code: module_meta_data.js_source(),
|
||||
module_name: module_meta_data.module_name,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadSafeState {
|
||||
pub fn new(
|
||||
flags: flags::DenoFlags,
|
||||
|
|
203
cli/worker.rs
203
cli/worker.rs
|
@ -1,28 +1,25 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use crate::compiler::compile_async;
|
||||
use crate::compiler::ModuleMetaData;
|
||||
use crate::errors::DenoError;
|
||||
use crate::errors::RustOrJsError;
|
||||
use crate::js_errors;
|
||||
use crate::msg;
|
||||
use crate::state::ThreadSafeState;
|
||||
use crate::tokio_util;
|
||||
use deno;
|
||||
use deno::Config;
|
||||
use deno::JSError;
|
||||
use deno::Loader;
|
||||
use deno::StartupData;
|
||||
use futures::future::Either;
|
||||
use futures::Async;
|
||||
use futures::Future;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use url::Url;
|
||||
|
||||
/// Wraps deno::Isolate to provide source maps, ops for the CLI, and
|
||||
/// high-level module loading
|
||||
#[derive(Clone)]
|
||||
pub struct Worker {
|
||||
inner: deno::Isolate,
|
||||
pub modules: deno::Modules,
|
||||
inner: Arc<Mutex<deno::Isolate>>,
|
||||
pub modules: Arc<Mutex<deno::Modules>>,
|
||||
pub state: ThreadSafeState,
|
||||
}
|
||||
|
||||
|
@ -38,8 +35,8 @@ impl Worker {
|
|||
state_.dispatch(control_buf, zero_copy_buf)
|
||||
});
|
||||
Self {
|
||||
inner: deno::Isolate::new(startup_data, config),
|
||||
modules: deno::Modules::new(),
|
||||
inner: Arc::new(Mutex::new(deno::Isolate::new(startup_data, config))),
|
||||
modules: Arc::new(Mutex::new(deno::Modules::new())),
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
@ -56,48 +53,55 @@ impl Worker {
|
|||
js_filename: &str,
|
||||
js_source: &str,
|
||||
) -> Result<(), JSError> {
|
||||
self.inner.execute(js_filename, js_source)
|
||||
let mut isolate = self.inner.lock().unwrap();
|
||||
isolate.execute(js_filename, js_source)
|
||||
}
|
||||
|
||||
/// Consumes worker. Executes the provided JavaScript module.
|
||||
pub fn execute_mod_async(
|
||||
self,
|
||||
&mut self,
|
||||
js_url: &Url,
|
||||
is_prefetch: bool,
|
||||
) -> impl Future<Item = Self, Error = (RustOrJsError, Self)> {
|
||||
let recursive_load = deno::RecursiveLoad::new(js_url.as_str(), self);
|
||||
recursive_load.and_then(
|
||||
move |(id, mut self_)| -> Result<Self, (deno::JSErrorOr<DenoError>, Self)> {
|
||||
self_.state.progress.done();
|
||||
) -> impl Future<Item = (), Error = RustOrJsError> {
|
||||
let worker = self.clone();
|
||||
let worker_ = worker.clone();
|
||||
let loader = self.state.clone();
|
||||
let isolate = self.inner.clone();
|
||||
let modules = self.modules.clone();
|
||||
let recursive_load =
|
||||
deno::RecursiveLoad::new(js_url.as_str(), loader, isolate, modules);
|
||||
recursive_load
|
||||
.and_then(move |id| -> Result<(), deno::JSErrorOr<DenoError>> {
|
||||
worker.state.progress.done();
|
||||
if is_prefetch {
|
||||
Ok(self_)
|
||||
Ok(())
|
||||
} else {
|
||||
let result = self_.inner.mod_evaluate(id);
|
||||
let mut isolate = worker.inner.lock().unwrap();
|
||||
let result = isolate.mod_evaluate(id);
|
||||
if let Err(err) = result {
|
||||
Err((deno::JSErrorOr::JSError(err), self_))
|
||||
Err(deno::JSErrorOr::JSError(err))
|
||||
} else {
|
||||
Ok(self_)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.map_err(|(err, self_)| {
|
||||
self_.state.progress.done();
|
||||
// Convert to RustOrJsError AND apply_source_map.
|
||||
let err = match err {
|
||||
deno::JSErrorOr::JSError(err) => RustOrJsError::Js(self_.apply_source_map(err)),
|
||||
deno::JSErrorOr::Other(err) => RustOrJsError::Rust(err),
|
||||
};
|
||||
(err, self_)
|
||||
})
|
||||
}).map_err(move |err| {
|
||||
worker_.state.progress.done();
|
||||
// Convert to RustOrJsError AND apply_source_map.
|
||||
match err {
|
||||
deno::JSErrorOr::JSError(err) => {
|
||||
RustOrJsError::Js(worker_.apply_source_map(err))
|
||||
}
|
||||
deno::JSErrorOr::Other(err) => RustOrJsError::Rust(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes worker. Executes the provided JavaScript module.
|
||||
pub fn execute_mod(
|
||||
self,
|
||||
&mut self,
|
||||
js_url: &Url,
|
||||
is_prefetch: bool,
|
||||
) -> Result<Self, (RustOrJsError, Self)> {
|
||||
) -> Result<(), RustOrJsError> {
|
||||
tokio_util::block_on(self.execute_mod_async(js_url, is_prefetch))
|
||||
}
|
||||
|
||||
|
@ -159,103 +163,16 @@ pub fn root_specifier_to_url(
|
|||
}
|
||||
}
|
||||
|
||||
impl Loader for Worker {
|
||||
type Error = DenoError;
|
||||
|
||||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error> {
|
||||
resolve_module_spec(specifier, referrer).map_err(DenoError::from)
|
||||
}
|
||||
|
||||
/// Given an absolute url, load its source code.
|
||||
fn load(
|
||||
&mut self,
|
||||
url: &str,
|
||||
) -> Box<deno::SourceCodeInfoFuture<Self::Error>> {
|
||||
self
|
||||
.state
|
||||
.metrics
|
||||
.resolve_count
|
||||
.fetch_add(1, Ordering::SeqCst);
|
||||
Box::new(
|
||||
fetch_module_meta_data_and_maybe_compile_async(&self.state, url, ".")
|
||||
.map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
err
|
||||
}).map(|module_meta_data| deno::SourceCodeInfo {
|
||||
// Real module name, might be different from initial URL
|
||||
// due to redirections.
|
||||
code: module_meta_data.js_source(),
|
||||
module_name: module_meta_data.module_name,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn isolate_and_modules<'a: 'b + 'c, 'b, 'c>(
|
||||
&'a mut self,
|
||||
) -> (&'b mut deno::Isolate, &'c mut deno::Modules) {
|
||||
(&mut self.inner, &mut self.modules)
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Worker {
|
||||
type Item = ();
|
||||
type Error = JSError;
|
||||
|
||||
fn poll(&mut self) -> Result<Async<()>, Self::Error> {
|
||||
self.inner.poll().map_err(|err| self.apply_source_map(err))
|
||||
let mut isolate = self.inner.lock().unwrap();
|
||||
isolate.poll().map_err(|err| self.apply_source_map(err))
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_module_meta_data_and_maybe_compile_async(
|
||||
state: &ThreadSafeState,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> impl Future<Item = ModuleMetaData, Error = DenoError> {
|
||||
let state_ = state.clone();
|
||||
let specifier = specifier.to_string();
|
||||
let referrer = referrer.to_string();
|
||||
|
||||
let f = futures::future::result(Worker::resolve(&specifier, &referrer));
|
||||
f.and_then(move |module_id| {
|
||||
let use_cache = !state_.flags.reload || state_.has_compiled(&module_id);
|
||||
let no_fetch = state_.flags.no_fetch;
|
||||
|
||||
state_
|
||||
.dir
|
||||
.fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch)
|
||||
.and_then(move |out| {
|
||||
if out.media_type == msg::MediaType::TypeScript
|
||||
&& !out.has_output_code_and_source_map()
|
||||
{
|
||||
debug!(">>>>> compile_sync START");
|
||||
Either::A(
|
||||
compile_async(state_.clone(), &specifier, &referrer, &out)
|
||||
.map_err(|e| {
|
||||
debug!("compiler error exiting!");
|
||||
eprintln!("\n{}", e.to_string());
|
||||
std::process::exit(1);
|
||||
}).and_then(move |out| {
|
||||
debug!(">>>>> compile_sync END");
|
||||
Ok(out)
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
Either::B(futures::future::ok(out))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fetch_module_meta_data_and_maybe_compile(
|
||||
state: &ThreadSafeState,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> Result<ModuleMetaData, DenoError> {
|
||||
tokio_util::block_on(fetch_module_meta_data_and_maybe_compile_async(
|
||||
state, specifier, referrer,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -286,15 +203,12 @@ mod tests {
|
|||
);
|
||||
let state_ = state.clone();
|
||||
tokio_util::run(lazy(move || {
|
||||
let worker = Worker::new("TEST".to_string(), StartupData::None, state);
|
||||
let mut worker =
|
||||
Worker::new("TEST".to_string(), StartupData::None, state);
|
||||
let result = worker.execute_mod(&js_url, false);
|
||||
let worker = match result {
|
||||
Err((err, worker)) => {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
worker
|
||||
}
|
||||
Ok(worker) => worker,
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
}
|
||||
tokio_util::panic_on_error(worker)
|
||||
}));
|
||||
|
||||
|
@ -318,15 +232,12 @@ mod tests {
|
|||
);
|
||||
let state_ = state.clone();
|
||||
tokio_util::run(lazy(move || {
|
||||
let worker = Worker::new("TEST".to_string(), StartupData::None, state);
|
||||
let mut worker =
|
||||
Worker::new("TEST".to_string(), StartupData::None, state);
|
||||
let result = worker.execute_mod(&js_url, false);
|
||||
let worker = match result {
|
||||
Err((err, worker)) => {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
worker
|
||||
}
|
||||
Ok(worker) => worker,
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
}
|
||||
tokio_util::panic_on_error(worker)
|
||||
}));
|
||||
|
||||
|
@ -356,13 +267,9 @@ mod tests {
|
|||
);
|
||||
js_check(worker.execute("denoMain()"));
|
||||
let result = worker.execute_mod(&js_url, false);
|
||||
let worker = match result {
|
||||
Err((err, worker)) => {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
worker
|
||||
}
|
||||
Ok(worker) => worker,
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("execute_mod err {:?}", err);
|
||||
}
|
||||
tokio_util::panic_on_error(worker)
|
||||
}));
|
||||
|
||||
|
@ -470,7 +377,7 @@ mod tests {
|
|||
fn execute_mod_resolve_error() {
|
||||
tokio_util::init(|| {
|
||||
// "foo" is not a vailid module specifier so this should return an error.
|
||||
let worker = create_test_worker();
|
||||
let mut worker = create_test_worker();
|
||||
let js_url = root_specifier_to_url("does-not-exist").unwrap();
|
||||
let result = worker.execute_mod_async(&js_url, false).wait();
|
||||
assert!(result.is_err());
|
||||
|
@ -482,7 +389,7 @@ mod tests {
|
|||
tokio_util::init(|| {
|
||||
// This assumes cwd is project root (an assumption made throughout the
|
||||
// tests).
|
||||
let worker = create_test_worker();
|
||||
let mut worker = create_test_worker();
|
||||
let js_url = root_specifier_to_url("./tests/002_hello.ts").unwrap();
|
||||
let result = worker.execute_mod_async(&js_url, false).wait();
|
||||
assert!(result.is_ok());
|
||||
|
|
190
core/modules.rs
190
core/modules.rs
|
@ -17,6 +17,8 @@ use std::collections::HashSet;
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Represent result of fetching the source code of a module.
|
||||
/// Contains both module name and code.
|
||||
|
@ -34,7 +36,7 @@ pub struct SourceCodeInfo {
|
|||
pub type SourceCodeInfoFuture<E> =
|
||||
dyn Future<Item = SourceCodeInfo, Error = E> + Send;
|
||||
|
||||
pub trait Loader {
|
||||
pub trait Loader: Send + Sync {
|
||||
type Error: std::error::Error + 'static;
|
||||
|
||||
/// Returns an absolute URL.
|
||||
|
@ -44,21 +46,7 @@ pub trait Loader {
|
|||
fn resolve(specifier: &str, referrer: &str) -> Result<String, Self::Error>;
|
||||
|
||||
/// Given an absolute url, load its source code.
|
||||
fn load(&mut self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>>;
|
||||
|
||||
fn isolate_and_modules<'a: 'b + 'c, 'b, 'c>(
|
||||
&'a mut self,
|
||||
) -> (&'b mut Isolate, &'c mut Modules);
|
||||
|
||||
fn isolate<'a: 'b, 'b>(&'a mut self) -> &'b mut Isolate {
|
||||
let (isolate, _) = self.isolate_and_modules();
|
||||
isolate
|
||||
}
|
||||
|
||||
fn modules<'a: 'b, 'b>(&'a mut self) -> &'b mut Modules {
|
||||
let (_, modules) = self.isolate_and_modules();
|
||||
modules
|
||||
}
|
||||
fn load(&self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>>;
|
||||
}
|
||||
|
||||
struct PendingLoad<E: Error> {
|
||||
|
@ -71,7 +59,9 @@ struct PendingLoad<E: Error> {
|
|||
/// complicating the Isolate API. Note that RecursiveLoad will take ownership of
|
||||
/// an Isolate during load.
|
||||
pub struct RecursiveLoad<L: Loader> {
|
||||
loader: Option<L>,
|
||||
loader: L,
|
||||
isolate: Arc<Mutex<Isolate>>,
|
||||
modules: Arc<Mutex<Modules>>,
|
||||
pending: Vec<PendingLoad<L::Error>>,
|
||||
is_pending: HashSet<String>,
|
||||
phantom: PhantomData<L>,
|
||||
|
@ -83,9 +73,16 @@ pub struct RecursiveLoad<L: Loader> {
|
|||
|
||||
impl<L: Loader> RecursiveLoad<L> {
|
||||
/// Starts a new parallel load of the given URL.
|
||||
pub fn new(url: &str, loader: L) -> Self {
|
||||
pub fn new(
|
||||
url: &str,
|
||||
loader: L,
|
||||
isolate: Arc<Mutex<Isolate>>,
|
||||
modules: Arc<Mutex<Modules>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
loader: Some(loader),
|
||||
loader,
|
||||
isolate,
|
||||
modules,
|
||||
root: None,
|
||||
root_specifier: Some(url.to_string()),
|
||||
root_id: None,
|
||||
|
@ -95,10 +92,6 @@ impl<L: Loader> RecursiveLoad<L> {
|
|||
}
|
||||
}
|
||||
|
||||
fn take_loader(&mut self) -> L {
|
||||
self.loader.take().unwrap()
|
||||
}
|
||||
|
||||
fn add(
|
||||
&mut self,
|
||||
specifier: &str,
|
||||
|
@ -108,20 +101,20 @@ impl<L: Loader> RecursiveLoad<L> {
|
|||
let url = L::resolve(specifier, referrer)?;
|
||||
|
||||
let is_root = if let Some(parent_id) = parent_id {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let modules = loader.modules();
|
||||
modules.add_child(parent_id, &url);
|
||||
{
|
||||
let mut m = self.modules.lock().unwrap();
|
||||
m.add_child(parent_id, &url);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
{
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let modules = loader.modules();
|
||||
// #B We only add modules that have not yet been resolved for RecursiveLoad.
|
||||
// Only short circuit after add_child().
|
||||
// This impacts possible conditions in #A.
|
||||
let modules = self.modules.lock().unwrap();
|
||||
if modules.is_registered(&url) {
|
||||
return Ok(url);
|
||||
}
|
||||
|
@ -129,10 +122,7 @@ impl<L: Loader> RecursiveLoad<L> {
|
|||
|
||||
if !self.is_pending.contains(&url) {
|
||||
self.is_pending.insert(url.clone());
|
||||
let source_code_info_future = {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
loader.load(&url)
|
||||
};
|
||||
let source_code_info_future = { self.loader.load(&url) };
|
||||
self.pending.push(PendingLoad {
|
||||
url: url.clone(),
|
||||
source_code_info_future,
|
||||
|
@ -153,15 +143,15 @@ pub enum JSErrorOr<E> {
|
|||
}
|
||||
|
||||
impl<L: Loader> Future for RecursiveLoad<L> {
|
||||
type Item = (deno_mod, L);
|
||||
type Error = (JSErrorOr<L::Error>, L);
|
||||
type Item = deno_mod;
|
||||
type Error = JSErrorOr<L::Error>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
if self.root.is_none() && self.root_specifier.is_some() {
|
||||
let s = self.root_specifier.take().unwrap();
|
||||
match self.add(&s, ".", None) {
|
||||
Err(err) => {
|
||||
return Err((JSErrorOr::Other(err), self.take_loader()));
|
||||
return Err(JSErrorOr::Other(err));
|
||||
}
|
||||
Ok(root) => {
|
||||
self.root = Some(root);
|
||||
|
@ -176,7 +166,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
let pending = &mut self.pending[i];
|
||||
match pending.source_code_info_future.poll() {
|
||||
Err(err) => {
|
||||
return Err((JSErrorOr::Other(err), self.take_loader()));
|
||||
return Err(JSErrorOr::Other(err));
|
||||
}
|
||||
Ok(Async::NotReady) => {
|
||||
i += 1;
|
||||
|
@ -195,8 +185,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
// 2b. The module with resolved module name has not yet been registerd
|
||||
// -> register & alias
|
||||
let is_module_registered = {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let modules = loader.modules();
|
||||
let modules = self.modules.lock().unwrap();
|
||||
modules.is_registered(&source_code_info.module_name)
|
||||
};
|
||||
|
||||
|
@ -206,8 +195,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
let module_name = &source_code_info.module_name;
|
||||
|
||||
let result = {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let isolate = loader.isolate();
|
||||
let isolate = self.isolate.lock().unwrap();
|
||||
isolate.mod_new(
|
||||
completed.is_root,
|
||||
module_name,
|
||||
|
@ -215,7 +203,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
)
|
||||
};
|
||||
if let Err(err) = result {
|
||||
return Err((JSErrorOr::JSError(err), self.take_loader()));
|
||||
return Err(JSErrorOr::JSError(err));
|
||||
}
|
||||
let mod_id = result.unwrap();
|
||||
if completed.is_root {
|
||||
|
@ -225,8 +213,7 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
|
||||
// Register new module.
|
||||
{
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let modules = loader.modules();
|
||||
let mut modules = self.modules.lock().unwrap();
|
||||
modules.register(mod_id, module_name);
|
||||
// If necessary, register the alias.
|
||||
if need_alias {
|
||||
|
@ -237,19 +224,17 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
|
||||
// Now we must iterate over all imports of the module and load them.
|
||||
let imports = {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let isolate = loader.isolate();
|
||||
let isolate = self.isolate.lock().unwrap();
|
||||
isolate.mod_get_imports(mod_id)
|
||||
};
|
||||
let referrer = module_name;
|
||||
for specifier in imports {
|
||||
self
|
||||
.add(&specifier, referrer, Some(mod_id))
|
||||
.map_err(|e| (JSErrorOr::Other(e), self.take_loader()))?;
|
||||
.map_err(JSErrorOr::Other)?;
|
||||
}
|
||||
} else if need_alias {
|
||||
let loader = self.loader.as_mut().unwrap();
|
||||
let modules = loader.modules();
|
||||
let mut modules = self.modules.lock().unwrap();
|
||||
modules.alias(&completed.url, &source_code_info.module_name);
|
||||
}
|
||||
}
|
||||
|
@ -261,11 +246,10 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
}
|
||||
|
||||
let root_id = self.root_id.unwrap();
|
||||
let mut loader = self.take_loader();
|
||||
let (isolate, modules) = loader.isolate_and_modules();
|
||||
let result = {
|
||||
let mut resolve_cb =
|
||||
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
||||
let modules = self.modules.lock().unwrap();
|
||||
let referrer = modules.get_name(referrer_id).unwrap();
|
||||
match L::resolve(specifier, &referrer) {
|
||||
Ok(url) => match modules.get_id(&url) {
|
||||
|
@ -278,12 +262,13 @@ impl<L: Loader> Future for RecursiveLoad<L> {
|
|||
}
|
||||
};
|
||||
|
||||
let mut isolate = self.isolate.lock().unwrap();
|
||||
isolate.mod_instantiate(root_id, &mut resolve_cb)
|
||||
};
|
||||
|
||||
match result {
|
||||
Err(err) => Err((JSErrorOr::JSError(err), loader)),
|
||||
Ok(()) => Ok(Async::Ready((root_id, loader))),
|
||||
Err(err) => Err(JSErrorOr::JSError(err)),
|
||||
Ok(()) => Ok(Async::Ready(root_id)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -548,9 +533,9 @@ mod tests {
|
|||
use std::fmt;
|
||||
|
||||
struct MockLoader {
|
||||
pub loads: Vec<String>,
|
||||
pub isolate: Isolate,
|
||||
pub modules: Modules,
|
||||
pub loads: Arc<Mutex<Vec<String>>>,
|
||||
pub isolate: Arc<Mutex<Isolate>>,
|
||||
pub modules: Arc<Mutex<Modules>>,
|
||||
}
|
||||
|
||||
impl MockLoader {
|
||||
|
@ -558,9 +543,9 @@ mod tests {
|
|||
let modules = Modules::new();
|
||||
let (isolate, _dispatch_count) = setup(Mode::AsyncImmediate);
|
||||
Self {
|
||||
loads: Vec::new(),
|
||||
isolate,
|
||||
modules,
|
||||
loads: Arc::new(Mutex::new(Vec::new())),
|
||||
isolate: Arc::new(Mutex::new(isolate)),
|
||||
modules: Arc::new(Mutex::new(modules)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -663,17 +648,12 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn load(&mut self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>> {
|
||||
self.loads.push(url.to_string());
|
||||
fn load(&self, url: &str) -> Box<SourceCodeInfoFuture<Self::Error>> {
|
||||
let mut loads = self.loads.lock().unwrap();
|
||||
loads.push(url.to_string());
|
||||
let url = url.to_string();
|
||||
Box::new(DelayedSourceCodeFuture { url, counter: 0 })
|
||||
}
|
||||
|
||||
fn isolate_and_modules<'a: 'b + 'c, 'b, 'c>(
|
||||
&'a mut self,
|
||||
) -> (&'b mut Isolate, &'c mut Modules) {
|
||||
(&mut self.isolate, &mut self.modules)
|
||||
}
|
||||
}
|
||||
|
||||
const A_SRC: &str = r#"
|
||||
|
@ -710,15 +690,24 @@ mod tests {
|
|||
#[test]
|
||||
fn test_recursive_load() {
|
||||
let loader = MockLoader::new();
|
||||
let mut recursive_load = RecursiveLoad::new("a.js", loader);
|
||||
let modules = loader.modules.clone();
|
||||
let modules_ = modules.clone();
|
||||
let isolate = loader.isolate.clone();
|
||||
let isolate_ = isolate.clone();
|
||||
let loads = loader.loads.clone();
|
||||
let mut recursive_load =
|
||||
RecursiveLoad::new("a.js", loader, isolate, modules);
|
||||
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_ok());
|
||||
if let Async::Ready((a_id, mut loader)) = result.ok().unwrap() {
|
||||
js_check(loader.isolate.mod_evaluate(a_id));
|
||||
assert_eq!(loader.loads, vec!["a.js", "b.js", "c.js", "d.js"]);
|
||||
if let Async::Ready(a_id) = result.ok().unwrap() {
|
||||
let mut isolate = isolate_.lock().unwrap();
|
||||
js_check(isolate.mod_evaluate(a_id));
|
||||
|
||||
let modules = &loader.modules;
|
||||
let l = loads.lock().unwrap();
|
||||
assert_eq!(l.to_vec(), vec!["a.js", "b.js", "c.js", "d.js"]);
|
||||
|
||||
let modules = modules_.lock().unwrap();
|
||||
|
||||
assert_eq!(modules.get_id("a.js"), Some(a_id));
|
||||
let b_id = modules.get_id("b.js").unwrap();
|
||||
|
@ -756,18 +745,27 @@ mod tests {
|
|||
#[test]
|
||||
fn test_circular_load() {
|
||||
let loader = MockLoader::new();
|
||||
let mut recursive_load = RecursiveLoad::new("circular1.js", loader);
|
||||
let isolate = loader.isolate.clone();
|
||||
let isolate_ = isolate.clone();
|
||||
let modules = loader.modules.clone();
|
||||
let modules_ = modules.clone();
|
||||
let loads = loader.loads.clone();
|
||||
let mut recursive_load =
|
||||
RecursiveLoad::new("circular1.js", loader, isolate, modules);
|
||||
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_ok());
|
||||
if let Async::Ready((circular1_id, mut loader)) = result.ok().unwrap() {
|
||||
js_check(loader.isolate.mod_evaluate(circular1_id));
|
||||
if let Async::Ready(circular1_id) = result.ok().unwrap() {
|
||||
let mut isolate = isolate_.lock().unwrap();
|
||||
js_check(isolate.mod_evaluate(circular1_id));
|
||||
|
||||
let l = loads.lock().unwrap();
|
||||
assert_eq!(
|
||||
loader.loads,
|
||||
l.to_vec(),
|
||||
vec!["circular1.js", "circular2.js", "circular3.js"]
|
||||
);
|
||||
|
||||
let modules = &loader.modules;
|
||||
let modules = modules_.lock().unwrap();
|
||||
|
||||
assert_eq!(modules.get_id("circular1.js"), Some(circular1_id));
|
||||
let circular2_id = modules.get_id("circular2.js").unwrap();
|
||||
|
@ -813,18 +811,26 @@ mod tests {
|
|||
#[test]
|
||||
fn test_redirect_load() {
|
||||
let loader = MockLoader::new();
|
||||
let mut recursive_load = RecursiveLoad::new("redirect1.js", loader);
|
||||
let isolate = loader.isolate.clone();
|
||||
let isolate_ = isolate.clone();
|
||||
let modules = loader.modules.clone();
|
||||
let modules_ = modules.clone();
|
||||
let loads = loader.loads.clone();
|
||||
let mut recursive_load =
|
||||
RecursiveLoad::new("redirect1.js", loader, isolate, modules);
|
||||
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_ok());
|
||||
if let Async::Ready((redirect1_id, mut loader)) = result.ok().unwrap() {
|
||||
js_check(loader.isolate.mod_evaluate(redirect1_id));
|
||||
if let Async::Ready(redirect1_id) = result.ok().unwrap() {
|
||||
let mut isolate = isolate_.lock().unwrap();
|
||||
js_check(isolate.mod_evaluate(redirect1_id));
|
||||
let l = loads.lock().unwrap();
|
||||
assert_eq!(
|
||||
loader.loads,
|
||||
l.to_vec(),
|
||||
vec!["redirect1.js", "./redirect2.js", "./dir/redirect3.js"]
|
||||
);
|
||||
|
||||
let modules = &loader.modules;
|
||||
let modules = modules_.lock().unwrap();
|
||||
|
||||
assert_eq!(modules.get_id("redirect1.js"), Some(redirect1_id));
|
||||
|
||||
|
@ -861,24 +867,28 @@ mod tests {
|
|||
#[test]
|
||||
fn slow_never_ready_modules() {
|
||||
let loader = MockLoader::new();
|
||||
let mut recursive_load = RecursiveLoad::new("main.js", loader);
|
||||
let isolate = loader.isolate.clone();
|
||||
let modules = loader.modules.clone();
|
||||
let loads = loader.loads.clone();
|
||||
let mut recursive_load =
|
||||
RecursiveLoad::new("main.js", loader, isolate, modules);
|
||||
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_ok());
|
||||
assert!(result.ok().unwrap().is_not_ready());
|
||||
|
||||
{
|
||||
let loader = recursive_load.loader.as_ref().unwrap();
|
||||
assert_eq!(loader.loads, vec!["main.js", "never_ready.js", "slow.js"]);
|
||||
let l = loads.lock().unwrap();
|
||||
assert_eq!(l.to_vec(), vec!["main.js", "never_ready.js", "slow.js"]);
|
||||
}
|
||||
|
||||
for _ in 0..10 {
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_ok());
|
||||
assert!(result.ok().unwrap().is_not_ready());
|
||||
let loader = recursive_load.loader.as_ref().unwrap();
|
||||
let l = loads.lock().unwrap();;
|
||||
assert_eq!(
|
||||
loader.loads,
|
||||
l.to_vec(),
|
||||
vec![
|
||||
"main.js",
|
||||
"never_ready.js",
|
||||
|
@ -900,12 +910,14 @@ mod tests {
|
|||
#[test]
|
||||
fn loader_disappears_after_error() {
|
||||
let loader = MockLoader::new();
|
||||
let mut recursive_load = RecursiveLoad::new("bad_import.js", loader);
|
||||
let isolate = loader.isolate.clone();
|
||||
let modules = loader.modules.clone();
|
||||
let mut recursive_load =
|
||||
RecursiveLoad::new("bad_import.js", loader, isolate, modules);
|
||||
let result = recursive_load.poll();
|
||||
assert!(result.is_err());
|
||||
let (either_err, _loader) = result.err().unwrap();
|
||||
let either_err = result.err().unwrap();
|
||||
assert_eq!(either_err, JSErrorOr::Other(MockError::ResolveErr));
|
||||
assert!(recursive_load.loader.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Reference in a new issue