diff options
Diffstat (limited to 'core/es_isolate.rs')
-rw-r--r-- | core/es_isolate.rs | 356 |
1 files changed, 215 insertions, 141 deletions
diff --git a/core/es_isolate.rs b/core/es_isolate.rs index a3775c8a4..a23af43d7 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -16,7 +16,7 @@ use futures::stream::StreamExt; use futures::stream::StreamFuture; use futures::task::AtomicWaker; use futures::Future; -use libc::c_void; +use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; @@ -46,8 +46,9 @@ use crate::StartupData; /// 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 { - core_isolate: Box<CoreIsolate>, +pub struct EsIsolate(CoreIsolate); + +pub struct EsIsolateState { loader: Rc<dyn ModuleLoader>, pub modules: Modules, pub(crate) dyn_import_map: @@ -62,13 +63,13 @@ impl Deref for EsIsolate { type Target = CoreIsolate; fn deref(&self) -> &Self::Target { - &self.core_isolate + &self.0 } } impl DerefMut for EsIsolate { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.core_isolate + &mut self.0 } } @@ -77,38 +78,27 @@ impl EsIsolate { loader: Rc<dyn ModuleLoader>, startup_data: StartupData, will_snapshot: bool, - ) -> Box<Self> { + ) -> Self { let mut core_isolate = CoreIsolate::new(startup_data, will_snapshot); { - let v8_isolate = core_isolate.v8_isolate.as_mut().unwrap(); - v8_isolate.set_host_initialize_import_meta_object_callback( + core_isolate.set_host_initialize_import_meta_object_callback( bindings::host_initialize_import_meta_object_callback, ); - v8_isolate.set_host_import_module_dynamically_callback( + core_isolate.set_host_import_module_dynamically_callback( bindings::host_import_module_dynamically_callback, ); } - let es_isolate = Self { + core_isolate.set_slot(Rc::new(RefCell::new(EsIsolateState { modules: Modules::new(), loader, - core_isolate, dyn_import_map: HashMap::new(), preparing_dyn_imports: FuturesUnordered::new(), pending_dyn_imports: FuturesUnordered::new(), waker: AtomicWaker::new(), - }; + }))); - let mut boxed_es_isolate = Box::new(es_isolate); - { - let es_isolate_ptr: *mut Self = Box::into_raw(boxed_es_isolate); - boxed_es_isolate = unsafe { Box::from_raw(es_isolate_ptr) }; - unsafe { - let v8_isolate = boxed_es_isolate.v8_isolate.as_mut().unwrap(); - v8_isolate.set_data(1, es_isolate_ptr as *mut c_void); - }; - } - boxed_es_isolate + EsIsolate(core_isolate) } /// Low-level module creation. @@ -120,14 +110,13 @@ impl EsIsolate { name: &str, source: &str, ) -> Result<ModuleId, ErrBox> { - let core_isolate = &mut self.core_isolate; - let v8_isolate = core_isolate.v8_isolate.as_mut().unwrap(); - let js_error_create_fn = &*core_isolate.js_error_create_fn; + let state_rc = Self::state(self); - let mut hs = v8::HandleScope::new(v8_isolate); + let core_state_rc = CoreIsolate::state(self); + let core_state = core_state_rc.borrow(); + let mut hs = v8::HandleScope::new(&mut self.0); let scope = hs.enter(); - assert!(!core_isolate.global_context.is_empty()); - let context = core_isolate.global_context.get(scope).unwrap(); + let context = core_state.global_context.get(scope).unwrap(); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); @@ -144,11 +133,7 @@ impl EsIsolate { if tc.has_caught() { assert!(maybe_module.is_none()); - return exception_to_err_result( - scope, - tc.exception().unwrap(), - js_error_create_fn, - ); + return exception_to_err_result(scope, tc.exception().unwrap()); } let module = maybe_module.unwrap(); @@ -158,16 +143,21 @@ impl EsIsolate { for i in 0..module.get_module_requests_length() { let import_specifier = module.get_module_request(i).to_rust_string_lossy(scope); + let state = state_rc.borrow(); let module_specifier = - self.loader.resolve(&import_specifier, name, false)?; + state.loader.resolve(&import_specifier, name, false)?; import_specifiers.push(module_specifier); } let mut handle = v8::Global::<v8::Module>::new(); handle.set(scope, module); - self - .modules - .register(id, name, main, handle, import_specifiers); + + { + let mut state = state_rc.borrow_mut(); + state + .modules + .register(id, name, main, handle, import_specifiers); + } Ok(id) } @@ -177,32 +167,30 @@ impl EsIsolate { /// 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 v8_isolate = self.core_isolate.v8_isolate.as_mut().unwrap(); - let js_error_create_fn = &*self.core_isolate.js_error_create_fn; + let state_rc = Self::state(self); + let state = state_rc.borrow(); - let mut hs = v8::HandleScope::new(v8_isolate); + let core_state_rc = CoreIsolate::state(self); + let core_state = core_state_rc.borrow(); + let mut hs = v8::HandleScope::new(&mut self.0); let scope = hs.enter(); - assert!(!self.core_isolate.global_context.is_empty()); - let context = self.core_isolate.global_context.get(scope).unwrap(); + let context = core_state.global_context.get(scope).unwrap(); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let mut try_catch = v8::TryCatch::new(scope); let tc = try_catch.enter(); - let module_info = match self.modules.get_info(id) { + let module_info = match state.modules.get_info(id) { Some(info) => info, None if id == 0 => return Ok(()), _ => panic!("module id {} not found in module table", id), }; let mut module = module_info.handle.get(scope).unwrap(); + drop(state); if module.get_status() == v8::ModuleStatus::Errored { - exception_to_err_result( - scope, - module.get_exception(), - js_error_create_fn, - )? + exception_to_err_result(scope, module.get_exception())? } let result = @@ -211,7 +199,7 @@ impl EsIsolate { Some(_) => Ok(()), None => { let exception = tc.exception().unwrap(); - exception_to_err_result(scope, exception, js_error_create_fn) + exception_to_err_result(scope, exception) } } } @@ -222,20 +210,24 @@ impl EsIsolate { /// 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> { - let core_isolate = &mut self.core_isolate; - let v8_isolate = core_isolate.v8_isolate.as_mut().unwrap(); - let js_error_create_fn = &*core_isolate.js_error_create_fn; + let state_rc = Self::state(self); + let state = state_rc.borrow(); + + let core_state_rc = CoreIsolate::state(self); - let mut hs = v8::HandleScope::new(v8_isolate); + let mut hs = v8::HandleScope::new(&mut self.0); let scope = hs.enter(); - assert!(!core_isolate.global_context.is_empty()); - let context = core_isolate.global_context.get(scope).unwrap(); + let context = { + let core_state = core_state_rc.borrow(); + core_state.global_context.get(scope).unwrap() + }; let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); - let info = self.modules.get_info(id).expect("ModuleInfo not found"); + let info = state.modules.get_info(id).expect("ModuleInfo not found"); let module = info.handle.get(scope).expect("Empty module handle"); let mut status = module.get_status(); + drop(state); if status == v8::ModuleStatus::Instantiated { // IMPORTANT: Top-level-await is enabled, which means that return value // of module evaluation is a promise. @@ -266,8 +258,9 @@ impl EsIsolate { 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(); if let Some(mut handle) = - core_isolate.pending_promise_exceptions.remove(&promise_id) + core_state.pending_promise_exceptions.remove(&promise_id) { handle.reset(scope); } @@ -280,68 +273,41 @@ impl EsIsolate { v8::ModuleStatus::Evaluated => Ok(()), v8::ModuleStatus::Errored => { let exception = module.get_exception(); - exception_to_err_result(scope, exception, js_error_create_fn) + exception_to_err_result(scope, exception) .map_err(|err| attach_handle_to_error(scope, err, exception)) } other => panic!("Unexpected module status {:?}", other), } } - // Called by V8 during `Isolate::mod_instantiate`. - pub fn module_resolve_cb( - &mut self, - specifier: &str, - referrer_id: ModuleId, - ) -> ModuleId { - let referrer = self.modules.get_name(referrer_id).unwrap(); - let specifier = self - .loader - .resolve(specifier, referrer, false) - .expect("Module should have been already resolved"); - self.modules.get_id(specifier.as_str()).unwrap_or(0) - } - - // Called by V8 during `Isolate::mod_instantiate`. - pub fn dyn_import_cb( - &mut self, - resolver_handle: v8::Global<v8::PromiseResolver>, - specifier: &str, - referrer: &str, - ) { - debug!("dyn_import specifier {} referrer {} ", specifier, referrer); - - let load = RecursiveModuleLoad::dynamic_import( - specifier, - referrer, - self.loader.clone(), - ); - self.dyn_import_map.insert(load.id, resolver_handle); - self.waker.wake(); - let fut = load.prepare().boxed_local(); - self.preparing_dyn_imports.push(fut); - } - fn dyn_import_error( &mut self, id: ModuleLoadId, err: ErrBox, ) -> Result<(), ErrBox> { - let core_isolate = &mut self.core_isolate; - let v8_isolate = core_isolate.v8_isolate.as_mut().unwrap(); + let state_rc = Self::state(self); + let mut state = state_rc.borrow_mut(); - let mut hs = v8::HandleScope::new(v8_isolate); + let core_state_rc = CoreIsolate::state(self); + let core_state = core_state_rc.borrow(); + + let mut hs = v8::HandleScope::new(&mut self.0); let scope = hs.enter(); - let context = core_isolate.global_context.get(scope).unwrap(); + let context = core_state.global_context.get(scope).unwrap(); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); - let mut resolver_handle = self + drop(core_state); + + let mut resolver_handle = state .dyn_import_map .remove(&id) .expect("Invalid dyn import id"); let resolver = resolver_handle.get(scope).unwrap(); resolver_handle.reset(scope); + drop(state); + let exception = err .downcast_ref::<ErrWithV8Handle>() .and_then(|err| err.get_handle().get(scope)) @@ -361,29 +327,40 @@ impl EsIsolate { id: ModuleLoadId, mod_id: ModuleId, ) -> Result<(), ErrBox> { + let state_rc = Self::state(self); + + let core_state_rc = CoreIsolate::state(self); + let core_state = core_state_rc.borrow(); + debug!("dyn_import_done {} {:?}", id, mod_id); assert!(mod_id != 0); - let v8_isolate = self.core_isolate.v8_isolate.as_mut().unwrap(); - let mut hs = v8::HandleScope::new(v8_isolate); + let mut hs = v8::HandleScope::new(&mut self.0); let scope = hs.enter(); - assert!(!self.core_isolate.global_context.is_empty()); - let context = self.core_isolate.global_context.get(scope).unwrap(); + let context = core_state.global_context.get(scope).unwrap(); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); - let mut resolver_handle = self - .dyn_import_map - .remove(&id) - .expect("Invalid dyn import id"); + let mut resolver_handle = { + let mut state = state_rc.borrow_mut(); + state + .dyn_import_map + .remove(&id) + .expect("Invalid dyn import id") + }; let resolver = resolver_handle.get(scope).unwrap(); resolver_handle.reset(scope); - let info = self - .modules - .get_info(mod_id) - .expect("Dyn import module info not found"); - // Resolution success - let mut module = info.handle.get(scope).unwrap(); + + let mut module = { + let state = state_rc.borrow(); + let info = state + .modules + .get_info(mod_id) + .expect("Dyn import module info not found"); + // Resolution success + info.handle.get(scope).unwrap() + }; assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated); + let module_namespace = module.get_module_namespace(); resolver.resolve(context, module_namespace).unwrap(); scope.isolate().run_microtasks(); @@ -394,8 +371,14 @@ impl EsIsolate { &mut self, cx: &mut Context, ) -> Poll<Result<(), ErrBox>> { + let state_rc = Self::state(self); + loop { - match self.preparing_dyn_imports.poll_next_unpin(cx) { + 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(())); @@ -406,7 +389,8 @@ impl EsIsolate { match prepare_result { Ok(load) => { - self.pending_dyn_imports.push(load.into_future()); + let state = state_rc.borrow_mut(); + state.pending_dyn_imports.push(load.into_future()); } Err(err) => { self.dyn_import_error(dyn_import_id, err)?; @@ -418,8 +402,14 @@ impl EsIsolate { } fn poll_dyn_imports(&mut self, cx: &mut Context) -> Poll<Result<(), ErrBox>> { + let state_rc = Self::state(self); loop { - match self.pending_dyn_imports.poll_next_unpin(cx) { + 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(())); @@ -438,7 +428,8 @@ impl EsIsolate { match self.register_during_load(info, &mut load) { Ok(()) => { // Keep importing until it's fully drained - self.pending_dyn_imports.push(load.into_future()); + let state = state_rc.borrow_mut(); + state.pending_dyn_imports.push(load.into_future()); } Err(err) => self.dyn_import_error(dyn_import_id, err)?, } @@ -481,6 +472,7 @@ impl EsIsolate { 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 @@ -493,10 +485,18 @@ impl EsIsolate { // If necessary, register an alias. if module_url_specified != module_url_found { - self.modules.alias(&module_url_specified, &module_url_found); + let mut state = state_rc.borrow_mut(); + state + .modules + .alias(&module_url_specified, &module_url_found); } - let module_id = match self.modules.get_id(&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!( @@ -510,10 +510,19 @@ impl EsIsolate { }; // Now we must iterate over all imports of the module and load them. - let imports = self.modules.get_children(module_id).unwrap(); + 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 { - if !self.modules.is_registered(module_specifier) { + 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()); } @@ -541,11 +550,13 @@ impl EsIsolate { specifier: &ModuleSpecifier, code: Option<String>, ) -> Result<ModuleId, ErrBox> { - let load = RecursiveModuleLoad::main( - &specifier.to_string(), - code, - self.loader.clone(), - ); + 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?; @@ -558,30 +569,49 @@ impl EsIsolate { let root_id = load.root_module_id.expect("Root module id empty"); self.mod_instantiate(root_id).map(|_| root_id) } + + 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 inner = self.get_mut(); + let es_isolate = self.get_mut(); + + let state_rc = Self::state(es_isolate); - inner.waker.register(cx.waker()); + { + let state = state_rc.borrow(); + state.waker.register(cx.waker()); + } - if !inner.preparing_dyn_imports.is_empty() { - let poll_imports = inner.prepare_dyn_imports(cx)?; + 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()); } - if !inner.pending_dyn_imports.is_empty() { - let poll_imports = inner.poll_dyn_imports(cx)?; + 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!(inner.core_isolate.poll_unpin(cx)) { + match ready!(es_isolate.0.poll_unpin(cx)) { Ok(()) => { - if inner.pending_dyn_imports.is_empty() - && inner.preparing_dyn_imports.is_empty() + let state = state_rc.borrow(); + if state.pending_dyn_imports.is_empty() + && state.preparing_dyn_imports.is_empty() { Poll::Ready(Ok(())) } else { @@ -593,10 +623,47 @@ impl Future for EsIsolate { } } +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::core_isolate::CoreIsolateState; use crate::js_check; use crate::modules::ModuleSourceFuture; use crate::ops::*; @@ -643,7 +710,7 @@ pub mod tests { let mut isolate = EsIsolate::new(loader, StartupData::None, false); - let dispatcher = move |_isolate: &mut CoreIsolate, + let dispatcher = move |_state: &mut CoreIsolateState, control: &[u8], _zero_copy: Option<ZeroCopyBuf>| -> Op { @@ -684,16 +751,23 @@ pub mod tests { .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - let imports = isolate.modules.get_children(mod_a); - assert_eq!( - imports, - Some(&vec![ModuleSpecifier::resolve_url("file:///b.js").unwrap()]) - ); + 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 imports = isolate.modules.get_children(mod_b).unwrap(); - assert_eq!(imports.len(), 0); + { + 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); |