summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2020-09-06 16:50:49 +0200
committerGitHub <noreply@github.com>2020-09-06 16:50:49 +0200
commit803bdd37c759ee859d89e7e957d2764d0db72af4 (patch)
tree0c055191e134803b893af818f97f8b38eba8142f
parentc821e8f2f1fb8ad5e9eb00854277cafc8c80b2f5 (diff)
refactor(core): merge CoreIsolate and EsIsolate (#7370)
-rw-r--r--cli/global_state.rs4
-rw-r--r--cli/main.rs4
-rw-r--r--cli/worker.rs6
-rw-r--r--core/bindings.rs7
-rw-r--r--core/core_isolate.rs905
-rw-r--r--core/es_isolate.rs983
-rw-r--r--core/lib.rs3
-rw-r--r--core/modules.rs85
8 files changed, 981 insertions, 1016 deletions
diff --git a/cli/global_state.rs b/cli/global_state.rs
index cb265d9e5..79618b741 100644
--- a/cli/global_state.rs
+++ b/cli/global_state.rs
@@ -96,7 +96,7 @@ impl GlobalState {
}
/// This function is called when new module load is
- /// initialized by the EsIsolate. Its resposibility is to collect
+ /// initialized by the CoreIsolate. Its resposibility is to collect
/// all dependencies and if it is required then also perform TS typecheck
/// and traspilation.
pub async fn prepare_module_load(
@@ -180,7 +180,7 @@ impl GlobalState {
}
// TODO(bartlomieju): this method doesn't need to be async anymore
- /// This method is used after `prepare_module_load` finishes and EsIsolate
+ /// This method is used after `prepare_module_load` finishes and CoreIsolate
/// starts loading source and executing source code. This method shouldn't
/// perform any IO (besides $DENO_DIR) and only operate on sources collected
/// during `prepare_module_load`.
diff --git a/cli/main.rs b/cli/main.rs
index 49efbfed5..2e2dc71a2 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -79,9 +79,9 @@ use crate::permissions::Permissions;
use crate::tsc::TargetLib;
use crate::worker::MainWorker;
use deno_core::v8_set_flags;
+use deno_core::CoreIsolate;
use deno_core::Deps;
use deno_core::ErrBox;
-use deno_core::EsIsolate;
use deno_core::ModuleSpecifier;
use deno_doc as doc;
use deno_doc::parser::DocFileLoader;
@@ -217,7 +217,7 @@ async fn print_file_info(
{
output.map = source_map.filename.to_str().map(|s| s.to_owned());
}
- let es_state_rc = EsIsolate::state(&worker.isolate);
+ let es_state_rc = CoreIsolate::state(&worker.isolate);
let es_state = es_state_rc.borrow();
if let Some(deps) = es_state.modules.deps(&module_specifier) {
diff --git a/cli/worker.rs b/cli/worker.rs
index 3aaf92f48..ac0ef0d26 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -91,7 +91,7 @@ fn create_channels() -> (WorkerChannelsInternal, WorkerHandle) {
/// - `WebWorker`
pub struct Worker {
pub name: String,
- pub isolate: deno_core::EsIsolate,
+ pub isolate: deno_core::CoreIsolate,
pub inspector: Option<Box<DenoInspector>>,
pub state: Rc<State>,
pub waker: AtomicWaker,
@@ -105,7 +105,7 @@ impl Worker {
startup_data: StartupData,
state: &Rc<State>,
) -> Self {
- let mut isolate = deno_core::EsIsolate::new(
+ let mut isolate = deno_core::CoreIsolate::new_with_loader(
state.clone(),
state.clone(),
startup_data,
@@ -235,7 +235,7 @@ impl Future for Worker {
}
impl Deref for Worker {
- type Target = deno_core::EsIsolate;
+ type Target = deno_core::CoreIsolate;
fn deref(&self) -> &Self::Target {
&self.isolate
}
diff --git a/core/bindings.rs b/core/bindings.rs
index 166c0ee6e..c399f265f 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -3,7 +3,6 @@
use crate::CoreIsolate;
use crate::CoreIsolateState;
use crate::ErrBox;
-use crate::EsIsolate;
use crate::JSError;
use crate::Op;
use crate::OpId;
@@ -240,7 +239,7 @@ pub extern "C" fn host_import_module_dynamically_callback(
let resolver_handle = v8::Global::new(scope, resolver);
{
- let state_rc = EsIsolate::state(scope);
+ let state_rc = CoreIsolate::state(scope);
let mut state = state_rc.borrow_mut();
state.dyn_import_cb(resolver_handle, &specifier_str, &referrer_name_str);
}
@@ -254,7 +253,7 @@ pub extern "C" fn host_initialize_import_meta_object_callback(
meta: v8::Local<v8::Object>,
) {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
- let state_rc = EsIsolate::state(scope);
+ let state_rc = CoreIsolate::state(scope);
let state = state_rc.borrow();
let id = module.get_identity_hash();
@@ -713,7 +712,7 @@ pub fn module_resolve_callback<'s>(
) -> Option<v8::Local<'s, v8::Module>> {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
- let state_rc = EsIsolate::state(scope);
+ let state_rc = CoreIsolate::state(scope);
let mut state = state_rc.borrow_mut();
let referrer_id = referrer.get_identity_hash();
diff --git a/core/core_isolate.rs b/core/core_isolate.rs
index 52e856174..b8922b982 100644
--- a/core/core_isolate.rs
+++ b/core/core_isolate.rs
@@ -8,13 +8,28 @@
use rusty_v8 as v8;
use crate::bindings;
+use crate::errors::attach_handle_to_error;
+use crate::errors::ErrWithV8Handle;
+use crate::futures::FutureExt;
+use crate::module_specifier::ModuleSpecifier;
+use crate::modules::LoadState;
+use crate::modules::ModuleId;
+use crate::modules::ModuleLoadId;
+use crate::modules::ModuleLoader;
+use crate::modules::ModuleSource;
+use crate::modules::Modules;
+use crate::modules::NoopModuleLoader;
+use crate::modules::PrepareLoadFuture;
+use crate::modules::RecursiveModuleLoad;
use crate::ops::*;
use crate::shared_queue::SharedQueue;
use crate::shared_queue::RECOMMENDED_SIZE;
use crate::ErrBox;
use crate::JSError;
+use crate::OpRouter;
use futures::stream::FuturesUnordered;
use futures::stream::StreamExt;
+use futures::stream::StreamFuture;
use futures::task::AtomicWaker;
use futures::Future;
use std::any::Any;
@@ -22,6 +37,7 @@ use std::cell::Cell;
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::From;
+use std::convert::TryFrom;
use std::ffi::c_void;
use std::mem::forget;
use std::ops::Deref;
@@ -125,6 +141,12 @@ pub struct CoreIsolateState {
pub(crate) pending_unref_ops: FuturesUnordered<PendingOpFuture>,
pub(crate) have_unpolled_ops: Cell<bool>,
pub(crate) op_router: Rc<dyn OpRouter>,
+ loader: Rc<dyn ModuleLoader>,
+ pub modules: Modules,
+ pub(crate) dyn_import_map:
+ HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>,
+ preparing_dyn_imports: FuturesUnordered<Pin<Box<PrepareLoadFuture>>>,
+ pending_dyn_imports: FuturesUnordered<StreamFuture<RecursiveModuleLoad>>,
waker: AtomicWaker,
}
@@ -199,6 +221,7 @@ pub struct HeapLimits {
}
pub(crate) struct IsolateOptions {
+ loader: Rc<dyn ModuleLoader>,
op_router: Rc<dyn OpRouter>,
startup_script: Option<OwnedScript>,
startup_snapshot: Option<Snapshot>,
@@ -216,6 +239,28 @@ impl CoreIsolate {
) -> Self {
let (startup_script, startup_snapshot) = startup_data.into_options();
let options = IsolateOptions {
+ loader: Rc::new(NoopModuleLoader),
+ op_router,
+ startup_script,
+ startup_snapshot,
+ will_snapshot,
+ heap_limits: None,
+ };
+
+ Self::from_options(options)
+ }
+
+ // TODO(bartlomieju): add `new_with_loader_and_heap_limits` function?
+ /// Create new isolate that can load and execute ESModules.
+ pub fn new_with_loader(
+ loader: Rc<dyn ModuleLoader>,
+ op_router: Rc<dyn OpRouter>,
+ startup_data: StartupData,
+ will_snapshot: bool,
+ ) -> Self {
+ let (startup_script, startup_snapshot) = startup_data.into_options();
+ let options = IsolateOptions {
+ loader,
op_router,
startup_script,
startup_snapshot,
@@ -239,6 +284,7 @@ impl CoreIsolate {
) -> Self {
let (startup_script, startup_snapshot) = startup_data.into_options();
let options = IsolateOptions {
+ loader: Rc::new(NoopModuleLoader),
op_router,
startup_script,
startup_snapshot,
@@ -316,6 +362,11 @@ impl CoreIsolate {
pending_unref_ops: FuturesUnordered::new(),
have_unpolled_ops: Cell::new(false),
op_router: options.op_router,
+ modules: Modules::new(),
+ loader: options.loader,
+ dyn_import_map: HashMap::new(),
+ preparing_dyn_imports: FuturesUnordered::new(),
+ pending_dyn_imports: FuturesUnordered::new(),
waker: AtomicWaker::new(),
})));
@@ -332,6 +383,12 @@ impl CoreIsolate {
fn setup_isolate(mut isolate: v8::OwnedIsolate) -> v8::OwnedIsolate {
isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 10);
isolate.set_promise_reject_callback(bindings::promise_reject_callback);
+ isolate.set_host_initialize_import_meta_object_callback(
+ bindings::host_initialize_import_meta_object_callback,
+ );
+ isolate.set_host_import_module_dynamically_callback(
+ bindings::host_import_module_dynamically_callback,
+ );
isolate
}
@@ -412,6 +469,8 @@ impl CoreIsolate {
// TODO(piscisaureus): The rusty_v8 type system should enforce this.
state.borrow_mut().global_context.take();
+ std::mem::take(&mut state.borrow_mut().modules);
+
let snapshot_creator = self.snapshot_creator.as_mut().unwrap();
let snapshot = snapshot_creator
.create_blob(v8::FunctionCodeHandling::Keep)
@@ -489,6 +548,24 @@ impl Future for CoreIsolate {
state.waker.register(cx.waker());
}
+ let has_preparing = {
+ let state = state_rc.borrow();
+ !state.preparing_dyn_imports.is_empty()
+ };
+ if has_preparing {
+ let poll_imports = core_isolate.prepare_dyn_imports(cx)?;
+ assert!(poll_imports.is_ready());
+ }
+
+ let has_pending = {
+ let state = state_rc.borrow();
+ !state.pending_dyn_imports.is_empty()
+ };
+ if has_pending {
+ let poll_imports = core_isolate.poll_dyn_imports(cx)?;
+ assert!(poll_imports.is_ready());
+ }
+
let scope = &mut v8::HandleScope::with_context(
&mut **core_isolate,
state_rc.borrow().global_context.as_ref().unwrap(),
@@ -563,7 +640,10 @@ impl Future for CoreIsolate {
let state = state_rc.borrow();
// We're idle if pending_ops is empty.
- if state.pending_ops.is_empty() {
+ if state.pending_ops.is_empty()
+ && state.pending_dyn_imports.is_empty()
+ && state.preparing_dyn_imports.is_empty()
+ {
Poll::Ready(Ok(()))
} else {
if state.have_unpolled_ops.get() {
@@ -584,6 +664,40 @@ impl CoreIsolateState {
) {
self.js_error_create_fn = Box::new(f);
}
+
+ // Called by V8 during `Isolate::mod_instantiate`.
+ pub fn module_resolve_cb(
+ &mut self,
+ specifier: &str,
+ referrer_id: ModuleId,
+ ) -> ModuleId {
+ let referrer = self.modules.get_name(referrer_id).unwrap();
+ let specifier = self
+ .loader
+ .resolve(specifier, referrer, false)
+ .expect("Module should have been already resolved");
+ self.modules.get_id(specifier.as_str()).unwrap_or(0)
+ }
+
+ // Called by V8 during `Isolate::mod_instantiate`.
+ pub fn dyn_import_cb(
+ &mut self,
+ resolver_handle: v8::Global<v8::PromiseResolver>,
+ specifier: &str,
+ referrer: &str,
+ ) {
+ debug!("dyn_import specifier {} referrer {} ", specifier, referrer);
+
+ let load = RecursiveModuleLoad::dynamic_import(
+ specifier,
+ referrer,
+ self.loader.clone(),
+ );
+ self.dyn_import_map.insert(load.id, resolver_handle);
+ self.waker.wake();
+ let fut = load.prepare().boxed_local();
+ self.preparing_dyn_imports.push(fut);
+ }
}
fn async_op_response<'s>(
@@ -728,13 +842,456 @@ fn boxed_slice_to_uint8array<'sc>(
.expect("Failed to create UintArray8")
}
+// Related to module loading
+impl CoreIsolate {
+ /// Low-level module creation.
+ ///
+ /// Called during module loading or dynamic import loading.
+ fn mod_new(
+ &mut self,
+ main: bool,
+ name: &str,
+ source: &str,
+ ) -> Result<ModuleId, ErrBox> {
+ let state_rc = Self::state(self);
+ let scope = &mut v8::HandleScope::with_context(
+ &mut **self,
+ state_rc.borrow().global_context.as_ref().unwrap(),
+ );
+
+ let name_str = v8::String::new(scope, name).unwrap();
+ let source_str = v8::String::new(scope, source).unwrap();
+
+ let origin = bindings::module_origin(scope, name_str);
+ let source = v8::script_compiler::Source::new(source_str, &origin);
+
+ let tc_scope = &mut v8::TryCatch::new(scope);
+
+ let maybe_module = v8::script_compiler::compile_module(tc_scope, source);
+
+ if tc_scope.has_caught() {
+ assert!(maybe_module.is_none());
+ let e = tc_scope.exception().unwrap();
+ return exception_to_err_result(tc_scope, e);
+ }
+
+ let module = maybe_module.unwrap();
+ let id = module.get_identity_hash();
+
+ let mut import_specifiers: Vec<ModuleSpecifier> = vec![];
+ for i in 0..module.get_module_requests_length() {
+ let import_specifier =
+ module.get_module_request(i).to_rust_string_lossy(tc_scope);
+ let state = state_rc.borrow();
+ let module_specifier =
+ state.loader.resolve(&import_specifier, name, false)?;
+ import_specifiers.push(module_specifier);
+ }
+
+ state_rc.borrow_mut().modules.register(
+ id,
+ name,
+ main,
+ v8::Global::<v8::Module>::new(tc_scope, module),
+ import_specifiers,
+ );
+
+ Ok(id)
+ }
+
+ /// Instantiates a ES module
+ ///
+ /// ErrBox can be downcast to a type that exposes additional information about
+ /// the V8 exception. By default this type is JSError, however it may be a
+ /// different type if CoreIsolate::set_js_error_create_fn() has been used.
+ fn mod_instantiate(&mut self, id: ModuleId) -> Result<(), ErrBox> {
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
+ let scope = &mut v8::HandleScope::with_context(
+ &mut **self,
+ state.global_context.as_ref().unwrap(),
+ );
+ let tc_scope = &mut v8::TryCatch::new(scope);
+
+ let module = match state.modules.get_info(id) {
+ Some(info) => v8::Local::new(tc_scope, &info.handle),
+ None if id == 0 => return Ok(()),
+ _ => panic!("module id {} not found in module table", id),
+ };
+ drop(state);
+
+ if module.get_status() == v8::ModuleStatus::Errored {
+ exception_to_err_result(tc_scope, module.get_exception())?
+ }
+
+ let result =
+ module.instantiate_module(tc_scope, bindings::module_resolve_callback);
+ match result {
+ Some(_) => Ok(()),
+ None => {
+ let exception = tc_scope.exception().unwrap();
+ exception_to_err_result(tc_scope, exception)
+ }
+ }
+ }
+
+ /// Evaluates an already instantiated ES module.
+ ///
+ /// ErrBox can be downcast to a type that exposes additional information about
+ /// the V8 exception. By default this type is JSError, however it may be a
+ /// different type if CoreIsolate::set_js_error_create_fn() has been used.
+ pub fn mod_evaluate(&mut self, id: ModuleId) -> Result<(), ErrBox> {
+ self.shared_init();
+
+ let state_rc = Self::state(self);
+
+ let scope = &mut v8::HandleScope::with_context(
+ &mut **self,
+ state_rc.borrow().global_context.as_ref().unwrap(),
+ );
+
+ let module = state_rc
+ .borrow()
+ .modules
+ .get_info(id)
+ .map(|info| v8::Local::new(scope, &info.handle))
+ .expect("ModuleInfo not found");
+ let mut status = module.get_status();
+
+ if status == v8::ModuleStatus::Instantiated {
+ // IMPORTANT: Top-level-await is enabled, which means that return value
+ // of module evaluation is a promise.
+ //
+ // Because that promise is created internally by V8, when error occurs during
+ // module evaluation the promise is rejected, and since the promise has no rejection
+ // handler it will result in call to `bindings::promise_reject_callback` adding
+ // the promise to pending promise rejection table - meaning Isolate will return
+ // error on next poll().
+ //
+ // This situation is not desirable as we want to manually return error at the
+ // end of this function to handle it further. It means we need to manually
+ // remove this promise from pending promise rejection table.
+ //
+ // For more details see:
+ // https://github.com/denoland/deno/issues/4908
+ // https://v8.dev/features/top-level-await#module-execution-order
+ let maybe_value = module.evaluate(scope);
+
+ // Update status after evaluating.
+ status = module.get_status();
+
+ if let Some(value) = maybe_value {
+ assert!(
+ status == v8::ModuleStatus::Evaluated
+ || status == v8::ModuleStatus::Errored
+ );
+ let promise = v8::Local::<v8::Promise>::try_from(value)
+ .expect("Expected to get promise as module evaluation result");
+ let promise_id = promise.get_identity_hash();
+ let mut state = state_rc.borrow_mut();
+ state.pending_promise_exceptions.remove(&promise_id);
+ } else {
+ assert!(status == v8::ModuleStatus::Errored);
+ }
+ }
+
+ match status {
+ v8::ModuleStatus::Evaluated => Ok(()),
+ v8::ModuleStatus::Errored => {
+ let exception = module.get_exception();
+ exception_to_err_result(scope, exception)
+ .map_err(|err| attach_handle_to_error(scope, err, exception))
+ }
+ other => panic!("Unexpected module status {:?}", other),
+ }
+ }
+
+ fn dyn_import_error(
+ &mut self,
+ id: ModuleLoadId,
+ err: ErrBox,
+ ) -> Result<(), ErrBox> {
+ let state_rc = Self::state(self);
+
+ let scope = &mut v8::HandleScope::with_context(
+ &mut **self,
+ state_rc.borrow().global_context.as_ref().unwrap(),
+ );
+
+ let resolver_handle = state_rc
+ .borrow_mut()
+ .dyn_import_map
+ .remove(&id)
+ .expect("Invalid dyn import id");
+ let resolver = resolver_handle.get(scope);
+
+ let exception = err
+ .downcast_ref::<ErrWithV8Handle>()
+ .map(|err| err.get_handle(scope))
+ .unwrap_or_else(|| {
+ let message = err.to_string();
+ let message = v8::String::new(scope, &message).unwrap();
+ v8::Exception::type_error(scope, message)
+ });
+
+ resolver.reject(scope, exception).unwrap();
+ scope.perform_microtask_checkpoint();
+ Ok(())
+ }
+
+ fn dyn_import_done(
+ &mut self,
+ id: ModuleLoadId,
+ mod_id: ModuleId,
+ ) -> Result<(), ErrBox> {
+ let state_rc = Self::state(self);
+
+ debug!("dyn_import_done {} {:?}", id, mod_id);
+ assert!(mod_id != 0);
+ let scope = &mut v8::HandleScope::with_context(
+ &mut **self,
+ state_rc.borrow().global_context.as_ref().unwrap(),
+ );
+
+ let resolver_handle = state_rc
+ .borrow_mut()
+ .dyn_import_map
+ .remove(&id)
+ .expect("Invalid dyn import id");
+ let resolver = resolver_handle.get(scope);
+
+ let module = {
+ let state = state_rc.borrow();
+ state
+ .modules
+ .get_info(mod_id)
+ .map(|info| v8::Local::new(scope, &info.handle))
+ .expect("Dyn import module info not found")
+ };
+ // Resolution success
+ assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated);
+
+ let module_namespace = module.get_module_namespace();
+ resolver.resolve(scope, module_namespace).unwrap();
+ scope.perform_microtask_checkpoint();
+ Ok(())
+ }
+
+ fn prepare_dyn_imports(
+ &mut self,
+ cx: &mut Context,
+ ) -> Poll<Result<(), ErrBox>> {
+ let state_rc = Self::state(self);
+
+ loop {
+ let r = {
+ let mut state = state_rc.borrow_mut();
+ state.preparing_dyn_imports.poll_next_unpin(cx)
+ };
+ match r {
+ Poll::Pending | Poll::Ready(None) => {
+ // There are no active dynamic import loaders, or none are ready.
+ return Poll::Ready(Ok(()));
+ }
+ Poll::Ready(Some(prepare_poll)) => {
+ let dyn_import_id = prepare_poll.0;
+ let prepare_result = prepare_poll.1;
+
+ match prepare_result {
+ Ok(load) => {
+ let state = state_rc.borrow_mut();
+ state.pending_dyn_imports.push(load.into_future());
+ }
+ Err(err) => {
+ self.dyn_import_error(dyn_import_id, err)?;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn poll_dyn_imports(&mut self, cx: &mut Context) -> Poll<Result<(), ErrBox>> {
+ let state_rc = Self::state(self);
+ loop {
+ let poll_result = {
+ let mut state = state_rc.borrow_mut();
+ state.pending_dyn_imports.poll_next_unpin(cx)
+ };
+
+ match poll_result {
+ Poll::Pending | Poll::Ready(None) => {
+ // There are no active dynamic import loaders, or none are ready.
+ return Poll::Ready(Ok(()));
+ }
+ Poll::Ready(Some(load_stream_poll)) => {
+ let maybe_result = load_stream_poll.0;
+ let mut load = load_stream_poll.1;
+ let dyn_import_id = load.id;
+
+ if let Some(load_stream_result) = maybe_result {
+ match load_stream_result {
+ Ok(info) => {
+ // A module (not necessarily the one dynamically imported) has been
+ // fetched. Create and register it, and if successful, poll for the
+ // next recursive-load event related to this dynamic import.
+ match self.register_during_load(info, &mut load) {
+ Ok(()) => {
+ // Keep importing until it's fully drained
+ let state = state_rc.borrow_mut();
+ state.pending_dyn_imports.push(load.into_future());
+ }
+ Err(err) => self.dyn_import_error(dyn_import_id, err)?,
+ }
+ }
+ Err(err) => {
+ // A non-javascript error occurred; this could be due to a an invalid
+ // module specifier, or a problem with the source map, or a failure
+ // to fetch the module source code.
+ self.dyn_import_error(dyn_import_id, err)?
+ }
+ }
+ } else {
+ // The top-level module from a dynamic import has been instantiated.
+ // Load is done.
+ let module_id = load.root_module_id.unwrap();
+ self.mod_instantiate(module_id)?;
+ match self.mod_evaluate(module_id) {
+ Ok(()) => self.dyn_import_done(dyn_import_id, module_id)?,
+ Err(err) => self.dyn_import_error(dyn_import_id, err)?,
+ };
+ }
+ }
+ }
+ }
+ }
+
+ fn register_during_load(
+ &mut self,
+ info: ModuleSource,
+ load: &mut RecursiveModuleLoad,
+ ) -> Result<(), ErrBox> {
+ let ModuleSource {
+ code,
+ module_url_specified,
+ module_url_found,
+ } = info;
+
+ let is_main =
+ load.state == LoadState::LoadingRoot && !load.is_dynamic_import();
+ let referrer_specifier =
+ ModuleSpecifier::resolve_url(&module_url_found).unwrap();
+
+ let state_rc = Self::state(self);
+ // #A There are 3 cases to handle at this moment:
+ // 1. Source code resolved result have the same module name as requested
+ // and is not yet registered
+ // -> register
+ // 2. Source code resolved result have a different name as requested:
+ // 2a. The module with resolved module name has been registered
+ // -> alias
+ // 2b. The module with resolved module name has not yet been registered
+ // -> register & alias
+
+ // If necessary, register an alias.
+ if module_url_specified != module_url_found {
+ let mut state = state_rc.borrow_mut();
+ state
+ .modules
+ .alias(&module_url_specified, &module_url_found);
+ }
+
+ let maybe_mod_id = {
+ let state = state_rc.borrow();
+ state.modules.get_id(&module_url_found)
+ };
+
+ let module_id = match maybe_mod_id {
+ Some(id) => {
+ // Module has already been registered.
+ debug!(
+ "Already-registered module fetched again: {}",
+ module_url_found
+ );
+ id
+ }
+ // Module not registered yet, do it now.
+ None => self.mod_new(is_main, &module_url_found, &code)?,
+ };
+
+ // Now we must iterate over all imports of the module and load them.
+ let imports = {
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
+ state.modules.get_children(module_id).unwrap().clone()
+ };
+
+ for module_specifier in imports {
+ let is_registered = {
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
+ state.modules.is_registered(&module_specifier)
+ };
+ if !is_registered {
+ load
+ .add_import(module_specifier.to_owned(), referrer_specifier.clone());
+ }
+ }
+
+ // If we just finished loading the root module, store the root module id.
+ if load.state == LoadState::LoadingRoot {
+ load.root_module_id = Some(module_id);
+ load.state = LoadState::LoadingImports;
+ }
+
+ if load.pending.is_empty() {
+ load.state = LoadState::Done;
+ }
+
+ Ok(())
+ }
+
+ /// Asynchronously load specified module and all of it's dependencies
+ ///
+ /// User must call `Isolate::mod_evaluate` with returned `ModuleId`
+ /// manually after load is finished.
+ pub async fn load_module(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ code: Option<String>,
+ ) -> Result<ModuleId, ErrBox> {
+ self.shared_init();
+ let loader = {
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
+ state.loader.clone()
+ };
+
+ let load = RecursiveModuleLoad::main(&specifier.to_string(), code, loader);
+ let (_load_id, prepare_result) = load.prepare().await;
+
+ let mut load = prepare_result?;
+
+ while let Some(info_result) = load.next().await {
+ let info = info_result?;
+ self.register_during_load(info, &mut load)?;
+ }
+
+ let root_id = load.root_module_id.expect("Root module id empty");
+ self.mod_instantiate(root_id).map(|_| root_id)
+ }
+}
+
#[cfg(test)]
pub mod tests {
use super::*;
+ use crate::modules::ModuleSourceFuture;
+ use crate::ops::*;
use crate::BasicState;
use crate::BufVec;
use futures::future::lazy;
use futures::FutureExt;
+ use std::io;
use std::ops::FnOnce;
use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -1365,4 +1922,350 @@ pub mod tests {
assert_eq!(0, callback_invoke_count_first.load(Ordering::SeqCst));
assert!(callback_invoke_count_second.load(Ordering::SeqCst) > 0);
}
+
+ #[test]
+ fn test_mods() {
+ #[derive(Default)]
+ struct ModsLoader {
+ pub count: Arc<AtomicUsize>,
+ }
+
+ impl ModuleLoader for ModsLoader {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, ErrBox> {
+ self.count.fetch_add(1, Ordering::Relaxed);
+ assert_eq!(specifier, "./b.js");
+ assert_eq!(referrer, "file:///a.js");
+ let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
+ Ok(s)
+ }
+
+ fn load(
+ &self,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ unreachable!()
+ }
+ }
+
+ let loader = Rc::new(ModsLoader::default());
+ let state = BasicState::new();
+
+ let resolve_count = loader.count.clone();
+ let dispatch_count = Arc::new(AtomicUsize::new(0));
+ let dispatch_count_ = dispatch_count.clone();
+
+ let dispatcher = move |_state: Rc<BasicState>, bufs: BufVec| -> Op {
+ dispatch_count_.fetch_add(1, Ordering::Relaxed);
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 1);
+ assert_eq!(bufs[0][0], 42);
+ let buf = [43u8, 0, 0, 0][..].into();
+ Op::Async(futures::future::ready(buf).boxed())
+ };
+ state.register_op("test", dispatcher);
+
+ let mut isolate =
+ CoreIsolate::new_with_loader(loader, state, StartupData::None, false);
+
+ js_check(isolate.execute(
+ "setup.js",
+ r#"
+ function assert(cond) {
+ if (!cond) {
+ throw Error("assert");
+ }
+ }
+ "#,
+ ));
+
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
+
+ let specifier_a = "file:///a.js".to_string();
+ let mod_a = isolate
+ .mod_new(
+ true,
+ &specifier_a,
+ r#"
+ import { b } from './b.js'
+ if (b() != 'b') throw Error();
+ let control = new Uint8Array([42]);
+ Deno.core.send(1, control);
+ "#,
+ )
+ .unwrap();
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
+
+ let state_rc = CoreIsolate::state(&isolate);
+ {
+ let state = state_rc.borrow();
+ let imports = state.modules.get_children(mod_a);
+ assert_eq!(
+ imports,
+ Some(&vec![ModuleSpecifier::resolve_url("file:///b.js").unwrap()])
+ );
+ }
+ let mod_b = isolate
+ .mod_new(false, "file:///b.js", "export function b() { return 'b' }")
+ .unwrap();
+ {
+ let state = state_rc.borrow();
+ let imports = state.modules.get_children(mod_b).unwrap();
+ assert_eq!(imports.len(), 0);
+ }
+
+ js_check(isolate.mod_instantiate(mod_b));
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
+ assert_eq!(resolve_count.load(Ordering::SeqCst), 1);
+
+ js_check(isolate.mod_instantiate(mod_a));
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
+
+ js_check(isolate.mod_evaluate(mod_a));
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 1);
+ }
+
+ #[test]
+ fn dyn_import_err() {
+ #[derive(Clone, Default)]
+ struct DynImportErrLoader {
+ pub count: Arc<AtomicUsize>,
+ }
+
+ impl ModuleLoader for DynImportErrLoader {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, ErrBox> {
+ self.count.fetch_add(1, Ordering::Relaxed);
+ assert_eq!(specifier, "/foo.js");
+ assert_eq!(referrer, "file:///dyn_import2.js");
+ let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
+ Ok(s)
+ }
+
+ fn load(
+ &self,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed()
+ }
+ }
+
+ // Test an erroneous dynamic import where the specified module isn't found.
+ run_in_task(|cx| {
+ let loader = Rc::new(DynImportErrLoader::default());
+ let count = loader.count.clone();
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
+
+ js_check(isolate.execute(
+ "file:///dyn_import2.js",
+ r#"
+ (async () => {
+ await import("/foo.js");
+ })();
+ "#,
+ ));
+
+ assert_eq!(count.load(Ordering::Relaxed), 0);
+ // We should get an error here.
+ let result = isolate.poll_unpin(cx);
+ if let Poll::Ready(Ok(_)) = result {
+ unreachable!();
+ }
+ assert_eq!(count.load(Ordering::Relaxed), 2);
+ })
+ }
+
+ #[derive(Clone, Default)]
+ struct DynImportOkLoader {
+ pub prepare_load_count: Arc<AtomicUsize>,
+ pub resolve_count: Arc<AtomicUsize>,
+ pub load_count: Arc<AtomicUsize>,
+ }
+
+ impl ModuleLoader for DynImportOkLoader {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, ErrBox> {
+ let c = self.resolve_count.fetch_add(1, Ordering::Relaxed);
+ assert!(c < 4);
+ assert_eq!(specifier, "./b.js");
+ assert_eq!(referrer, "file:///dyn_import3.js");
+ let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
+ Ok(s)
+ }
+
+ fn load(
+ &self,
+ specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ self.load_count.fetch_add(1, Ordering::Relaxed);
+ let info = ModuleSource {
+ module_url_specified: specifier.to_string(),
+ module_url_found: specifier.to_string(),
+ code: "export function b() { return 'b' }".to_owned(),
+ };
+ async move { Ok(info) }.boxed()
+ }
+
+ fn prepare_load(
+ &self,
+ _load_id: ModuleLoadId,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<String>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<dyn Future<Output = Result<(), ErrBox>>>> {
+ self.prepare_load_count.fetch_add(1, Ordering::Relaxed);
+ async { Ok(()) }.boxed_local()
+ }
+ }
+
+ #[test]
+ fn dyn_import_ok() {
+ run_in_task(|cx| {
+ let loader = Rc::new(DynImportOkLoader::default());
+ let prepare_load_count = loader.prepare_load_count.clone();
+ let resolve_count = loader.resolve_count.clone();
+ let load_count = loader.load_count.clone();
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
+
+ // Dynamically import mod_b
+ js_check(isolate.execute(
+ "file:///dyn_import3.js",
+ r#"
+ (async () => {
+ let mod = await import("./b.js");
+ if (mod.b() !== 'b') {
+ throw Error("bad1");
+ }
+ // And again!
+ mod = await import("./b.js");
+ if (mod.b() !== 'b') {
+ throw Error("bad2");
+ }
+ })();
+ "#,
+ ));
+
+ // First poll runs `prepare_load` hook.
+ assert!(matches!(isolate.poll_unpin(cx), Poll::Pending));
+ assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
+
+ // Second poll actually loads modules into the isolate.
+ assert!(matches!(isolate.poll_unpin(cx), Poll::Ready(Ok(_))));
+ assert_eq!(resolve_count.load(Ordering::Relaxed), 4);
+ assert_eq!(load_count.load(Ordering::Relaxed), 2);
+ assert!(matches!(isolate.poll_unpin(cx), Poll::Ready(Ok(_))));
+ assert_eq!(resolve_count.load(Ordering::Relaxed), 4);
+ assert_eq!(load_count.load(Ordering::Relaxed), 2);
+ })
+ }
+
+ #[test]
+ fn dyn_import_borrow_mut_error() {
+ // https://github.com/denoland/deno/issues/6054
+ run_in_task(|cx| {
+ let loader = Rc::new(DynImportOkLoader::default());
+ let prepare_load_count = loader.prepare_load_count.clone();
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
+ js_check(isolate.execute(
+ "file:///dyn_import3.js",
+ r#"
+ (async () => {
+ let mod = await import("./b.js");
+ if (mod.b() !== 'b') {
+ throw Error("bad");
+ }
+ // Now do any op
+ Deno.core.ops();
+ })();
+ "#,
+ ));
+ // First poll runs `prepare_load` hook.
+ let _ = isolate.poll_unpin(cx);
+ assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
+ // Second poll triggers error
+ let _ = isolate.poll_unpin(cx);
+ })
+ }
+
+ #[test]
+ fn es_snapshot() {
+ #[derive(Default)]
+ struct ModsLoader;
+
+ impl ModuleLoader for ModsLoader {
+ fn resolve(
+ &self,
+ specifier: &str,
+ referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, ErrBox> {
+ assert_eq!(specifier, "file:///main.js");
+ assert_eq!(referrer, ".");
+ let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
+ Ok(s)
+ }
+
+ fn load(
+ &self,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ unreachable!()
+ }
+ }
+
+ let loader = std::rc::Rc::new(ModsLoader::default());
+ let mut runtime_isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ true,
+ );
+
+ let specifier = ModuleSpecifier::resolve_url("file:///main.js").unwrap();
+ let source_code = "Deno.core.print('hello\\n')".to_string();
+
+ let module_id = futures::executor::block_on(
+ runtime_isolate.load_module(&specifier, Some(source_code)),
+ )
+ .unwrap();
+
+ js_check(runtime_isolate.mod_evaluate(module_id));
+
+ let _snapshot = runtime_isolate.snapshot();
+ }
}
diff --git a/core/es_isolate.rs b/core/es_isolate.rs
deleted file mode 100644
index 194f1adfa..000000000
--- a/core/es_isolate.rs
+++ /dev/null
@@ -1,983 +0,0 @@
-// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-
-// This module provides higher level implementation of CoreIsolate that
-// supports asynchronous loading and executution of ES Modules.
-// The isolate.rs should never depend on this module.
-
-use rusty_v8 as v8;
-
-use crate::bindings;
-use crate::errors::ErrBox;
-use crate::errors::ErrWithV8Handle;
-use crate::futures::FutureExt;
-use crate::OpRouter;
-use futures::ready;
-use futures::stream::FuturesUnordered;
-use futures::stream::StreamExt;
-use futures::stream::StreamFuture;
-use futures::task::AtomicWaker;
-use futures::Future;
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::convert::TryFrom;
-use std::ops::{Deref, DerefMut};
-use std::option::Option;
-use std::pin::Pin;
-use std::rc::Rc;
-use std::task::Context;
-use std::task::Poll;
-
-use crate::core_isolate::exception_to_err_result;
-use crate::errors::attach_handle_to_error;
-use crate::module_specifier::ModuleSpecifier;
-use crate::modules::LoadState;
-use crate::modules::ModuleId;
-use crate::modules::ModuleLoadId;
-use crate::modules::ModuleLoader;
-use crate::modules::ModuleSource;
-use crate::modules::Modules;
-use crate::modules::PrepareLoadFuture;
-use crate::modules::RecursiveModuleLoad;
-use crate::CoreIsolate;
-use crate::StartupData;
-
-/// More specialized version of `CoreIsolate` that provides loading
-/// and execution of ES Modules.
-///
-/// Creating `EsIsolate` requires to pass `loader` argument
-/// that implements `ModuleLoader` trait - that way actual resolution and
-/// loading of modules can be customized by the implementor.
-pub struct EsIsolate(CoreIsolate);
-
-pub struct EsIsolateState {
- loader: Rc<dyn ModuleLoader>,
- pub modules: Modules,
- pub(crate) dyn_import_map:
- HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>,
-
- preparing_dyn_imports: FuturesUnordered<Pin<Box<PrepareLoadFuture>>>,
- pending_dyn_imports: FuturesUnordered<StreamFuture<RecursiveModuleLoad>>,
- waker: AtomicWaker,
-}
-
-impl Deref for EsIsolate {
- type Target = CoreIsolate;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl DerefMut for EsIsolate {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-impl EsIsolate {
- pub fn new(
- loader: Rc<dyn ModuleLoader>,
- op_router: Rc<dyn OpRouter>,
- startup_data: StartupData,
- will_snapshot: bool,
- ) -> Self {
- let mut core_isolate =
- CoreIsolate::new(op_router, startup_data, will_snapshot);
- {
- core_isolate.set_host_initialize_import_meta_object_callback(
- bindings::host_initialize_import_meta_object_callback,
- );
- core_isolate.set_host_import_module_dynamically_callback(
- bindings::host_import_module_dynamically_callback,
- );
- }
-
- core_isolate.set_slot(Rc::new(RefCell::new(EsIsolateState {
- modules: Modules::new(),
- loader,
- dyn_import_map: HashMap::new(),
- preparing_dyn_imports: FuturesUnordered::new(),
- pending_dyn_imports: FuturesUnordered::new(),
- waker: AtomicWaker::new(),
- })));
-
- EsIsolate(core_isolate)
- }
-
- /// Low-level module creation.
- ///
- /// Called during module loading or dynamic import loading.
- fn mod_new(
- &mut self,
- main: bool,
- name: &str,
- source: &str,
- ) -> Result<ModuleId, ErrBox> {
- let state_rc = Self::state(self);
- let core_state_rc = CoreIsolate::state(self);
- let scope = &mut v8::HandleScope::with_context(
- &mut *self.0,
- core_state_rc.borrow().global_context.as_ref().unwrap(),
- );
-
- let name_str = v8::String::new(scope, name).unwrap();
- let source_str = v8::String::new(scope, source).unwrap();
-
- let origin = bindings::module_origin(scope, name_str);
- let source = v8::script_compiler::Source::new(source_str, &origin);
-
- let tc_scope = &mut v8::TryCatch::new(scope);
-
- let maybe_module = v8::script_compiler::compile_module(tc_scope, source);
-
- if tc_scope.has_caught() {
- assert!(maybe_module.is_none());
- let e = tc_scope.exception().unwrap();
- return exception_to_err_result(tc_scope, e);
- }
-
- let module = maybe_module.unwrap();
- let id = module.get_identity_hash();
-
- let mut import_specifiers: Vec<ModuleSpecifier> = vec![];
- for i in 0..module.get_module_requests_length() {
- let import_specifier =
- module.get_module_request(i).to_rust_string_lossy(tc_scope);
- let state = state_rc.borrow();
- let module_specifier =
- state.loader.resolve(&import_specifier, name, false)?;
- import_specifiers.push(module_specifier);
- }
-
- state_rc.borrow_mut().modules.register(
- id,
- name,
- main,
- v8::Global::<v8::Module>::new(tc_scope, module),
- import_specifiers,
- );
-
- Ok(id)
- }
-
- /// Instantiates a ES module
- ///
- /// ErrBox can be downcast to a type that exposes additional information about
- /// the V8 exception. By default this type is JSError, however it may be a
- /// different type if CoreIsolate::set_js_error_create_fn() has been used.
- fn mod_instantiate(&mut self, id: ModuleId) -> Result<(), ErrBox> {
- let core_state_rc = CoreIsolate::state(self);
- let core_state = core_state_rc.borrow();
- let scope = &mut v8::HandleScope::with_context(
- &mut *self.0,
- core_state.global_context.as_ref().unwrap(),
- );
- let tc_scope = &mut v8::TryCatch::new(scope);
-
- let module = match Self::state(tc_scope).borrow().modules.get_info(id) {
- Some(info) => v8::Local::new(tc_scope, &info.handle),
- None if id == 0 => return Ok(()),
- _ => panic!("module id {} not found in module table", id),
- };
-
- if module.get_status() == v8::ModuleStatus::Errored {
- exception_to_err_result(tc_scope, module.get_exception())?
- }
-
- let result =
- module.instantiate_module(tc_scope, bindings::module_resolve_callback);
- match result {
- Some(_) => Ok(()),
- None => {
- let exception = tc_scope.exception().unwrap();
- exception_to_err_result(tc_scope, exception)
- }
- }
- }
-
- /// Evaluates an already instantiated ES module.
- ///
- /// ErrBox can be downcast to a type that exposes additional information about
- /// the V8 exception. By default this type is JSError, however it may be a
- /// different type if CoreIsolate::set_js_error_create_fn() has been used.
- pub fn mod_evaluate(&mut self, id: ModuleId) -> Result<(), ErrBox> {
- self.shared_init();
-
- let core_state_rc = CoreIsolate::state(self);
-
- let scope = &mut v8::HandleScope::with_context(
- &mut *self.0,
- core_state_rc.borrow().global_context.as_ref().unwrap(),
- );
-
- let module = Self::state(scope)
- .borrow()
- .modules
- .get_info(id)
- .map(|info| v8::Local::new(scope, &info.handle))
- .expect("ModuleInfo not found");
- let mut status = module.get_status();
-
- if status == v8::ModuleStatus::Instantiated {
- // IMPORTANT: Top-level-await is enabled, which means that return value
- // of module evaluation is a promise.
- //
- // Because that promise is created internally by V8, when error occurs during
- // module evaluation the promise is rejected, and since the promise has no rejection
- // handler it will result in call to `bindings::promise_reject_callback` adding
- // the promise to pending promise rejection table - meaning Isolate will return
- // error on next poll().
- //
- // This situation is not desirable as we want to manually return error at the
- // end of this function to handle it further. It means we need to manually
- // remove this promise from pending promise rejection table.
- //
- // For more details see:
- // https://github.com/denoland/deno/issues/4908
- // https://v8.dev/features/top-level-await#module-execution-order
- let maybe_value = module.evaluate(scope);
-
- // Update status after evaluating.
- status = module.get_status();
-
- if let Some(value) = maybe_value {
- assert!(
- status == v8::ModuleStatus::Evaluated
- || status == v8::ModuleStatus::Errored
- );
- let promise = v8::Local::<v8::Promise>::try_from(value)
- .expect("Expected to get promise as module evaluation result");
- let promise_id = promise.get_identity_hash();
- let mut core_state = core_state_rc.borrow_mut();
- core_state.pending_promise_exceptions.remove(&promise_id);
- } else {
- assert!(status == v8::ModuleStatus::Errored);
- }
- }
-
- match status {
- v8::ModuleStatus::Evaluated => Ok(()),
- v8::ModuleStatus::Errored => {
- let exception = module.get_exception();
- exception_to_err_result(scope, exception)
- .map_err(|err| attach_handle_to_error(scope, err, exception))
- }
- other => panic!("Unexpected module status {:?}", other),
- }
- }
-
- fn dyn_import_error(
- &mut self,
- id: ModuleLoadId,
- err: ErrBox,
- ) -> Result<(), ErrBox> {
- let state_rc = Self::state(self);
- let core_state_rc = CoreIsolate::state(self);
-
- let scope = &mut v8::HandleScope::with_context(
- &mut *self.0,
- core_state_rc.borrow().global_context.as_ref().unwrap(),
- );
-
- let resolver_handle = state_rc
- .borrow_mut()
- .dyn_import_map
- .remove(&id)
- .expect("Invalid dyn import id");
- let resolver = resolver_handle.get(scope);
-
- let exception = err
- .downcast_ref::<ErrWithV8Handle>()
- .map(|err| err.get_handle(scope))
- .unwrap_or_else(|| {
- let message = err.to_string();
- let message = v8::String::new(scope, &message).unwrap();
- v8::Exception::type_error(scope, message)
- });
-
- resolver.reject(scope, exception).unwrap();
- scope.perform_microtask_checkpoint();
- Ok(())
- }
-
- fn dyn_import_done(
- &mut self,
- id: ModuleLoadId,
- mod_id: ModuleId,
- ) -> Result<(), ErrBox> {
- let state_rc = Self::state(self);
-
- let core_state_rc = CoreIsolate::state(self);
-
- debug!("dyn_import_done {} {:?}", id, mod_id);
- assert!(mod_id != 0);
- let scope = &mut v8::HandleScope::with_context(
- &mut *self.0,
- core_state_rc.borrow().global_context.as_ref().unwrap(),
- );
-
- let resolver_handle = state_rc
- .borrow_mut()
- .dyn_import_map
- .remove(&id)
- .expect("Invalid dyn import id");
- let resolver = resolver_handle.get(scope);
-
- let module = {
- let state = state_rc.borrow();
- state
- .modules
- .get_info(mod_id)
- .map(|info| v8::Local::new(scope, &info.handle))
- .expect("Dyn import module info not found")
- };
- // Resolution success
- assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated);
-
- let module_namespace = module.get_module_namespace();
- resolver.resolve(scope, module_namespace).unwrap();
- scope.perform_microtask_checkpoint();
- Ok(())
- }
-
- fn prepare_dyn_imports(
- &mut self,
- cx: &mut Context,
- ) -> Poll<Result<(), ErrBox>> {
- let state_rc = Self::state(self);
-
- loop {
- let r = {
- let mut state = state_rc.borrow_mut();
- state.preparing_dyn_imports.poll_next_unpin(cx)
- };
- match r {
- Poll::Pending | Poll::Ready(None) => {
- // There are no active dynamic import loaders, or none are ready.
- return Poll::Ready(Ok(()));
- }
- Poll::Ready(Some(prepare_poll)) => {
- let dyn_import_id = prepare_poll.0;
- let prepare_result = prepare_poll.1;
-
- match prepare_result {
- Ok(load) => {
- let state = state_rc.borrow_mut();
- state.pending_dyn_imports.push(load.into_future());
- }
- Err(err) => {
- self.dyn_import_error(dyn_import_id, err)?;
- }
- }
- }
- }
- }
- }
-
- fn poll_dyn_imports(&mut self, cx: &mut Context) -> Poll<Result<(), ErrBox>> {
- let state_rc = Self::state(self);
- loop {
- let poll_result = {
- let mut state = state_rc.borrow_mut();
- state.pending_dyn_imports.poll_next_unpin(cx)
- };
-
- match poll_result {
- Poll::Pending | Poll::Ready(None) => {
- // There are no active dynamic import loaders, or none are ready.
- return Poll::Ready(Ok(()));
- }
- Poll::Ready(Some(load_stream_poll)) => {
- let maybe_result = load_stream_poll.0;
- let mut load = load_stream_poll.1;
- let dyn_import_id = load.id;
-
- if let Some(load_stream_result) = maybe_result {
- match load_stream_result {
- Ok(info) => {
- // A module (not necessarily the one dynamically imported) has been
- // fetched. Create and register it, and if successful, poll for the
- // next recursive-load event related to this dynamic import.
- match self.register_during_load(info, &mut load) {
- Ok(()) => {
- // Keep importing until it's fully drained
- let state = state_rc.borrow_mut();
- state.pending_dyn_imports.push(load.into_future());
- }
- Err(err) => self.dyn_import_error(dyn_import_id, err)?,
- }
- }
- Err(err) => {
- // A non-javascript error occurred; this could be due to a an invalid
- // module specifier, or a problem with the source map, or a failure
- // to fetch the module source code.
- self.dyn_import_error(dyn_import_id, err)?
- }
- }
- } else {
- // The top-level module from a dynamic import has been instantiated.
- // Load is done.
- let module_id = load.root_module_id.unwrap();
- self.mod_instantiate(module_id)?;
- match self.mod_evaluate(module_id) {
- Ok(()) => self.dyn_import_done(dyn_import_id, module_id)?,
- Err(err) => self.dyn_import_error(dyn_import_id, err)?,
- };
- }
- }
- }
- }
- }
-
- fn register_during_load(
- &mut self,
- info: ModuleSource,
- load: &mut RecursiveModuleLoad,
- ) -> Result<(), ErrBox> {
- let ModuleSource {
- code,
- module_url_specified,
- module_url_found,
- } = info;
-
- let is_main =
- load.state == LoadState::LoadingRoot && !load.is_dynamic_import();
- let referrer_specifier =
- ModuleSpecifier::resolve_url(&module_url_found).unwrap();
-
- let state_rc = Self::state(self);
- // #A There are 3 cases to handle at this moment:
- // 1. Source code resolved result have the same module name as requested
- // and is not yet registered
- // -> register
- // 2. Source code resolved result have a different name as requested:
- // 2a. The module with resolved module name has been registered
- // -> alias
- // 2b. The module with resolved module name has not yet been registered
- // -> register & alias
-
- // If necessary, register an alias.
- if module_url_specified != module_url_found {
- let mut state = state_rc.borrow_mut();
- state
- .modules
- .alias(&module_url_specified, &module_url_found);
- }
-
- let maybe_mod_id = {
- let state = state_rc.borrow();
- state.modules.get_id(&module_url_found)
- };
-
- let module_id = match maybe_mod_id {
- Some(id) => {
- // Module has already been registered.
- debug!(
- "Already-registered module fetched again: {}",
- module_url_found
- );
- id
- }
- // Module not registered yet, do it now.
- None => self.mod_new(is_main, &module_url_found, &code)?,
- };
-
- // Now we must iterate over all imports of the module and load them.
- let imports = {
- let state_rc = Self::state(self);
- let state = state_rc.borrow();
- state.modules.get_children(module_id).unwrap().clone()
- };
-
- for module_specifier in imports {
- let is_registered = {
- let state_rc = Self::state(self);
- let state = state_rc.borrow();
- state.modules.is_registered(&module_specifier)
- };
- if !is_registered {
- load
- .add_import(module_specifier.to_owned(), referrer_specifier.clone());
- }
- }
-
- // If we just finished loading the root module, store the root module id.
- if load.state == LoadState::LoadingRoot {
- load.root_module_id = Some(module_id);
- load.state = LoadState::LoadingImports;
- }
-
- if load.pending.is_empty() {
- load.state = LoadState::Done;
- }
-
- Ok(())
- }
-
- /// Asynchronously load specified module and all of it's dependencies
- ///
- /// User must call `Isolate::mod_evaluate` with returned `ModuleId`
- /// manually after load is finished.
- pub async fn load_module(
- &mut self,
- specifier: &ModuleSpecifier,
- code: Option<String>,
- ) -> Result<ModuleId, ErrBox> {
- self.shared_init();
- let loader = {
- let state_rc = Self::state(self);
- let state = state_rc.borrow();
- state.loader.clone()
- };
-
- let load = RecursiveModuleLoad::main(&specifier.to_string(), code, loader);
- let (_load_id, prepare_result) = load.prepare().await;
-
- let mut load = prepare_result?;
-
- while let Some(info_result) = load.next().await {
- let info = info_result?;
- self.register_during_load(info, &mut load)?;
- }
-
- let root_id = load.root_module_id.expect("Root module id empty");
- self.mod_instantiate(root_id).map(|_| root_id)
- }
-
- pub fn snapshot(&mut self) -> v8::StartupData {
- let state_rc = Self::state(self);
- std::mem::take(&mut state_rc.borrow_mut().modules);
- CoreIsolate::snapshot(self)
- }
-
- pub fn state(isolate: &v8::Isolate) -> Rc<RefCell<EsIsolateState>> {
- let s = isolate.get_slot::<Rc<RefCell<EsIsolateState>>>().unwrap();
- s.clone()
- }
-}
-
-impl Future for EsIsolate {
- type Output = Result<(), ErrBox>;
-
- fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
- let es_isolate = self.get_mut();
-
- let state_rc = Self::state(es_isolate);
-
- {
- let state = state_rc.borrow();
- state.waker.register(cx.waker());
- }
-
- let has_preparing = {
- let state = state_rc.borrow();
- !state.preparing_dyn_imports.is_empty()
- };
- if has_preparing {
- let poll_imports = es_isolate.prepare_dyn_imports(cx)?;
- assert!(poll_imports.is_ready());
- }
-
- let has_pending = {
- let state = state_rc.borrow();
- !state.pending_dyn_imports.is_empty()
- };
- if has_pending {
- let poll_imports = es_isolate.poll_dyn_imports(cx)?;
- assert!(poll_imports.is_ready());
- }
-
- match ready!(es_isolate.0.poll_unpin(cx)) {
- Ok(()) => {
- let state = state_rc.borrow();
- if state.pending_dyn_imports.is_empty()
- && state.preparing_dyn_imports.is_empty()
- {
- Poll::Ready(Ok(()))
- } else {
- Poll::Pending
- }
- }
- Err(e) => Poll::Ready(Err(e)),
- }
- }
-}
-
-impl EsIsolateState {
- // Called by V8 during `Isolate::mod_instantiate`.
- pub fn module_resolve_cb(
- &mut self,
- specifier: &str,
- referrer_id: ModuleId,
- ) -> ModuleId {
- let referrer = self.modules.get_name(referrer_id).unwrap();
- let specifier = self
- .loader
- .resolve(specifier, referrer, false)
- .expect("Module should have been already resolved");
- self.modules.get_id(specifier.as_str()).unwrap_or(0)
- }
-
- // Called by V8 during `Isolate::mod_instantiate`.
- pub fn dyn_import_cb(
- &mut self,
- resolver_handle: v8::Global<v8::PromiseResolver>,
- specifier: &str,
- referrer: &str,
- ) {
- debug!("dyn_import specifier {} referrer {} ", specifier, referrer);
-
- let load = RecursiveModuleLoad::dynamic_import(
- specifier,
- referrer,
- self.loader.clone(),
- );
- self.dyn_import_map.insert(load.id, resolver_handle);
- self.waker.wake();
- let fut = load.prepare().boxed_local();
- self.preparing_dyn_imports.push(fut);
- }
-}
-
-#[cfg(test)]
-pub mod tests {
- use super::*;
- use crate::core_isolate::tests::run_in_task;
- use crate::js_check;
- use crate::modules::ModuleSourceFuture;
- use crate::ops::*;
- use crate::BasicState;
- use crate::BufVec;
- use std::io;
- use std::sync::atomic::{AtomicUsize, Ordering};
- use std::sync::Arc;
-
- #[test]
- fn test_mods() {
- #[derive(Default)]
- struct ModsLoader {
- pub count: Arc<AtomicUsize>,
- }
-
- impl ModuleLoader for ModsLoader {
- fn resolve(
- &self,
- specifier: &str,
- referrer: &str,
- _is_main: bool,
- ) -> Result<ModuleSpecifier, ErrBox> {
- self.count.fetch_add(1, Ordering::Relaxed);
- assert_eq!(specifier, "./b.js");
- assert_eq!(referrer, "file:///a.js");
- let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
- Ok(s)
- }
-
- fn load(
- &self,
- _module_specifier: &ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
- _is_dyn_import: bool,
- ) -> Pin<Box<ModuleSourceFuture>> {
- unreachable!()
- }
- }
-
- let loader = Rc::new(ModsLoader::default());
- let state = BasicState::new();
-
- let resolve_count = loader.count.clone();
- let dispatch_count = Arc::new(AtomicUsize::new(0));
- let dispatch_count_ = dispatch_count.clone();
-
- let dispatcher = move |_state: Rc<BasicState>, bufs: BufVec| -> Op {
- dispatch_count_.fetch_add(1, Ordering::Relaxed);
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 1);
- assert_eq!(bufs[0][0], 42);
- let buf = [43u8, 0, 0, 0][..].into();
- Op::Async(futures::future::ready(buf).boxed())
- };
- state.register_op("test", dispatcher);
-
- let mut isolate = EsIsolate::new(loader, state, StartupData::None, false);
-
- js_check(isolate.execute(
- "setup.js",
- r#"
- function assert(cond) {
- if (!cond) {
- throw Error("assert");
- }
- }
- "#,
- ));
-
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
-
- let specifier_a = "file:///a.js".to_string();
- let mod_a = isolate
- .mod_new(
- true,
- &specifier_a,
- r#"
- import { b } from './b.js'
- if (b() != 'b') throw Error();
- let control = new Uint8Array([42]);
- Deno.core.send(1, control);
- "#,
- )
- .unwrap();
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
-
- let state_rc = EsIsolate::state(&isolate);
- {
- let state = state_rc.borrow();
- let imports = state.modules.get_children(mod_a);
- assert_eq!(
- imports,
- Some(&vec![ModuleSpecifier::resolve_url("file:///b.js").unwrap()])
- );
- }
- let mod_b = isolate
- .mod_new(false, "file:///b.js", "export function b() { return 'b' }")
- .unwrap();
- {
- let state = state_rc.borrow();
- let imports = state.modules.get_children(mod_b).unwrap();
- assert_eq!(imports.len(), 0);
- }
-
- js_check(isolate.mod_instantiate(mod_b));
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
- assert_eq!(resolve_count.load(Ordering::SeqCst), 1);
-
- js_check(isolate.mod_instantiate(mod_a));
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 0);
-
- js_check(isolate.mod_evaluate(mod_a));
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 1);
- }
-
- #[test]
- fn dyn_import_err() {
- #[derive(Clone, Default)]
- struct DynImportErrLoader {
- pub count: Arc<AtomicUsize>,
- }
-
- impl ModuleLoader for DynImportErrLoader {
- fn resolve(
- &self,
- specifier: &str,
- referrer: &str,
- _is_main: bool,
- ) -> Result<ModuleSpecifier, ErrBox> {
- self.count.fetch_add(1, Ordering::Relaxed);
- assert_eq!(specifier, "/foo.js");
- assert_eq!(referrer, "file:///dyn_import2.js");
- let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
- Ok(s)
- }
-
- fn load(
- &self,
- _module_specifier: &ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
- _is_dyn_import: bool,
- ) -> Pin<Box<ModuleSourceFuture>> {
- async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed()
- }
- }
-
- // Test an erroneous dynamic import where the specified module isn't found.
- run_in_task(|cx| {
- let loader = Rc::new(DynImportErrLoader::default());
- let count = loader.count.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
-
- js_check(isolate.execute(
- "file:///dyn_import2.js",
- r#"
- (async () => {
- await import("/foo.js");
- })();
- "#,
- ));
-
- assert_eq!(count.load(Ordering::Relaxed), 0);
- // We should get an error here.
- let result = isolate.poll_unpin(cx);
- if let Poll::Ready(Ok(_)) = result {
- unreachable!();
- }
- assert_eq!(count.load(Ordering::Relaxed), 2);
- })
- }
-
- #[derive(Clone, Default)]
- struct DynImportOkLoader {
- pub prepare_load_count: Arc<AtomicUsize>,
- pub resolve_count: Arc<AtomicUsize>,
- pub load_count: Arc<AtomicUsize>,
- }
-
- impl ModuleLoader for DynImportOkLoader {
- fn resolve(
- &self,
- specifier: &str,
- referrer: &str,
- _is_main: bool,
- ) -> Result<ModuleSpecifier, ErrBox> {
- let c = self.resolve_count.fetch_add(1, Ordering::Relaxed);
- assert!(c < 4);
- assert_eq!(specifier, "./b.js");
- assert_eq!(referrer, "file:///dyn_import3.js");
- let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
- Ok(s)
- }
-
- fn load(
- &self,
- specifier: &ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
- _is_dyn_import: bool,
- ) -> Pin<Box<ModuleSourceFuture>> {
- self.load_count.fetch_add(1, Ordering::Relaxed);
- let info = ModuleSource {
- module_url_specified: specifier.to_string(),
- module_url_found: specifier.to_string(),
- code: "export function b() { return 'b' }".to_owned(),
- };
- async move { Ok(info) }.boxed()
- }
-
- fn prepare_load(
- &self,
- _load_id: ModuleLoadId,
- _module_specifier: &ModuleSpecifier,
- _maybe_referrer: Option<String>,
- _is_dyn_import: bool,
- ) -> Pin<Box<dyn Future<Output = Result<(), ErrBox>>>> {
- self.prepare_load_count.fetch_add(1, Ordering::Relaxed);
- async { Ok(()) }.boxed_local()
- }
- }
-
- #[test]
- fn dyn_import_ok() {
- run_in_task(|cx| {
- let loader = Rc::new(DynImportOkLoader::default());
- let prepare_load_count = loader.prepare_load_count.clone();
- let resolve_count = loader.resolve_count.clone();
- let load_count = loader.load_count.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
-
- // Dynamically import mod_b
- js_check(isolate.execute(
- "file:///dyn_import3.js",
- r#"
- (async () => {
- let mod = await import("./b.js");
- if (mod.b() !== 'b') {
- throw Error("bad1");
- }
- // And again!
- mod = await import("./b.js");
- if (mod.b() !== 'b') {
- throw Error("bad2");
- }
- })();
- "#,
- ));
-
- // First poll runs `prepare_load` hook.
- assert!(matches!(isolate.poll_unpin(cx), Poll::Pending));
- assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
-
- // Second poll actually loads modules into the isolate.
- assert!(matches!(isolate.poll_unpin(cx), Poll::Ready(Ok(_))));
- assert_eq!(resolve_count.load(Ordering::Relaxed), 4);
- assert_eq!(load_count.load(Ordering::Relaxed), 2);
- assert!(matches!(isolate.poll_unpin(cx), Poll::Ready(Ok(_))));
- assert_eq!(resolve_count.load(Ordering::Relaxed), 4);
- assert_eq!(load_count.load(Ordering::Relaxed), 2);
- })
- }
-
- #[test]
- fn dyn_import_borrow_mut_error() {
- // https://github.com/denoland/deno/issues/6054
- run_in_task(|cx| {
- let loader = Rc::new(DynImportOkLoader::default());
- let prepare_load_count = loader.prepare_load_count.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
- js_check(isolate.execute(
- "file:///dyn_import3.js",
- r#"
- (async () => {
- let mod = await import("./b.js");
- if (mod.b() !== 'b') {
- throw Error("bad");
- }
- // Now do any op
- Deno.core.ops();
- })();
- "#,
- ));
- // First poll runs `prepare_load` hook.
- let _ = isolate.poll_unpin(cx);
- assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
- // Second poll triggers error
- let _ = isolate.poll_unpin(cx);
- })
- }
-
- #[test]
- fn es_snapshot() {
- #[derive(Default)]
- struct ModsLoader;
-
- impl ModuleLoader for ModsLoader {
- fn resolve(
- &self,
- specifier: &str,
- referrer: &str,
- _is_main: bool,
- ) -> Result<ModuleSpecifier, ErrBox> {
- assert_eq!(specifier, "file:///main.js");
- assert_eq!(referrer, ".");
- let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap();
- Ok(s)
- }
-
- fn load(
- &self,
- _module_specifier: &ModuleSpecifier,
- _maybe_referrer: Option<ModuleSpecifier>,
- _is_dyn_import: bool,
- ) -> Pin<Box<ModuleSourceFuture>> {
- unreachable!()
- }
- }
-
- let loader = std::rc::Rc::new(ModsLoader::default());
- let mut runtime_isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, true);
-
- let specifier = ModuleSpecifier::resolve_url("file:///main.js").unwrap();
- let source_code = "Deno.core.print('hello\\n')".to_string();
-
- let module_id = futures::executor::block_on(
- runtime_isolate.load_module(&specifier, Some(source_code)),
- )
- .unwrap();
-
- js_check(runtime_isolate.mod_evaluate(module_id));
-
- let _snapshot = runtime_isolate.snapshot();
- }
-}
diff --git a/core/lib.rs b/core/lib.rs
index 647c91aa4..f09278b2f 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -12,7 +12,6 @@ mod basic_state;
mod bindings;
mod core_isolate;
mod errors;
-mod es_isolate;
mod flags;
mod module_specifier;
mod modules;
@@ -37,8 +36,6 @@ pub use crate::core_isolate::StartupData;
pub use crate::errors::AnyError;
pub use crate::errors::ErrBox;
pub use crate::errors::JSError;
-pub use crate::es_isolate::EsIsolate;
-pub use crate::es_isolate::EsIsolateState;
pub use crate::flags::v8_set_flags;
pub use crate::module_specifier::ModuleResolutionError;
pub use crate::module_specifier::ModuleSpecifier;
diff --git a/core/modules.rs b/core/modules.rs
index 817e1f25e..7b1e0c5a3 100644
--- a/core/modules.rs
+++ b/core/modules.rs
@@ -65,7 +65,7 @@ pub trait ModuleLoader {
&self,
specifier: &str,
referrer: &str,
- is_main: bool,
+ _is_main: bool,
) -> Result<ModuleSpecifier, ErrBox>;
/// Given ModuleSpecifier, load its source code.
@@ -98,6 +98,31 @@ pub trait ModuleLoader {
}
}
+/// Placeholder structure used when creating
+/// isolate that doesn't support module loading.
+pub(crate) struct NoopModuleLoader;
+
+impl ModuleLoader for NoopModuleLoader {
+ fn resolve(
+ &self,
+ _specifier: &str,
+ _referrer: &str,
+ _is_main: bool,
+ ) -> Result<ModuleSpecifier, ErrBox> {
+ Err(ErrBox::error("Module loading is not supported"))
+ }
+
+ fn load(
+ &self,
+ _module_specifier: &ModuleSpecifier,
+ _maybe_referrer: Option<ModuleSpecifier>,
+ _is_dyn_import: bool,
+ ) -> Pin<Box<ModuleSourceFuture>> {
+ async { Err(ErrBox::error("Module loading is not supported")) }
+ .boxed_local()
+ }
+}
+
#[derive(Debug, Eq, PartialEq)]
enum Kind {
Main,
@@ -520,9 +545,9 @@ impl fmt::Display for Deps {
#[cfg(test)]
mod tests {
use super::*;
- use crate::es_isolate::EsIsolate;
use crate::js_check;
use crate::BasicState;
+ use crate::CoreIsolate;
use crate::StartupData;
use futures::future::FutureExt;
use std::error::Error;
@@ -700,8 +725,12 @@ mod tests {
fn test_recursive_load() {
let loader = MockLoader::new();
let loads = loader.loads.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
let spec = ModuleSpecifier::resolve_url("file:///a.js").unwrap();
let a_id_fut = isolate.load_module(&spec, None);
let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load");
@@ -718,7 +747,7 @@ mod tests {
]
);
- let state_rc = EsIsolate::state(&isolate);
+ let state_rc = CoreIsolate::state(&isolate);
let state = state_rc.borrow();
let modules = &state.modules;
assert_eq!(modules.get_id("file:///a.js"), Some(a_id));
@@ -763,8 +792,12 @@ mod tests {
fn test_circular_load() {
let loader = MockLoader::new();
let loads = loader.loads.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
let fut = async move {
let spec = ModuleSpecifier::resolve_url("file:///circular1.js").unwrap();
@@ -783,7 +816,7 @@ mod tests {
]
);
- let state_rc = EsIsolate::state(&isolate);
+ let state_rc = CoreIsolate::state(&isolate);
let state = state_rc.borrow();
let modules = &state.modules;
@@ -837,8 +870,12 @@ mod tests {
fn test_redirect_load() {
let loader = MockLoader::new();
let loads = loader.loads.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
let fut = async move {
let spec = ModuleSpecifier::resolve_url("file:///redirect1.js").unwrap();
@@ -857,7 +894,7 @@ mod tests {
]
);
- let state_rc = EsIsolate::state(&isolate);
+ let state_rc = CoreIsolate::state(&isolate);
let state = state_rc.borrow();
let modules = &state.modules;
@@ -902,8 +939,12 @@ mod tests {
run_in_task(|mut cx| {
let loader = MockLoader::new();
let loads = loader.loads.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
let spec = ModuleSpecifier::resolve_url("file:///main.js").unwrap();
let mut recursive_load = isolate.load_module(&spec, None).boxed_local();
@@ -948,8 +989,12 @@ mod tests {
fn loader_disappears_after_error() {
run_in_task(|mut cx| {
let loader = MockLoader::new();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
let spec = ModuleSpecifier::resolve_url("file:///bad_import.js").unwrap();
let mut load_fut = isolate.load_module(&spec, None).boxed_local();
let result = load_fut.poll_unpin(&mut cx);
@@ -977,8 +1022,12 @@ mod tests {
fn recursive_load_main_with_code() {
let loader = MockLoader::new();
let loads = loader.loads.clone();
- let mut isolate =
- EsIsolate::new(loader, BasicState::new(), StartupData::None, false);
+ let mut isolate = CoreIsolate::new_with_loader(
+ loader,
+ BasicState::new(),
+ StartupData::None,
+ false,
+ );
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
@@ -998,7 +1047,7 @@ mod tests {
vec!["file:///b.js", "file:///c.js", "file:///d.js"]
);
- let state_rc = EsIsolate::state(&isolate);
+ let state_rc = CoreIsolate::state(&isolate);
let state = state_rc.borrow();
let modules = &state.modules;