diff options
Diffstat (limited to 'core/es_isolate.rs')
-rw-r--r-- | core/es_isolate.rs | 983 |
1 files changed, 0 insertions, 983 deletions
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(); - } -} |