diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2020-01-08 15:06:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-08 15:06:04 +0100 |
commit | cbdf9c50095b86e72a8e0e715a02f6eb327f7c53 (patch) | |
tree | e663acae0cbb3410090b2ac087a28ce57987a606 | |
parent | 8466460311ce3522d598def7155144b082b2b9ad (diff) |
refactor: module loading in EsIsolate (#3615)
* refactored RecursiveLoad - it was renamed to RecursiveModuleLoad, it does not take ownership of isolate anymore - a struct implementing Stream that yields SourceCodeInfo
* untangled module loading logic between RecursiveLoad and isolate - that logic is encapsulated in EsIsolate and RecursiveModuleLoad, where isolate just consumes modules as they become available - does not require to pass Arc<Mutex<Isolate>> around anymore
* removed EsIsolate.mods_ in favor of Modules and moved them inside EsIsolate
* EsIsolate now requires "loader" argument during construction - struct that implements Loader trait
* rewrite first methods on isolate as async
-rw-r--r-- | cli/js.rs | 4 | ||||
-rw-r--r-- | cli/lib.rs | 4 | ||||
-rw-r--r-- | cli/worker.rs | 63 | ||||
-rw-r--r-- | core/bindings.rs | 20 | ||||
-rw-r--r-- | core/es_isolate.rs | 716 | ||||
-rw-r--r-- | core/modules.rs | 754 | ||||
-rw-r--r-- | deno_typescript/lib.rs | 12 |
7 files changed, 689 insertions, 884 deletions
@@ -28,7 +28,7 @@ pub fn get_asset(name: &str) -> Option<&'static str> { #[test] fn cli_snapshot() { - let mut isolate = deno_core::EsIsolate::new( + let mut isolate = deno_core::Isolate::new( deno_core::StartupData::Snapshot(CLI_SNAPSHOT), false, ); @@ -45,7 +45,7 @@ fn cli_snapshot() { #[test] fn compiler_snapshot() { - let mut isolate = deno_core::EsIsolate::new( + let mut isolate = deno_core::Isolate::new( deno_core::StartupData::Snapshot(COMPILER_SNAPSHOT), false, ); diff --git a/cli/lib.rs b/cli/lib.rs index f0df3f149..6882fea20 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -172,7 +172,6 @@ fn print_cache_info(worker: Worker) { async fn print_file_info(worker: Worker, module_specifier: ModuleSpecifier) { let global_state_ = &worker.state.global_state; - let state_ = &worker.state; let maybe_source_file = global_state_ .file_fetcher @@ -233,7 +232,8 @@ async fn print_file_info(worker: Worker, module_specifier: ModuleSpecifier) { ); } - if let Some(deps) = state_.modules.lock().unwrap().deps(&compiled.name) { + let isolate = worker.isolate.try_lock().unwrap(); + if let Some(deps) = isolate.modules.deps(&compiled.name) { println!("{}{}", colors::bold("deps:\n".to_string()), deps.name); if let Some(ref depsdeps) = deps.deps { for d in depsdeps { diff --git a/cli/worker.rs b/cli/worker.rs index cd8d6c8ae..2b335127f 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -6,7 +6,6 @@ use deno_core; use deno_core::Buf; use deno_core::ErrBox; use deno_core::ModuleSpecifier; -use deno_core::RecursiveLoad; use deno_core::StartupData; use futures::channel::mpsc; use futures::future::FutureExt; @@ -20,6 +19,7 @@ use std::sync::Arc; use std::sync::Mutex; use std::task::Context; use std::task::Poll; +use tokio::sync::Mutex as AsyncMutex; use url::Url; /// Wraps mpsc channels so they can be referenced @@ -35,7 +35,7 @@ pub struct WorkerChannels { #[derive(Clone)] pub struct Worker { pub name: String, - isolate: Arc<Mutex<Box<deno_core::EsIsolate>>>, + pub isolate: Arc<AsyncMutex<Box<deno_core::EsIsolate>>>, pub state: ThreadSafeState, external_channels: Arc<Mutex<WorkerChannels>>, } @@ -47,10 +47,13 @@ impl Worker { state: ThreadSafeState, external_channels: WorkerChannels, ) -> Self { - let isolate = - Arc::new(Mutex::new(deno_core::EsIsolate::new(startup_data, false))); + let isolate = Arc::new(AsyncMutex::new(deno_core::EsIsolate::new( + Box::new(state.clone()), + startup_data, + false, + ))); { - let mut i = isolate.lock().unwrap(); + let mut i = isolate.try_lock().unwrap(); let op_registry = i.op_registry.clone(); ops::compiler::init(&mut i, &state); @@ -71,18 +74,6 @@ impl Worker { ops::timers::init(&mut i, &state); ops::workers::init(&mut i, &state); - let state_ = state.clone(); - i.set_dyn_import(move |id, specifier, referrer| { - let load_stream = RecursiveLoad::dynamic_import( - id, - specifier, - referrer, - state_.clone(), - state_.modules.clone(), - ); - Box::new(load_stream) - }); - let global_state_ = state.global_state.clone(); i.set_js_error_create(move |v8_exception| { JSError::from_v8_exception(v8_exception, &global_state_.ts_compiler) @@ -101,7 +92,7 @@ impl Worker { &mut self, handler: Box<dyn FnMut(ErrBox) -> Result<(), ErrBox>>, ) { - let mut i = self.isolate.lock().unwrap(); + let mut i = self.isolate.try_lock().unwrap(); i.set_error_handler(handler); } @@ -119,39 +110,31 @@ impl Worker { js_filename: &str, js_source: &str, ) -> Result<(), ErrBox> { - let mut isolate = self.isolate.lock().unwrap(); + let mut isolate = self.isolate.try_lock().unwrap(); isolate.execute(js_filename, js_source) } /// Executes the provided JavaScript module. - pub fn execute_mod_async( + /// + /// Takes ownership of the isolate behind mutex. + pub async fn execute_mod_async( &mut self, module_specifier: &ModuleSpecifier, maybe_code: Option<String>, is_prefetch: bool, - ) -> impl Future<Output = Result<(), ErrBox>> { + ) -> Result<(), ErrBox> { + let specifier = module_specifier.to_string(); let worker = self.clone(); - let loader = self.state.clone(); - let isolate = self.isolate.clone(); - let modules = self.state.modules.clone(); - let recursive_load = RecursiveLoad::main( - &module_specifier.to_string(), - maybe_code, - loader, - modules, - ); - async move { - let id = recursive_load.get_future(isolate).await?; - worker.state.global_state.progress.done(); - - if !is_prefetch { - let mut isolate = worker.isolate.lock().unwrap(); - return isolate.mod_evaluate(id); - } + let mut isolate = self.isolate.lock().await; + let id = isolate.load_module(&specifier, maybe_code).await?; + worker.state.global_state.progress.done(); - Ok(()) + if !is_prefetch { + return isolate.mod_evaluate(id); } + + Ok(()) } /// Post message to worker as a host. @@ -183,7 +166,7 @@ impl Future for Worker { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { let inner = self.get_mut(); - let mut isolate = inner.isolate.lock().unwrap(); + let mut isolate = inner.isolate.try_lock().unwrap(); isolate.poll_unpin(cx) } } diff --git a/core/bindings.rs b/core/bindings.rs index ee78cf01d..72b51814a 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -1,7 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::es_isolate::EsIsolate; -use crate::es_isolate::ResolveContext; use crate::isolate::DenoBuf; use crate::isolate::Isolate; use crate::isolate::PinnedBuf; @@ -283,7 +282,7 @@ pub extern "C" fn host_initialize_import_meta_object_callback( let id = module.get_identity_hash(); assert_ne!(id, 0); - let info = deno_isolate.get_module_info(id).expect("Module not found"); + let info = deno_isolate.modules.get_info(id).expect("Module not found"); meta.create_data_property( context, @@ -713,9 +712,12 @@ pub fn module_resolve_callback( let scope = hs.enter(); let referrer_id = referrer.get_identity_hash(); - let referrer_info = deno_isolate - .get_module_info(referrer_id) - .expect("ModuleInfo not found"); + let referrer_name = deno_isolate + .modules + .get_info(referrer_id) + .expect("ModuleInfo not found") + .name + .to_string(); let len_ = referrer.get_module_requests_length(); let specifier_str = specifier.to_rust_string_lossy(scope); @@ -725,15 +727,13 @@ pub fn module_resolve_callback( let req_str = req.to_rust_string_lossy(scope); if req_str == specifier_str { - let ResolveContext { resolve_fn } = - unsafe { ResolveContext::from_raw_ptr(deno_isolate.resolve_context) }; - let id = resolve_fn(&req_str, referrer_id); - let maybe_info = deno_isolate.get_module_info(id); + let id = deno_isolate.module_resolve_cb(&req_str, referrer_id); + let maybe_info = deno_isolate.modules.get_info(id); if maybe_info.is_none() { let msg = format!( "Cannot resolve module \"{}\" from \"{}\"", - req_str, referrer_info.name + req_str, referrer_name ); let msg = v8::String::new(scope, &msg).unwrap(); isolate.throw_exception(msg.into()); diff --git a/core/es_isolate.rs b/core/es_isolate.rs index 0e9bb5b5b..655b42a06 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -1,9 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -// Do not add any dependency to modules.rs! -// modules.rs is complex and should remain decoupled from isolate.rs to keep the -// Isolate struct from becoming too bloating for users who do not need -// asynchronous module loading. +// This module provides higher level implementation of Isolate that +// supports asynchronous loading and executution of ES Modules. +// The isolate.rs should never depend on this module. use rusty_v8 as v8; @@ -13,16 +12,11 @@ use futures::future::Future; use futures::future::FutureExt; use futures::ready; use futures::stream::FuturesUnordered; -use futures::stream::IntoStream; -use futures::stream::Stream; use futures::stream::StreamExt; use futures::stream::StreamFuture; -use futures::stream::TryStream; -use futures::stream::TryStreamExt; use futures::task::AtomicWaker; use libc::c_void; use std::collections::HashMap; -use std::fmt; use std::ops::{Deref, DerefMut}; use std::option::Option; use std::pin::Pin; @@ -32,6 +26,11 @@ use std::task::Poll; use crate::isolate::Isolate; use crate::isolate::StartupData; +use crate::module_specifier::ModuleSpecifier; +use crate::modules::LoadState; +use crate::modules::Loader; +use crate::modules::Modules; +use crate::modules::RecursiveModuleLoad; pub type ModuleId = i32; pub type DynImportId = i32; @@ -48,78 +47,6 @@ pub struct SourceCodeInfo { pub module_url_found: String, } -#[derive(Debug, Eq, PartialEq)] -pub enum RecursiveLoadEvent { - Fetch(SourceCodeInfo), - Instantiate(ModuleId), -} - -pub trait ImportStream: TryStream { - fn register( - &mut self, - source_code_info: SourceCodeInfo, - isolate: &mut EsIsolate, - ) -> Result<(), ErrBox>; -} - -type DynImportStream = Box< - dyn ImportStream< - Ok = RecursiveLoadEvent, - Error = ErrBox, - Item = Result<RecursiveLoadEvent, ErrBox>, - > + Send - + Unpin, ->; - -type DynImportFn = dyn Fn(DynImportId, &str, &str) -> DynImportStream; - -/// Wraps DynImportStream to include the DynImportId, so that it doesn't -/// need to be exposed. -#[derive(Debug)] -struct DynImport { - pub id: DynImportId, - pub inner: DynImportStream, -} - -impl fmt::Debug for DynImportStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DynImportStream(..)") - } -} - -impl Stream for DynImport { - type Item = Result<(DynImportId, RecursiveLoadEvent), (DynImportId, ErrBox)>; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context, - ) -> Poll<Option<Self::Item>> { - let self_inner = self.get_mut(); - let result = ready!(self_inner.inner.try_poll_next_unpin(cx)).unwrap(); - match result { - Ok(event) => Poll::Ready(Some(Ok((self_inner.id, event)))), - Err(e) => Poll::Ready(Some(Err((self_inner.id, e)))), - } - } -} - -impl ImportStream for DynImport { - fn register( - &mut self, - source_code_info: SourceCodeInfo, - isolate: &mut EsIsolate, - ) -> Result<(), ErrBox> { - self.inner.register(source_code_info, isolate) - } -} - -pub struct ModuleInfo { - pub main: bool, - pub name: String, - pub handle: v8::Global<v8::Module>, - pub import_specifiers: Vec<String>, -} - /// A single execution context of JavaScript. Corresponds roughly to the "Web /// Worker" concept in the DOM. An Isolate is a Future that can be used with /// Tokio. The Isolate future complete when there is an error or when all @@ -130,15 +57,13 @@ pub struct ModuleInfo { /// as arguments. An async Op corresponds exactly to a Promise in JavaScript. pub struct EsIsolate { core_isolate: Box<Isolate>, - - mods_: HashMap<ModuleId, ModuleInfo>, + loader: Arc<Box<dyn Loader + Unpin>>, + pub modules: Modules, pub(crate) next_dyn_import_id: DynImportId, pub(crate) dyn_import_map: HashMap<DynImportId, v8::Global<v8::PromiseResolver>>, - pub(crate) resolve_context: *mut c_void, - pending_dyn_imports: FuturesUnordered<StreamFuture<IntoStream<DynImport>>>, - dyn_import: Option<Arc<DynImportFn>>, + pending_dyn_imports: FuturesUnordered<StreamFuture<RecursiveModuleLoad>>, waker: AtomicWaker, } @@ -166,7 +91,7 @@ impl Drop for EsIsolate { let mut locker = v8::Locker::new(&isolate); let mut hs = v8::HandleScope::new(&mut locker); let scope = hs.enter(); - for module in self.mods_.values_mut() { + for module in self.modules.info.values_mut() { module.handle.reset(scope); } for handle in self.dyn_import_map.values_mut() { @@ -176,30 +101,12 @@ impl Drop for EsIsolate { } } -/// Called during mod_instantiate() to resolve imports. -type ResolveFn<'a> = dyn FnMut(&str, ModuleId) -> ModuleId + 'a; - -/// Used internally by Isolate::mod_instantiate to wrap ResolveFn and -/// encapsulate pointer casts. -pub struct ResolveContext<'a> { - pub resolve_fn: &'a mut ResolveFn<'a>, -} - -impl<'a> ResolveContext<'a> { - #[inline] - fn as_raw_ptr(&mut self) -> *mut c_void { - self as *mut _ as *mut c_void - } - - #[allow(clippy::missing_safety_doc)] - #[inline] - pub(crate) unsafe fn from_raw_ptr(ptr: *mut c_void) -> &'a mut Self { - &mut *(ptr as *mut _) - } -} - impl EsIsolate { - pub fn new(startup_data: StartupData, will_snapshot: bool) -> Box<Self> { + pub fn new( + loader: Box<dyn Loader + Unpin>, + startup_data: StartupData, + will_snapshot: bool, + ) -> Box<Self> { let mut core_isolate = Isolate::new(startup_data, will_snapshot); { let isolate = core_isolate.v8_isolate.as_mut().unwrap(); @@ -212,12 +119,11 @@ impl EsIsolate { } let es_isolate = Self { + modules: Modules::new(), + loader: Arc::new(loader), core_isolate, - mods_: HashMap::new(), next_dyn_import_id: 0, dyn_import_map: HashMap::new(), - resolve_context: std::ptr::null_mut(), - dyn_import: None, pending_dyn_imports: FuturesUnordered::new(), waker: AtomicWaker::new(), }; @@ -276,15 +182,9 @@ impl EsIsolate { let mut handle = v8::Global::<v8::Module>::new(); handle.set(scope, module); - self.mods_.insert( - id, - ModuleInfo { - main, - name: name.to_string(), - import_specifiers, - handle, - }, - ); + self + .modules + .register(id, name, main, handle, import_specifiers); context.exit(); id } @@ -301,19 +201,18 @@ impl EsIsolate { } pub fn mod_get_imports(&self, id: ModuleId) -> Vec<String> { - let info = self.get_module_info(id).unwrap(); + let info = self.modules.get_info(id).unwrap(); let len = info.import_specifiers.len(); let mut out = Vec::new(); for i in 0..len { - let info = self.get_module_info(id).unwrap(); + let info = self.modules.get_info(id).unwrap(); let specifier = info.import_specifiers.get(i).unwrap().to_string(); out.push(specifier); } out } - fn mod_instantiate2(&mut self, mut ctx: ResolveContext<'_>, id: ModuleId) { - self.resolve_context = ctx.as_raw_ptr(); + fn mod_instantiate2(&mut self, id: ModuleId) { let isolate = self.core_isolate.v8_isolate.as_ref().unwrap(); let mut locker = v8::Locker::new(isolate); let mut hs = v8::HandleScope::new(&mut locker); @@ -324,9 +223,10 @@ impl EsIsolate { let mut try_catch = v8::TryCatch::new(scope); let tc = try_catch.enter(); - let maybe_info = self.get_module_info(id); + let maybe_info = self.modules.get_info(id); if maybe_info.is_none() { + context.exit(); return; } @@ -334,6 +234,7 @@ impl EsIsolate { let mut module = module_handle.get(scope).unwrap(); if module.get_status() == v8::ModuleStatus::Errored { + context.exit(); return; } @@ -350,30 +251,19 @@ impl EsIsolate { } context.exit(); - self.resolve_context = std::ptr::null_mut(); } + /// Instanciates a ES module /// /// ErrBox can be downcast to a type that exposes additional information about /// the V8 exception. By default this type is CoreJSError, however it may be a /// different type if Isolate::set_js_error_create() has been used. - pub fn mod_instantiate( - &mut self, - id: ModuleId, - resolve_fn: &mut ResolveFn, - ) -> Result<(), ErrBox> { - let ctx = ResolveContext { resolve_fn }; - self.mod_instantiate2(ctx, id); + fn mod_instantiate(&mut self, id: ModuleId) -> Result<(), ErrBox> { + self.mod_instantiate2(id); self.core_isolate.check_last_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 CoreJSError, however it may be a - /// different type if Isolate::set_js_error_create() has been used. - pub fn mod_evaluate(&mut self, id: ModuleId) -> Result<(), ErrBox> { - self.shared_init(); + fn mod_evaluate2(&mut self, id: ModuleId) { let isolate = self.core_isolate.v8_isolate.as_ref().unwrap(); let mut locker = v8::Locker::new(isolate); let mut hs = v8::HandleScope::new(&mut locker); @@ -382,7 +272,7 @@ impl EsIsolate { let mut context = self.core_isolate.global_context.get(scope).unwrap(); context.enter(); - let info = self.get_module_info(id).expect("ModuleInfo not found"); + let info = self.modules.get_info(id).expect("ModuleInfo not found"); let mut module = info.handle.get(scope).expect("Empty module handle"); let mut status = module.get_status(); @@ -416,22 +306,32 @@ impl EsIsolate { }; context.exit(); - - self.core_isolate.check_last_exception() } - pub fn get_module_info(&self, id: ModuleId) -> Option<&ModuleInfo> { - if id == 0 { - return None; - } - self.mods_.get(&id) + /// 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 CoreJSError, however it may be a + /// different type if Isolate::set_js_error_create() has been used. + pub fn mod_evaluate(&mut self, id: ModuleId) -> Result<(), ErrBox> { + self.shared_init(); + self.mod_evaluate2(id); + self.core_isolate.check_last_exception() } - pub fn set_dyn_import<F>(&mut self, f: F) - where - F: Fn(DynImportId, &str, &str) -> DynImportStream + Send + Sync + 'static, - { - self.dyn_import = Some(Arc::new(f)); + pub fn module_resolve_cb( + &mut self, + specifier: &str, + referrer_id: ModuleId, + ) -> ModuleId { + let referrer = self.modules.get_name(referrer_id).unwrap(); + // We should have already resolved and Ready this module, so + // resolve() will not fail this time. + let specifier = self + .modules + .get_cached_specifier(specifier, &referrer) + .expect("Module should already be resolved"); + self.modules.get_id(specifier.as_str()).unwrap_or(0) } pub fn dyn_import_cb( @@ -442,16 +342,14 @@ impl EsIsolate { ) { debug!("dyn_import specifier {} referrer {} ", specifier, referrer); - if let Some(ref f) = self.dyn_import { - let inner = f(id, specifier, referrer); - let stream = DynImport { inner, id }; - self.waker.wake(); - self - .pending_dyn_imports - .push(stream.into_stream().into_future()); - } else { - panic!("dyn_import callback not set") - } + let load = RecursiveModuleLoad::dynamic_import( + id, + specifier, + referrer, + self.loader.clone(), + ); + self.waker.wake(); + self.pending_dyn_imports.push(load.into_future()); } fn dyn_import_done( @@ -486,7 +384,7 @@ impl EsIsolate { let mut resolver = resolver_handle.get(scope).unwrap(); resolver_handle.reset(scope); - let maybe_info = self.get_module_info(mod_id); + let maybe_info = self.modules.get_info(mod_id); if let Some(info) = maybe_info { // Resolution success @@ -518,47 +416,151 @@ impl EsIsolate { } fn poll_dyn_imports(&mut self, cx: &mut Context) -> Poll<Result<(), ErrBox>> { - use RecursiveLoadEvent::*; loop { match self.pending_dyn_imports.poll_next_unpin(cx) { Poll::Pending | Poll::Ready(None) => { // There are no active dynamic import loaders, or none are ready. return Poll::Ready(Ok(())); } - Poll::Ready(Some(( - Some(Ok((dyn_import_id, Fetch(source_code_info)))), - mut stream, - ))) => { - // 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 stream.get_mut().register(source_code_info, self) { - Ok(()) => self.pending_dyn_imports.push(stream.into_future()), - Err(err) => { - self.dyn_import_done(dyn_import_id, Err(Some(err.to_string())))? + 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.dyn_import_id.unwrap(); + + 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 + self.pending_dyn_imports.push(load.into_future()); + } + Err(err) => self.dyn_import_done( + dyn_import_id, + Err(Some(err.to_string())), + )?, + } + } + 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_done(dyn_import_id, Err(Some(err.to_string())))? + } + } + } 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)?; + if let Ok(()) = self.mod_evaluate(module_id) { + self.dyn_import_done(dyn_import_id, Ok(module_id))? + } else { + self.dyn_import_done(dyn_import_id, Err(None))? } } } - Poll::Ready(Some(( - Some(Ok((dyn_import_id, Instantiate(module_id)))), - _, - ))) => { - // The top-level module from a dynamic import has been instantiated. - match self.mod_evaluate(module_id) { - Ok(()) => self.dyn_import_done(dyn_import_id, Ok(module_id))?, - Err(..) => self.dyn_import_done(dyn_import_id, Err(None))?, - } - } - Poll::Ready(Some((Some(Err((dyn_import_id, 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_done(dyn_import_id, Err(Some(err.to_string())))? - } - Poll::Ready(Some((None, _))) => unreachable!(), } } } + + fn register_during_load( + &mut self, + info: SourceCodeInfo, + load: &mut RecursiveModuleLoad, + ) -> Result<(), ErrBox> { + let SourceCodeInfo { + code, + module_url_specified, + module_url_found, + } = info; + + let is_main = + load.state == LoadState::LoadingRoot && !load.is_dynamic_import(); + let referrer_name = &module_url_found.to_string(); + let referrer_specifier = + ModuleSpecifier::resolve_url(referrer_name).unwrap(); + + // #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 registerd + // -> register & alias + + // If necessary, register an alias. + if module_url_specified != module_url_found { + self.modules.alias(&module_url_specified, &module_url_found); + } + + let module_id = match self.modules.get_id(&module_url_found) { + 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 = self.mod_get_imports(module_id); + for import in imports { + let module_specifier = self.loader.resolve( + &import, + referrer_name, + false, + load.is_dynamic_import(), + )?; + self + .modules + .cache_specifier(&import, referrer_name, &module_specifier); + let module_name = module_specifier.as_str(); + + if !self.modules.is_registered(module_name) { + load.add_import(module_specifier, 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(()) + } + + pub async fn load_module( + &mut self, + specifier: &str, + code: Option<String>, + ) -> Result<ModuleId, ErrBox> { + let mut load = + RecursiveModuleLoad::main(specifier, code, self.loader.clone()); + + 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) + } } impl Future for EsIsolate { @@ -594,16 +596,48 @@ pub mod tests { use crate::isolate::js_check; use crate::isolate::tests::run_in_task; use crate::isolate::PinnedBuf; + use crate::modules::SourceCodeInfoFuture; use crate::ops::*; use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::Mutex; - pub fn setup() -> (Box<EsIsolate>, Arc<AtomicUsize>) { + #[test] + fn test_mods() { + #[derive(Clone, Default)] + struct ModsLoader { + pub count: Arc<AtomicUsize>, + } + + impl Loader for ModsLoader { + fn resolve( + &self, + specifier: &str, + referrer: &str, + _is_main: bool, + _is_dyn_import: 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>, + ) -> Pin<Box<SourceCodeInfoFuture>> { + unreachable!() + } + } + + let loader = Box::new(ModsLoader::default()); + let resolve_count = loader.count.clone(); let dispatch_count = Arc::new(AtomicUsize::new(0)); let dispatch_count_ = dispatch_count.clone(); - let mut isolate = EsIsolate::new(StartupData::None, false); + let mut isolate = EsIsolate::new(loader, StartupData::None, false); let dispatcher = move |control: &[u8], _zero_copy: Option<PinnedBuf>| -> CoreOp { @@ -626,19 +660,16 @@ pub mod tests { } "#, )); + assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - (isolate, dispatch_count) - } - #[test] - fn test_mods() { - let (mut isolate, dispatch_count) = setup(); + let specifier_a = "file:///a.js".to_string(); let mod_a = isolate .mod_new( true, - "a.js", + &specifier_a, r#" - import { b } from 'b.js' + import { b } from './b.js' if (b() != 'b') throw Error(); let control = new Uint8Array([42]); Deno.core.send(1, control); @@ -648,223 +679,231 @@ pub mod tests { assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); let imports = isolate.mod_get_imports(mod_a); - assert_eq!(imports, vec!["b.js".to_string()]); + let specifier_b = "./b.js".to_string(); + assert_eq!(imports, vec![specifier_b.clone()]); let mod_b = isolate - .mod_new(false, "b.js", "export function b() { return 'b' }") + .mod_new(false, "file:///b.js", "export function b() { return 'b' }") .unwrap(); let imports = isolate.mod_get_imports(mod_b); assert_eq!(imports.len(), 0); - let resolve_count = Arc::new(AtomicUsize::new(0)); - let resolve_count_ = resolve_count.clone(); - - let mut resolve = move |specifier: &str, _referrer: ModuleId| -> ModuleId { - resolve_count_.fetch_add(1, Ordering::SeqCst); - assert_eq!(specifier, "b.js"); - mod_b - }; - - js_check(isolate.mod_instantiate(mod_b, &mut resolve)); + let module_specifier = + ModuleSpecifier::resolve_import(&specifier_b, &specifier_a).unwrap(); + isolate.modules.cache_specifier( + &specifier_b, + &specifier_a, + &module_specifier, + ); + js_check(isolate.mod_instantiate(mod_b)); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); assert_eq!(resolve_count.load(Ordering::SeqCst), 0); - js_check(isolate.mod_instantiate(mod_a, &mut resolve)); + js_check(isolate.mod_instantiate(mod_a)); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - assert_eq!(resolve_count.load(Ordering::SeqCst), 1); js_check(isolate.mod_evaluate(mod_a)); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert_eq!(resolve_count.load(Ordering::SeqCst), 1); } - struct MockImportStream(Vec<Result<RecursiveLoadEvent, ErrBox>>); - - impl Stream for MockImportStream { - type Item = Result<RecursiveLoadEvent, ErrBox>; - - fn poll_next( - self: Pin<&mut Self>, - _cx: &mut Context, - ) -> Poll<Option<Self::Item>> { - let inner = self.get_mut(); - let event = if inner.0.is_empty() { - None - } else { - Some(inner.0.remove(0)) - }; - Poll::Ready(event) + #[test] + fn dyn_import_err() { + #[derive(Clone, Default)] + struct DynImportErrLoader { + pub count: Arc<AtomicUsize>, } - } - impl ImportStream for MockImportStream { - fn register( - &mut self, - module_data: SourceCodeInfo, - isolate: &mut EsIsolate, - ) -> Result<(), ErrBox> { - let id = isolate.mod_new( - false, - &module_data.module_url_found, - &module_data.code, - )?; - println!( - "MockImportStream register {} {}", - id, module_data.module_url_found - ); - Ok(()) + impl Loader for DynImportErrLoader { + fn resolve( + &self, + specifier: &str, + referrer: &str, + _is_main: bool, + _is_dyn_import: 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>, + ) -> Pin<Box<SourceCodeInfoFuture>> { + async { Err(ErrBox::from(io::Error::from(io::ErrorKind::NotFound))) } + .boxed() + } } - } - #[test] - fn dyn_import_err() { // Test an erroneous dynamic import where the specified module isn't found. run_in_task(|cx| { - let count = Arc::new(AtomicUsize::new(0)); - let count_ = count.clone(); - let mut isolate = EsIsolate::new(StartupData::None, false); - isolate.set_dyn_import(move |_, specifier, referrer| { - count_.fetch_add(1, Ordering::Relaxed); - assert_eq!(specifier, "foo.js"); - assert_eq!(referrer, "dyn_import2.js"); - let err = io::Error::from(io::ErrorKind::NotFound); - let stream = MockImportStream(vec![Err(err.into())]); - Box::new(stream) - }); + let loader = Box::new(DynImportErrLoader::default()); + let count = loader.count.clone(); + let mut isolate = EsIsolate::new(loader, StartupData::None, false); + js_check(isolate.execute( - "dyn_import2.js", + "file:///dyn_import2.js", r#" (async () => { - await import("foo.js"); + await import("/foo.js"); })(); "#, )); - assert_eq!(count.load(Ordering::Relaxed), 1); + 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), 1); }) } #[test] fn dyn_import_err2() { - use std::convert::TryInto; - // Import multiple modules to demonstrate that after failed dynamic import - // another dynamic import can still be run - run_in_task(|cx| { - let count = Arc::new(AtomicUsize::new(0)); - let count_ = count.clone(); - let mut isolate = EsIsolate::new(StartupData::None, false); - isolate.set_dyn_import(move |_, specifier, referrer| { - let c = count_.fetch_add(1, Ordering::Relaxed); + #[derive(Clone, Default)] + struct DynImportErr2Loader { + pub count: Arc<AtomicUsize>, + } + + impl Loader for DynImportErr2Loader { + fn resolve( + &self, + specifier: &str, + referrer: &str, + _is_main: bool, + _is_dyn_import: bool, + ) -> Result<ModuleSpecifier, ErrBox> { + let c = self.count.fetch_add(1, Ordering::Relaxed); match c { - 0 => assert_eq!(specifier, "foo1.js"), - 1 => assert_eq!(specifier, "foo2.js"), - 2 => assert_eq!(specifier, "foo3.js"), + 0 => assert_eq!(specifier, "/foo1.js"), + 1 => assert_eq!(specifier, "/foo2.js"), + 2 => assert_eq!(specifier, "/foo3.js"), _ => unreachable!(), } - assert_eq!(referrer, "dyn_import_error.js"); + assert_eq!(referrer, "file:///dyn_import_error.js"); + let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + Ok(s) + } - let source_code_info = SourceCodeInfo { - module_url_specified: specifier.to_owned(), - module_url_found: specifier.to_owned(), + fn load( + &self, + specifier: &ModuleSpecifier, + _maybe_referrer: Option<ModuleSpecifier>, + ) -> Pin<Box<SourceCodeInfoFuture>> { + let info = SourceCodeInfo { + module_url_specified: specifier.to_string(), + module_url_found: specifier.to_string(), code: "# not valid JS".to_owned(), }; - let stream = MockImportStream(vec![ - Ok(RecursiveLoadEvent::Fetch(source_code_info)), - Ok(RecursiveLoadEvent::Instantiate(c.try_into().unwrap())), - ]); - Box::new(stream) - }); + async move { Ok(info) }.boxed() + } + } + + // Import multiple modules to demonstrate that after failed dynamic import + // another dynamic import can still be run + run_in_task(|cx| { + let loader = Box::new(DynImportErr2Loader::default()); + let loader1 = loader.clone(); + let mut isolate = EsIsolate::new(loader, StartupData::None, false); js_check(isolate.execute( - "dyn_import_error.js", + "file:///dyn_import_error.js", r#" (async () => { - await import("foo1.js"); + await import("/foo1.js"); })(); (async () => { - await import("foo2.js"); + await import("/foo2.js"); })(); (async () => { - await import("foo3.js"); + await import("/foo3.js"); })(); "#, )); - assert_eq!(count.load(Ordering::Relaxed), 3); + assert_eq!(loader1.count.load(Ordering::Relaxed), 0); // Now each poll should return error assert!(match isolate.poll_unpin(cx) { Poll::Ready(Err(_)) => true, _ => false, }); + assert_eq!(loader1.count.load(Ordering::Relaxed), 1); assert!(match isolate.poll_unpin(cx) { Poll::Ready(Err(_)) => true, _ => false, }); + assert_eq!(loader1.count.load(Ordering::Relaxed), 2); assert!(match isolate.poll_unpin(cx) { Poll::Ready(Err(_)) => true, _ => false, }); + assert_eq!(loader1.count.load(Ordering::Relaxed), 3); }) } #[test] fn dyn_import_ok() { - run_in_task(|cx| { - let count = Arc::new(AtomicUsize::new(0)); - let count_ = count.clone(); - - // Sometimes Rust is really annoying. - let mod_b = Arc::new(Mutex::new(0)); - let mod_b2 = mod_b.clone(); + #[derive(Clone, Default)] + struct DynImportOkLoader { + pub resolve_count: Arc<AtomicUsize>, + pub load_count: Arc<AtomicUsize>, + } - let mut isolate = EsIsolate::new(StartupData::None, false); - isolate.set_dyn_import(move |_id, specifier, referrer| { - let c = count_.fetch_add(1, Ordering::Relaxed); + impl Loader for DynImportOkLoader { + fn resolve( + &self, + specifier: &str, + referrer: &str, + _is_main: bool, + _is_dyn_import: bool, + ) -> Result<ModuleSpecifier, ErrBox> { + let c = self.resolve_count.fetch_add(1, Ordering::Relaxed); match c { - 0 => assert_eq!(specifier, "foo1.js"), - 1 => assert_eq!(specifier, "foo2.js"), + 0 => assert_eq!(specifier, "./b.js"), + 1 => assert_eq!(specifier, "./b.js"), _ => unreachable!(), } - assert_eq!(referrer, "dyn_import3.js"); - let mod_id = *mod_b2.lock().unwrap(); - let source_code_info = SourceCodeInfo { - module_url_specified: "foo.js".to_owned(), - module_url_found: "foo.js".to_owned(), - code: "".to_owned(), - }; - let stream = MockImportStream(vec![ - Ok(RecursiveLoadEvent::Fetch(source_code_info)), - Ok(RecursiveLoadEvent::Instantiate(mod_id)), - ]); - Box::new(stream) - }); + assert_eq!(referrer, "file:///dyn_import3.js"); + let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + Ok(s) + } - // Instantiate mod_b - { - let mut mod_id = mod_b.lock().unwrap(); - *mod_id = isolate - .mod_new(false, "b.js", "export function b() { return 'b' }") - .unwrap(); - let mut resolve = move |_specifier: &str, - _referrer: ModuleId| - -> ModuleId { unreachable!() }; - js_check(isolate.mod_instantiate(*mod_id, &mut resolve)); + fn load( + &self, + specifier: &ModuleSpecifier, + _maybe_referrer: Option<ModuleSpecifier>, + ) -> Pin<Box<SourceCodeInfoFuture>> { + self.load_count.fetch_add(1, Ordering::Relaxed); + let info = SourceCodeInfo { + 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() } + } + + run_in_task(|cx| { + let loader = Box::new(DynImportOkLoader::default()); + let resolve_count = loader.resolve_count.clone(); + let load_count = loader.load_count.clone(); + let mut isolate = EsIsolate::new(loader, StartupData::None, false); + // Dynamically import mod_b js_check(isolate.execute( - "dyn_import3.js", + "file:///dyn_import3.js", r#" (async () => { - let mod = await import("foo1.js"); + let mod = await import("./b.js"); if (mod.b() !== 'b') { throw Error("bad1"); } // And again! - mod = await import("foo2.js"); + mod = await import("./b.js"); if (mod.b() !== 'b') { throw Error("bad2"); } @@ -872,17 +911,18 @@ pub mod tests { "#, )); - assert_eq!(count.load(Ordering::Relaxed), 1); assert!(match isolate.poll_unpin(cx) { Poll::Ready(Ok(_)) => true, _ => false, }); - assert_eq!(count.load(Ordering::Relaxed), 2); + assert_eq!(resolve_count.load(Ordering::Relaxed), 2); + assert_eq!(load_count.load(Ordering::Relaxed), 2); assert!(match isolate.poll_unpin(cx) { Poll::Ready(Ok(_)) => true, _ => false, }); - assert_eq!(count.load(Ordering::Relaxed), 2); + assert_eq!(resolve_count.load(Ordering::Relaxed), 2); + assert_eq!(load_count.load(Ordering::Relaxed), 2); }) } } diff --git a/core/modules.rs b/core/modules.rs index 1579b3480..60158d6f1 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -1,17 +1,10 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -// Implementation note: one could imagine combining this module with Isolate to -// provide a more intuitive high-level API. However, due to the complexity -// inherent in asynchronous module loading, we would like the Isolate to remain -// small and simple for users who do not use modules or if they do can load them -// synchronously. The isolate.rs module should never depend on this module. +use rusty_v8 as v8; use crate::any_error::ErrBox; use crate::es_isolate::DynImportId; -use crate::es_isolate::EsIsolate; -use crate::es_isolate::ImportStream; use crate::es_isolate::ModuleId; -use crate::es_isolate::RecursiveLoadEvent as Event; use crate::es_isolate::SourceCodeInfo; use crate::module_specifier::ModuleSpecifier; use futures::future::FutureExt; @@ -24,7 +17,6 @@ use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use std::sync::Mutex; use std::task::Context; use std::task::Poll; @@ -55,73 +47,72 @@ pub trait Loader: Send + Sync { #[derive(Debug, Eq, PartialEq)] enum Kind { Main, - DynamicImport(DynImportId), + DynamicImport, } #[derive(Debug, Eq, PartialEq)] -enum State { - ResolveMain(String, Option<String>), // specifier, maybe code - ResolveImport(String, String), // specifier, referrer +pub enum LoadState { + ResolveMain(String, Option<String>), + ResolveImport(String, String), LoadingRoot, - LoadingImports(ModuleId), - Instantiated(ModuleId), + LoadingImports, + Done, } /// This future is used to implement parallel async module loading without -/// complicating the Isolate API. -/// TODO: RecursiveLoad desperately needs to be merged with Modules. -pub struct RecursiveLoad<L: Loader + Unpin> { +/// that is consumed by the isolate. +pub struct RecursiveModuleLoad { kind: Kind, - state: State, - loader: L, - modules: Arc<Mutex<Modules>>, - pending: FuturesUnordered<Pin<Box<SourceCodeInfoFuture>>>, - is_pending: HashSet<ModuleSpecifier>, + // Kind::Main + pub root_module_id: Option<ModuleId>, + // Kind::Main + pub dyn_import_id: Option<DynImportId>, + pub state: LoadState, + pub loader: Arc<Box<dyn Loader + Unpin>>, + pub pending: FuturesUnordered<Pin<Box<SourceCodeInfoFuture>>>, + pub is_pending: HashSet<ModuleSpecifier>, } -impl<L: Loader + Unpin> RecursiveLoad<L> { +impl RecursiveModuleLoad { /// Starts a new parallel load of the given URL of the main module. pub fn main( specifier: &str, code: Option<String>, - loader: L, - modules: Arc<Mutex<Modules>>, + loader: Arc<Box<dyn Loader + Unpin>>, ) -> Self { let kind = Kind::Main; - let state = State::ResolveMain(specifier.to_owned(), code); - Self::new(kind, state, loader, modules) + let state = LoadState::ResolveMain(specifier.to_owned(), code); + Self::new(kind, state, loader, None) } pub fn dynamic_import( id: DynImportId, specifier: &str, referrer: &str, - loader: L, - modules: Arc<Mutex<Modules>>, + loader: Arc<Box<dyn Loader + Unpin>>, ) -> Self { - let kind = Kind::DynamicImport(id); - let state = State::ResolveImport(specifier.to_owned(), referrer.to_owned()); - Self::new(kind, state, loader, modules) + let kind = Kind::DynamicImport; + let state = + LoadState::ResolveImport(specifier.to_owned(), referrer.to_owned()); + Self::new(kind, state, loader, Some(id)) } - pub fn dyn_import_id(&self) -> Option<DynImportId> { - match self.kind { - Kind::Main => None, - Kind::DynamicImport(id) => Some(id), - } + pub fn is_dynamic_import(&self) -> bool { + self.kind != Kind::Main } fn new( kind: Kind, - state: State, - loader: L, - modules: Arc<Mutex<Modules>>, + state: LoadState, + loader: Arc<Box<dyn Loader + Unpin>>, + dyn_import_id: Option<DynImportId>, ) -> Self { Self { + root_module_id: None, + dyn_import_id, kind, state, loader, - modules, pending: FuturesUnordered::new(), is_pending: HashSet::new(), } @@ -129,190 +120,49 @@ impl<L: Loader + Unpin> RecursiveLoad<L> { fn add_root(&mut self) -> Result<(), ErrBox> { let module_specifier = match self.state { - State::ResolveMain(ref specifier, _) => self.loader.resolve( - specifier, - ".", - true, - self.dyn_import_id().is_some(), - )?, - State::ResolveImport(ref specifier, ref referrer) => self - .loader - .resolve(specifier, referrer, false, self.dyn_import_id().is_some())?, - _ => unreachable!(), - }; - - // We deliberately do not check if this module is already present in the - // module map. That's because the module map doesn't track whether a - // a module's dependencies have been loaded and whether it's been - // instantiated, so if we did find this module in the module map and used - // its id, this could lead to a crash. - // - // For the time being code and metadata for a module specifier is fetched - // multiple times, register() uses only the first result, and assigns the - // same module id to all instances. - // - // TODO: this is very ugly. The module map and recursive loader should be - // integrated into one thing. - self - .pending - .push(self.loader.load(&module_specifier, None).boxed()); - self.state = State::LoadingRoot; - - Ok(()) - } - - fn add_import( - &mut self, - specifier: &str, - referrer: &str, - parent_id: ModuleId, - ) -> Result<(), ErrBox> { - let referrer_specifier = ModuleSpecifier::resolve_url(referrer) - .expect("Referrer should be a valid specifier"); - let module_specifier = self.loader.resolve( - specifier, - referrer, - false, - self.dyn_import_id().is_some(), - )?; - let module_name = module_specifier.as_str(); - - let mut modules = self.modules.lock().unwrap(); - - modules.add_child(parent_id, module_name); - - if !modules.is_registered(module_name) - && !self.is_pending.contains(&module_specifier) - { - let fut = self - .loader - .load(&module_specifier, Some(referrer_specifier)); - self.pending.push(fut.boxed()); - self.is_pending.insert(module_specifier); - } - - Ok(()) - } - - /// Returns a future that resolves to the final module id of the root module. - /// This future needs to take ownership of the isolate. - pub fn get_future( - self, - isolate: Arc<Mutex<Box<EsIsolate>>>, - ) -> impl Future<Output = Result<ModuleId, ErrBox>> { - async move { - let mut load = self; - loop { - let event = load.try_next().await?; - match event.unwrap() { - Event::Fetch(info) => { - let mut isolate = isolate.lock().unwrap(); - load.register(info, &mut isolate)?; - } - Event::Instantiate(id) => return Ok(id), - } + LoadState::ResolveMain(ref specifier, _) => { + self.loader.resolve(specifier, ".", true, false)? } - } - } -} - -impl<L: Loader + Unpin> ImportStream for RecursiveLoad<L> { - // TODO: this should not be part of RecursiveLoad. - fn register( - &mut self, - source_code_info: SourceCodeInfo, - isolate: &mut EsIsolate, - ) -> Result<(), ErrBox> { - // #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 registerd - // -> register & alias - let SourceCodeInfo { - code, - module_url_specified, - module_url_found, - } = source_code_info; - - let is_main = self.kind == Kind::Main && self.state == State::LoadingRoot; - - let module_id = { - let mut modules = self.modules.lock().unwrap(); - - // If necessary, register an alias. - if module_url_specified != module_url_found { - modules.alias(&module_url_specified, &module_url_found); + LoadState::ResolveImport(ref specifier, ref referrer) => { + self.loader.resolve(specifier, referrer, false, true)? } - match modules.get_id(&module_url_found) { - // Module has already been registered. - Some(id) => { - debug!( - "Already-registered module fetched again: {}", - module_url_found - ); - id - } - // Module not registered yet, do it now. - None => { - let id = isolate.mod_new(is_main, &module_url_found, &code)?; - modules.register(id, &module_url_found); - id - } - } + _ => unreachable!(), }; - // Now we must iterate over all imports of the module and load them. - let imports = isolate.mod_get_imports(module_id); - for import in imports { - self.add_import(&import, &module_url_found, module_id)?; - } - - // If we just finished loading the root module, store the root module id. - match self.state { - State::LoadingRoot => self.state = State::LoadingImports(module_id), - State::LoadingImports(..) => {} - _ => unreachable!(), + let load_fut = match &self.state { + LoadState::ResolveMain(_, Some(code)) => { + futures::future::ok(SourceCodeInfo { + code: code.to_owned(), + module_url_specified: module_specifier.to_string(), + module_url_found: module_specifier.to_string(), + }) + .boxed() + } + _ => self.loader.load(&module_specifier, None).boxed(), }; - // If all imports have been loaded, instantiate the root module. - if self.pending.is_empty() { - let root_id = match self.state { - State::LoadingImports(mod_id) => mod_id, - _ => unreachable!(), - }; + self.pending.push(load_fut); - let mut resolve_cb = - |specifier: &str, referrer_id: ModuleId| -> ModuleId { - let modules = self.modules.lock().unwrap(); - let referrer = modules.get_name(referrer_id).unwrap(); - match self.loader.resolve( - specifier, - &referrer, - is_main, - self.dyn_import_id().is_some(), - ) { - Ok(specifier) => modules.get_id(specifier.as_str()).unwrap_or(0), - // We should have already resolved and Ready this module, so - // resolve() will not fail this time. - Err(..) => unreachable!(), - } - }; - isolate.mod_instantiate(root_id, &mut resolve_cb)?; + self.state = LoadState::LoadingRoot; + Ok(()) + } - self.state = State::Instantiated(root_id); + pub fn add_import( + &mut self, + specifier: ModuleSpecifier, + referrer: ModuleSpecifier, + ) { + if !self.is_pending.contains(&specifier) { + let fut = self.loader.load(&specifier, Some(referrer)); + self.pending.push(fut.boxed()); + self.is_pending.insert(specifier); } - - Ok(()) } } -impl<L: Loader + Unpin> Stream for RecursiveLoad<L> { - type Item = Result<Event, ErrBox>; +impl Stream for RecursiveModuleLoad { + type Item = Result<SourceCodeInfo, ErrBox>; fn poll_next( self: Pin<&mut Self>, @@ -320,53 +170,29 @@ impl<L: Loader + Unpin> Stream for RecursiveLoad<L> { ) -> Poll<Option<Self::Item>> { let inner = self.get_mut(); match inner.state { - State::ResolveMain(ref specifier, Some(ref code)) => { - let module_specifier = inner.loader.resolve( - specifier, - ".", - true, - inner.dyn_import_id().is_some(), - )?; - let info = SourceCodeInfo { - code: code.to_owned(), - module_url_specified: module_specifier.to_string(), - module_url_found: module_specifier.to_string(), - }; - inner.state = State::LoadingRoot; - Poll::Ready(Some(Ok(Event::Fetch(info)))) - } - State::ResolveMain(..) | State::ResolveImport(..) => { + LoadState::ResolveMain(..) | LoadState::ResolveImport(..) => { if let Err(e) = inner.add_root() { return Poll::Ready(Some(Err(e))); } inner.try_poll_next_unpin(cx) } - State::LoadingRoot | State::LoadingImports(..) => { + LoadState::LoadingRoot | LoadState::LoadingImports => { match inner.pending.try_poll_next_unpin(cx)? { Poll::Ready(None) => unreachable!(), - Poll::Ready(Some(info)) => Poll::Ready(Some(Ok(Event::Fetch(info)))), + Poll::Ready(Some(info)) => Poll::Ready(Some(Ok(info))), Poll::Pending => Poll::Pending, } } - State::Instantiated(id) => Poll::Ready(Some(Ok(Event::Instantiate(id)))), + LoadState::Done => Poll::Ready(None), } } } -struct ModuleInfo { - name: String, - children: Vec<String>, -} - -impl ModuleInfo { - fn has_child(&self, child_name: &str) -> bool { - for c in self.children.iter() { - if c == child_name { - return true; - } - } - false - } +pub struct ModuleInfo { + pub main: bool, + pub name: String, + pub handle: v8::Global<v8::Module>, + pub import_specifiers: Vec<String>, } /// A symbolic module entity. @@ -436,8 +262,9 @@ impl ModuleNameMap { /// A collection of JS modules. #[derive(Default)] pub struct Modules { - info: HashMap<ModuleId, ModuleInfo>, + pub(crate) info: HashMap<ModuleId, ModuleInfo>, by_name: ModuleNameMap, + pub(crate) specifier_cache: HashMap<(String, String), ModuleSpecifier>, } impl Modules { @@ -445,6 +272,7 @@ impl Modules { Self { info: HashMap::new(), by_name: ModuleNameMap::new(), + specifier_cache: HashMap::new(), } } @@ -453,7 +281,7 @@ impl Modules { } pub fn get_children(&self, id: ModuleId) -> Option<&Vec<String>> { - self.info.get(&id).map(|i| &i.children) + self.info.get(&id).map(|i| &i.import_specifiers) } pub fn get_children2(&self, name: &str) -> Option<&Vec<String>> { @@ -468,19 +296,14 @@ impl Modules { self.by_name.get(name).is_some() } - pub fn add_child(&mut self, parent_id: ModuleId, child_name: &str) -> bool { - self - .info - .get_mut(&parent_id) - .map(move |i| { - if !i.has_child(&child_name) { - i.children.push(child_name.to_string()); - } - }) - .is_some() - } - - pub fn register(&mut self, id: ModuleId, name: &str) { + pub fn register( + &mut self, + id: ModuleId, + name: &str, + main: bool, + handle: v8::Global<v8::Module>, + import_specifiers: Vec<String>, + ) { let name = String::from(name); debug!("register_complete {}", name); @@ -488,8 +311,10 @@ impl Modules { self.info.insert( id, ModuleInfo { + main, name, - children: Vec::new(), + import_specifiers, + handle, }, ); } @@ -502,6 +327,35 @@ impl Modules { self.by_name.is_alias(name) } + pub fn get_info(&self, id: ModuleId) -> Option<&ModuleInfo> { + if id == 0 { + return None; + } + self.info.get(&id) + } + + pub fn cache_specifier( + &mut self, + specifier: &str, + referrer: &str, + resolved_specifier: &ModuleSpecifier, + ) { + self.specifier_cache.insert( + (specifier.to_string(), referrer.to_string()), + resolved_specifier.to_owned(), + ); + } + + pub fn get_cached_specifier( + &self, + specifier: &str, + referrer: &str, + ) -> Option<&ModuleSpecifier> { + self + .specifier_cache + .get(&(specifier.to_string(), referrer.to_string())) + } + pub fn deps(&self, url: &str) -> Option<Deps> { Deps::new(self, url) } @@ -609,28 +463,22 @@ impl fmt::Display for Deps { #[cfg(test)] mod tests { use super::*; - use crate::es_isolate::tests::*; + use crate::es_isolate::EsIsolate; use crate::isolate::js_check; use futures::future::FutureExt; - use futures::stream::StreamExt; use std::error::Error; use std::fmt; use std::future::Future; + use std::sync::Mutex; struct MockLoader { pub loads: Arc<Mutex<Vec<String>>>, - pub isolate: Arc<Mutex<Box<EsIsolate>>>, - pub modules: Arc<Mutex<Modules>>, } impl MockLoader { fn new() -> Self { - let modules = Modules::new(); - let (isolate, _dispatch_count) = setup(); Self { loads: Arc::new(Mutex::new(Vec::new())), - isolate: Arc::new(Mutex::new(isolate)), - modules: Arc::new(Mutex::new(modules)), } } } @@ -788,68 +636,41 @@ mod tests { // nevertheless run it in the tokio executor. Ideally run_in_task can be // removed in the future. use crate::isolate::tests::run_in_task; + use crate::isolate::StartupData; #[test] fn test_recursive_load() { - run_in_task(|mut cx| { - let loader = MockLoader::new(); - 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::main("/a.js", None, loader, modules); - - let a_id = loop { - match recursive_load.try_poll_next_unpin(&mut cx) { - Poll::Ready(Some(Ok(Event::Fetch(info)))) => { - let mut isolate = isolate.lock().unwrap(); - recursive_load.register(info, &mut isolate).unwrap(); - } - Poll::Ready(Some(Ok(Event::Instantiate(id)))) => break id, - _ => panic!("unexpected result"), - }; - }; - - let mut isolate = isolate_.lock().unwrap(); - js_check(isolate.mod_evaluate(a_id)); - - let l = loads.lock().unwrap(); - assert_eq!( - l.to_vec(), - vec![ - "file:///a.js", - "file:///b.js", - "file:///c.js", - "file:///d.js" - ] - ); - - let modules = modules_.lock().unwrap(); - - assert_eq!(modules.get_id("file:///a.js"), Some(a_id)); - let b_id = modules.get_id("file:///b.js").unwrap(); - let c_id = modules.get_id("file:///c.js").unwrap(); - let d_id = modules.get_id("file:///d.js").unwrap(); + let loader = MockLoader::new(); + let loads = loader.loads.clone(); + let mut isolate = + EsIsolate::new(Box::new(loader), StartupData::None, false); + let a_id_fut = isolate.load_module("/a.js", None); + let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load"); + + js_check(isolate.mod_evaluate(a_id)); + let l = loads.lock().unwrap(); + assert_eq!( + l.to_vec(), + vec![ + "file:///a.js", + "file:///b.js", + "file:///c.js", + "file:///d.js" + ] + ); - assert_eq!( - modules.get_children(a_id), - Some(&vec![ - "file:///b.js".to_string(), - "file:///c.js".to_string() - ]) - ); - assert_eq!( - modules.get_children(b_id), - Some(&vec!["file:///c.js".to_string()]) - ); - assert_eq!( - modules.get_children(c_id), - Some(&vec!["file:///d.js".to_string()]) - ); - assert_eq!(modules.get_children(d_id), Some(&vec![])); - }) + let modules = &isolate.modules; + assert_eq!(modules.get_id("file:///a.js"), Some(a_id)); + let b_id = modules.get_id("file:///b.js").unwrap(); + let c_id = modules.get_id("file:///c.js").unwrap(); + let d_id = modules.get_id("file:///d.js").unwrap(); + assert_eq!( + modules.get_children(a_id), + Some(&vec!["/b.js".to_string(), "/c.js".to_string()]) + ); + assert_eq!(modules.get_children(b_id), Some(&vec!["/c.js".to_string()])); + assert_eq!(modules.get_children(c_id), Some(&vec!["/d.js".to_string()])); + assert_eq!(modules.get_children(d_id), Some(&vec![])); } const CIRCULAR1_SRC: &str = r#" @@ -870,60 +691,55 @@ mod tests { #[test] fn test_circular_load() { - run_in_task(|mut cx| { - let loader = MockLoader::new(); - let isolate = loader.isolate.clone(); - let isolate_ = isolate.clone(); - let modules = loader.modules.clone(); - let modules_ = modules.clone(); - let loads = loader.loads.clone(); - let recursive_load = - RecursiveLoad::main("/circular1.js", None, loader, modules); - let mut load_fut = recursive_load.get_future(isolate).boxed(); - let result = Pin::new(&mut load_fut).poll(&mut cx); - assert!(result.is_ready()); - if let Poll::Ready(Ok(circular1_id)) = result { - let mut isolate = isolate_.lock().unwrap(); - js_check(isolate.mod_evaluate(circular1_id)); + let loader = MockLoader::new(); + let loads = loader.loads.clone(); + let mut isolate = + EsIsolate::new(Box::new(loader), StartupData::None, false); - let l = loads.lock().unwrap(); - assert_eq!( - l.to_vec(), - vec![ - "file:///circular1.js", - "file:///circular2.js", - "file:///circular3.js" - ] - ); + let fut = async move { + let result = isolate.load_module("/circular1.js", None).await; + assert!(result.is_ok()); + let circular1_id = result.unwrap(); + js_check(isolate.mod_evaluate(circular1_id)); - let modules = modules_.lock().unwrap(); + let l = loads.lock().unwrap(); + assert_eq!( + l.to_vec(), + vec![ + "file:///circular1.js", + "file:///circular2.js", + "file:///circular3.js" + ] + ); - assert_eq!(modules.get_id("file:///circular1.js"), Some(circular1_id)); - let circular2_id = modules.get_id("file:///circular2.js").unwrap(); + let modules = &isolate.modules; - assert_eq!( - modules.get_children(circular1_id), - Some(&vec!["file:///circular2.js".to_string()]) - ); + assert_eq!(modules.get_id("file:///circular1.js"), Some(circular1_id)); + let circular2_id = modules.get_id("file:///circular2.js").unwrap(); - assert_eq!( - modules.get_children(circular2_id), - Some(&vec!["file:///circular3.js".to_string()]) - ); + assert_eq!( + modules.get_children(circular1_id), + Some(&vec!["/circular2.js".to_string()]) + ); - assert!(modules.get_id("file:///circular3.js").is_some()); - let circular3_id = modules.get_id("file:///circular3.js").unwrap(); - assert_eq!( - modules.get_children(circular3_id), - Some(&vec![ - "file:///circular1.js".to_string(), - "file:///circular2.js".to_string() - ]) - ); - } else { - unreachable!(); - } - }) + assert_eq!( + modules.get_children(circular2_id), + Some(&vec!["/circular3.js".to_string()]) + ); + + assert!(modules.get_id("file:///circular3.js").is_some()); + let circular3_id = modules.get_id("file:///circular3.js").unwrap(); + assert_eq!( + modules.get_children(circular3_id), + Some(&vec![ + "/circular1.js".to_string(), + "/circular2.js".to_string() + ]) + ); + } + .boxed(); + + futures::executor::block_on(fut); } const REDIRECT1_SRC: &str = r#" @@ -942,52 +758,47 @@ mod tests { #[test] fn test_redirect_load() { - run_in_task(|mut cx| { - let loader = MockLoader::new(); - let isolate = loader.isolate.clone(); - let isolate_ = isolate.clone(); - let modules = loader.modules.clone(); - let modules_ = modules.clone(); - let loads = loader.loads.clone(); - let recursive_load = - RecursiveLoad::main("/redirect1.js", None, loader, modules); - let mut load_fut = recursive_load.get_future(isolate).boxed(); - let result = Pin::new(&mut load_fut).poll(&mut cx); + let loader = MockLoader::new(); + let loads = loader.loads.clone(); + let mut isolate = + EsIsolate::new(Box::new(loader), StartupData::None, false); + + let fut = async move { + let result = isolate.load_module("/redirect1.js", None).await; println!(">> result {:?}", result); - assert!(result.is_ready()); - if let Poll::Ready(Ok(redirect1_id)) = result { - let mut isolate = isolate_.lock().unwrap(); - js_check(isolate.mod_evaluate(redirect1_id)); - let l = loads.lock().unwrap(); - assert_eq!( - l.to_vec(), - vec![ - "file:///redirect1.js", - "file:///redirect2.js", - "file:///dir/redirect3.js" - ] - ); + assert!(result.is_ok()); + let redirect1_id = result.unwrap(); + js_check(isolate.mod_evaluate(redirect1_id)); + let l = loads.lock().unwrap(); + assert_eq!( + l.to_vec(), + vec![ + "file:///redirect1.js", + "file:///redirect2.js", + "file:///dir/redirect3.js" + ] + ); - let modules = modules_.lock().unwrap(); + let modules = &isolate.modules; - assert_eq!(modules.get_id("file:///redirect1.js"), Some(redirect1_id)); + assert_eq!(modules.get_id("file:///redirect1.js"), Some(redirect1_id)); - let redirect2_id = modules.get_id("file:///dir/redirect2.js").unwrap(); - assert!(modules.is_alias("file:///redirect2.js")); - assert!(!modules.is_alias("file:///dir/redirect2.js")); - assert_eq!(modules.get_id("file:///redirect2.js"), Some(redirect2_id)); + let redirect2_id = modules.get_id("file:///dir/redirect2.js").unwrap(); + assert!(modules.is_alias("file:///redirect2.js")); + assert!(!modules.is_alias("file:///dir/redirect2.js")); + assert_eq!(modules.get_id("file:///redirect2.js"), Some(redirect2_id)); - let redirect3_id = modules.get_id("file:///redirect3.js").unwrap(); - assert!(modules.is_alias("file:///dir/redirect3.js")); - assert!(!modules.is_alias("file:///redirect3.js")); - assert_eq!( - modules.get_id("file:///dir/redirect3.js"), - Some(redirect3_id) - ); - } else { - unreachable!(); - } - }) + let redirect3_id = modules.get_id("file:///redirect3.js").unwrap(); + assert!(modules.is_alias("file:///dir/redirect3.js")); + assert!(!modules.is_alias("file:///redirect3.js")); + assert_eq!( + modules.get_id("file:///dir/redirect3.js"), + Some(redirect3_id) + ); + } + .boxed(); + + futures::executor::block_on(fut); } // main.js @@ -1010,13 +821,10 @@ mod tests { fn slow_never_ready_modules() { run_in_task(|mut cx| { let loader = MockLoader::new(); - let isolate = loader.isolate.clone(); - let modules = loader.modules.clone(); let loads = loader.loads.clone(); - let mut recursive_load = - RecursiveLoad::main("/main.js", None, loader, modules) - .get_future(isolate) - .boxed(); + let mut isolate = + EsIsolate::new(Box::new(loader), StartupData::None, false); + let mut recursive_load = isolate.load_module("/main.js", None).boxed(); let result = recursive_load.poll_unpin(&mut cx); assert!(result.is_pending()); @@ -1059,11 +867,9 @@ mod tests { fn loader_disappears_after_error() { run_in_task(|mut cx| { let loader = MockLoader::new(); - let isolate = loader.isolate.clone(); - let modules = loader.modules.clone(); - let recursive_load = - RecursiveLoad::main("/bad_import.js", None, loader, modules); - let mut load_fut = recursive_load.get_future(isolate).boxed(); + let mut isolate = + EsIsolate::new(Box::new(loader), StartupData::None, false); + let mut load_fut = isolate.load_module("/bad_import.js", None).boxed(); let result = load_fut.poll_unpin(&mut cx); if let Poll::Ready(Err(err)) = result { assert_eq!( @@ -1087,67 +893,41 @@ mod tests { #[test] fn recursive_load_main_with_code() { - run_in_task(|mut cx| { - let loader = MockLoader::new(); - let modules = loader.modules.clone(); - let modules_ = modules.clone(); - let isolate = loader.isolate.clone(); - let isolate_ = isolate.clone(); - let loads = loader.loads.clone(); - // In default resolution code should be empty. - // Instead we explicitly pass in our own code. - // The behavior should be very similar to /a.js. - let mut recursive_load = RecursiveLoad::main( - "/main_with_code.js", - Some(MAIN_WITH_CODE_SRC.to_owned()), - loader, - modules, - ); - - let main_id = loop { - match recursive_load.poll_next_unpin(&mut cx) { - Poll::Ready(Some(Ok(Event::Fetch(info)))) => { - let mut isolate = isolate.lock().unwrap(); - recursive_load.register(info, &mut isolate).unwrap(); - } - Poll::Ready(Some(Ok(Event::Instantiate(id)))) => break id, - _ => panic!("unexpected result"), - }; - }; - - let mut isolate = isolate_.lock().unwrap(); - js_check(isolate.mod_evaluate(main_id)); - - let l = loads.lock().unwrap(); - assert_eq!( - l.to_vec(), - vec!["file:///b.js", "file:///c.js", "file:///d.js"] - ); + let loader = MockLoader::new(); + let loads = loader.loads.clone(); + let mut isolate = + EsIsolate::new(Box::new(loader), 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. + let main_id_fut = isolate + .load_module("/main_with_code.js", Some(MAIN_WITH_CODE_SRC.to_owned())) + .boxed(); + let main_id = + futures::executor::block_on(main_id_fut).expect("Failed to load"); + + js_check(isolate.mod_evaluate(main_id)); + + let l = loads.lock().unwrap(); + assert_eq!( + l.to_vec(), + vec!["file:///b.js", "file:///c.js", "file:///d.js"] + ); - let modules = modules_.lock().unwrap(); + let modules = &isolate.modules; - assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id)); - let b_id = modules.get_id("file:///b.js").unwrap(); - let c_id = modules.get_id("file:///c.js").unwrap(); - let d_id = modules.get_id("file:///d.js").unwrap(); + assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id)); + let b_id = modules.get_id("file:///b.js").unwrap(); + let c_id = modules.get_id("file:///c.js").unwrap(); + let d_id = modules.get_id("file:///d.js").unwrap(); - assert_eq!( - modules.get_children(main_id), - Some(&vec![ - "file:///b.js".to_string(), - "file:///c.js".to_string() - ]) - ); - assert_eq!( - modules.get_children(b_id), - Some(&vec!["file:///c.js".to_string()]) - ); - assert_eq!( - modules.get_children(c_id), - Some(&vec!["file:///d.js".to_string()]) - ); - assert_eq!(modules.get_children(d_id), Some(&vec![])); - }) + assert_eq!( + modules.get_children(main_id), + Some(&vec!["/b.js".to_string(), "/c.js".to_string()]) + ); + assert_eq!(modules.get_children(b_id), Some(&vec!["/c.js".to_string()])); + assert_eq!(modules.get_children(c_id), Some(&vec!["/d.js".to_string()])); + assert_eq!(modules.get_children(d_id), Some(&vec![])); } #[test] @@ -1156,6 +936,7 @@ mod tests { assert!(modules.deps("foo").is_none()); } + /* TODO(bartlomieju): reenable #[test] fn deps() { // "foo" -> "bar" @@ -1192,4 +973,5 @@ mod tests { maybe_deps.unwrap().to_json() ); } + */ } diff --git a/deno_typescript/lib.rs b/deno_typescript/lib.rs index 430493f33..c86ed645b 100644 --- a/deno_typescript/lib.rs +++ b/deno_typescript/lib.rs @@ -10,7 +10,7 @@ use deno_core::js_check; pub use deno_core::v8_set_flags; use deno_core::CoreOp; use deno_core::ErrBox; -use deno_core::EsIsolate; +use deno_core::Isolate; use deno_core::ModuleSpecifier; use deno_core::PinnedBuf; use deno_core::StartupData; @@ -64,13 +64,13 @@ where } pub struct TSIsolate { - isolate: Box<EsIsolate>, + isolate: Box<Isolate>, state: Arc<Mutex<TSState>>, } impl TSIsolate { fn new(bundle: bool) -> TSIsolate { - let mut isolate = EsIsolate::new(StartupData::None, false); + let mut isolate = Isolate::new(StartupData::None, false); js_check(isolate.execute("assets/typescript.js", TYPESCRIPT_CODE)); js_check(isolate.execute("compiler_main.js", COMPILER_CODE)); @@ -180,7 +180,7 @@ pub fn mksnapshot_bundle( bundle: &Path, state: Arc<Mutex<TSState>>, ) -> Result<(), ErrBox> { - let runtime_isolate = &mut EsIsolate::new(StartupData::None, true); + let runtime_isolate = &mut Isolate::new(StartupData::None, true); let source_code_vec = std::fs::read(bundle)?; let source_code = std::str::from_utf8(&source_code_vec)?; @@ -203,7 +203,7 @@ pub fn mksnapshot_bundle_ts( bundle: &Path, state: Arc<Mutex<TSState>>, ) -> Result<(), ErrBox> { - let runtime_isolate = &mut EsIsolate::new(StartupData::None, true); + let runtime_isolate = &mut Isolate::new(StartupData::None, true); let source_code_vec = std::fs::read(bundle)?; let source_code = std::str::from_utf8(&source_code_vec)?; @@ -222,7 +222,7 @@ pub fn mksnapshot_bundle_ts( } fn write_snapshot( - runtime_isolate: &mut EsIsolate, + runtime_isolate: &mut Isolate, bundle: &Path, ) -> Result<(), ErrBox> { println!("creating snapshot..."); |