summaryrefslogtreecommitdiff
path: root/core/es_isolate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/es_isolate.rs')
-rw-r--r--core/es_isolate.rs356
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);