summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bindings.rs103
-rw-r--r--core/core_isolate.rs392
-rw-r--r--core/es_isolate.rs356
-rw-r--r--core/examples/http_bench.rs7
-rw-r--r--core/lib.rs2
-rw-r--r--core/modules.rs16
-rw-r--r--core/ops.rs30
7 files changed, 527 insertions, 379 deletions
diff --git a/core/bindings.rs b/core/bindings.rs
index 22f6767e8..7bcb99a38 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -250,9 +250,6 @@ pub extern "C" fn host_import_module_dynamically_callback(
let mut cbs = v8::CallbackScope::new_escapable(context);
let mut hs = v8::EscapableHandleScope::new(cbs.enter());
let scope = hs.enter();
- let isolate = scope.isolate();
- let core_isolate: &mut EsIsolate =
- unsafe { &mut *(isolate.get_data(1) as *mut EsIsolate) };
// NOTE(bartlomieju): will crash for non-UTF-8 specifier
let specifier_str = specifier
@@ -277,11 +274,11 @@ pub extern "C" fn host_import_module_dynamically_callback(
let mut resolver_handle = v8::Global::new();
resolver_handle.set(scope, resolver);
- core_isolate.dyn_import_cb(
- resolver_handle,
- &specifier_str,
- &referrer_name_str,
- );
+ {
+ let state_rc = EsIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
+ state.dyn_import_cb(resolver_handle, &specifier_str, &referrer_name_str);
+ }
&mut *scope.escape(promise)
}
@@ -294,14 +291,13 @@ pub extern "C" fn host_initialize_import_meta_object_callback(
let mut cbs = v8::CallbackScope::new(context);
let mut hs = v8::HandleScope::new(cbs.enter());
let scope = hs.enter();
- let isolate = scope.isolate();
- let core_isolate: &mut EsIsolate =
- unsafe { &mut *(isolate.get_data(1) as *mut EsIsolate) };
+ let state_rc = EsIsolate::state(scope.isolate());
+ let state = state_rc.borrow();
let id = module.get_identity_hash();
assert_ne!(id, 0);
- let info = core_isolate.modules.get_info(id).expect("Module not found");
+ let info = state.modules.get_info(id).expect("Module not found");
meta.create_data_property(
context,
@@ -320,10 +316,10 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
let mut hs = v8::HandleScope::new(cbs.enter());
let scope = hs.enter();
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
- let context = core_isolate.global_context.get(scope).unwrap();
+ let context = state.global_context.get(scope).unwrap();
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
@@ -335,13 +331,13 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
let error = message.get_value();
let mut error_global = v8::Global::<v8::Value>::new();
error_global.set(scope, error);
- core_isolate
+ state
.pending_promise_exceptions
.insert(promise_id, error_global);
}
v8::PromiseRejectEvent::PromiseHandlerAddedAfterReject => {
if let Some(mut handle) =
- core_isolate.pending_promise_exceptions.remove(&promise_id)
+ state.pending_promise_exceptions.remove(&promise_id)
{
handle.reset(scope);
}
@@ -415,17 +411,17 @@ fn recv(
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
- if !core_isolate.js_recv_cb.is_empty() {
+ if !state.js_recv_cb.is_empty() {
let msg = v8::String::new(scope, "Deno.core.recv already called.").unwrap();
scope.isolate().throw_exception(msg.into());
return;
}
let recv_fn = v8::Local::<v8::Function>::try_from(args.get(0)).unwrap();
- core_isolate.js_recv_cb.set(scope, recv_fn);
+ state.js_recv_cb.set(scope, recv_fn);
}
fn send(
@@ -433,10 +429,6 @@ fn send(
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
- assert!(!core_isolate.global_context.is_empty());
-
let op_id = match v8::Local::<v8::Uint32>::try_from(args.get(0)) {
Ok(op_id) => op_id.value() as u32,
Err(err) => {
@@ -465,9 +457,12 @@ fn send(
.map(ZeroCopyBuf::new)
.ok();
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
+ assert!(!state.global_context.is_empty());
+
// If response is empty then it's either async op or exception was thrown
- let maybe_response =
- core_isolate.dispatch_op(scope, op_id, control, zero_copy);
+ let maybe_response = state.dispatch_op(scope, op_id, control, zero_copy);
if let Some(response) = maybe_response {
// Synchronous response.
@@ -476,7 +471,7 @@ fn send(
if !buf.is_empty() {
let ui8 = boxed_slice_to_uint8array(scope, buf);
- rv.set(ui8.into())
+ rv.set(ui8.into());
}
}
}
@@ -486,10 +481,10 @@ fn set_macrotask_callback(
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
- if !core_isolate.js_macrotask_cb.is_empty() {
+ if !state.js_macrotask_cb.is_empty() {
let msg =
v8::String::new(scope, "Deno.core.setMacrotaskCallback already called.")
.unwrap();
@@ -499,7 +494,7 @@ fn set_macrotask_callback(
let macrotask_cb_fn =
v8::Local::<v8::Function>::try_from(args.get(0)).unwrap();
- core_isolate.js_macrotask_cb.set(scope, macrotask_cb_fn);
+ state.js_macrotask_cb.set(scope, macrotask_cb_fn);
}
fn eval_context(
@@ -507,10 +502,10 @@ fn eval_context(
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
- assert!(!core_isolate.global_context.is_empty());
- let context = core_isolate.global_context.get(scope).unwrap();
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow();
+ assert!(!state.global_context.is_empty());
+ let context = state.global_context.get(scope).unwrap();
let source = match v8::Local::<v8::String>::try_from(args.get(0)) {
Ok(s) => s,
@@ -643,10 +638,10 @@ fn format_error(
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
let e = JSError::from_v8_exception(scope, args.get(0));
- let e = (core_isolate.js_error_create_fn)(e);
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow();
+ let e = (state.js_error_create_fn)(e);
let e = e.to_string();
let e = v8::String::new(scope, &e).unwrap();
rv.set(e.into())
@@ -734,19 +729,19 @@ fn shared_getter(
_args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
// Lazily initialize the persistent external ArrayBuffer.
- if core_isolate.shared_ab.is_empty() {
+ if state.shared_ab.is_empty() {
let ab = v8::SharedArrayBuffer::with_backing_store(
scope,
- core_isolate.shared.get_backing_store(),
+ state.shared.get_backing_store(),
);
- core_isolate.shared_ab.set(scope, ab);
+ state.shared_ab.set(scope, ab);
}
- let shared_ab = core_isolate.shared_ab.get(scope).unwrap();
+ let shared_ab = state.shared_ab.get(scope).unwrap();
rv.set(shared_ab.into());
}
@@ -759,11 +754,11 @@ pub fn module_resolve_callback<'s>(
let mut scope = v8::EscapableHandleScope::new(scope.enter());
let scope = scope.enter();
- let core_isolate: &mut EsIsolate =
- unsafe { &mut *(scope.isolate().get_data(1) as *mut EsIsolate) };
+ let state_rc = EsIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
let referrer_id = referrer.get_identity_hash();
- let referrer_name = core_isolate
+ let referrer_name = state
.modules
.get_info(referrer_id)
.expect("ModuleInfo not found")
@@ -778,8 +773,8 @@ pub fn module_resolve_callback<'s>(
let req_str = req.to_rust_string_lossy(scope);
if req_str == specifier_str {
- let id = core_isolate.module_resolve_cb(&req_str, referrer_id);
- let maybe_info = core_isolate.modules.get_info(id);
+ let id = state.module_resolve_cb(&req_str, referrer_id);
+ let maybe_info = state.modules.get_info(id);
if maybe_info.is_none() {
let msg = format!(
@@ -810,10 +805,10 @@ fn get_promise_details(
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
- let core_isolate: &mut CoreIsolate =
- unsafe { &mut *(scope.isolate().get_data(0) as *mut CoreIsolate) };
- assert!(!core_isolate.global_context.is_empty());
- let context = core_isolate.global_context.get(scope).unwrap();
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow();
+ assert!(!state.global_context.is_empty());
+ let context = state.global_context.get(scope).unwrap();
let promise = match v8::Local::<v8::Promise>::try_from(args.get(0)) {
Ok(val) => val,
diff --git a/core/core_isolate.rs b/core/core_isolate.rs
index 12495e2d2..dff887ab3 100644
--- a/core/core_isolate.rs
+++ b/core/core_isolate.rs
@@ -16,20 +16,20 @@ use crate::JSError;
use crate::ResourceTable;
use crate::ZeroCopyBuf;
use futures::future::FutureExt;
-use futures::stream::select;
use futures::stream::FuturesUnordered;
use futures::stream::StreamExt;
use futures::task::AtomicWaker;
use futures::Future;
-use libc::c_void;
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::From;
use std::mem::forget;
+use std::ops::Deref;
+use std::ops::DerefMut;
use std::option::Option;
use std::pin::Pin;
use std::rc::Rc;
-use std::sync::{Arc, Mutex, Once};
+use std::sync::Once;
use std::task::Context;
use std::task::Poll;
@@ -72,7 +72,6 @@ pub enum StartupData<'a> {
}
type JSErrorCreateFn = dyn Fn(JSError) -> ErrBox;
-type IsolateErrorHandleFn = dyn FnMut(ErrBox) -> Result<(), ErrBox>;
/// A single execution context of JavaScript. Corresponds roughly to the "Web
/// Worker" concept in the DOM. An CoreIsolate is a Future that can be used with
@@ -82,28 +81,53 @@ type IsolateErrorHandleFn = dyn FnMut(ErrBox) -> Result<(), ErrBox>;
/// Ops are created in JavaScript by calling Deno.core.dispatch(), and in Rust
/// by implementing dispatcher function that takes control buffer and optional zero copy buffer
/// as arguments. An async Op corresponds exactly to a Promise in JavaScript.
-#[allow(unused)]
pub struct CoreIsolate {
- pub v8_isolate: Option<v8::OwnedIsolate>,
+ // This is an Option<OwnedIsolate> instead of just OwnedIsolate to workaround
+ // an safety issue with SnapshotCreator. See CoreIsolate::drop.
+ v8_isolate: Option<v8::OwnedIsolate>,
snapshot_creator: Option<v8::SnapshotCreator>,
has_snapshotted: bool,
+ needs_init: bool,
+ startup_script: Option<OwnedScript>,
+}
+
+/// Internal state for CoreIsolate which is stored in one of v8::Isolate's
+/// embedder slots.
+pub struct CoreIsolateState {
pub resource_table: Rc<RefCell<ResourceTable>>,
pub global_context: v8::Global<v8::Context>,
pub(crate) shared_ab: v8::Global<v8::SharedArrayBuffer>,
pub(crate) js_recv_cb: v8::Global<v8::Function>,
pub(crate) js_macrotask_cb: v8::Global<v8::Function>,
pub(crate) pending_promise_exceptions: HashMap<i32, v8::Global<v8::Value>>,
- shared_isolate_handle: Arc<Mutex<Option<*mut v8::Isolate>>>,
pub(crate) js_error_create_fn: Box<JSErrorCreateFn>,
- needs_init: bool,
pub(crate) shared: SharedQueue,
pending_ops: FuturesUnordered<PendingOpFuture>,
pending_unref_ops: FuturesUnordered<PendingOpFuture>,
have_unpolled_ops: bool,
- startup_script: Option<OwnedScript>,
pub op_registry: OpRegistry,
waker: AtomicWaker,
- error_handler: Option<Box<IsolateErrorHandleFn>>,
+}
+
+// TODO(ry) The trait v8::InIsolate is superfluous. HandleScope::new should just
+// take &mut v8::Isolate.
+impl v8::InIsolate for CoreIsolate {
+ fn isolate(&mut self) -> &mut v8::Isolate {
+ self.v8_isolate.as_mut().unwrap()
+ }
+}
+
+impl Deref for CoreIsolate {
+ type Target = v8::Isolate;
+ fn deref(&self) -> &v8::Isolate {
+ self.v8_isolate.as_ref().unwrap()
+ }
+}
+
+impl DerefMut for CoreIsolate {
+ fn deref_mut(&mut self) -> &mut v8::Isolate {
+ self.v8_isolate.as_mut().unwrap()
+ }
}
impl Drop for CoreIsolate {
@@ -128,8 +152,6 @@ impl Drop for CoreIsolate {
}
}
-static DENO_INIT: Once = Once::new();
-
#[allow(clippy::missing_safety_doc)]
pub unsafe fn v8_init() {
let platform = v8::new_default_platform().unwrap();
@@ -150,7 +172,8 @@ pub unsafe fn v8_init() {
impl CoreIsolate {
/// startup_data defines the snapshot or script used at startup to initialize
/// the isolate.
- pub fn new(startup_data: StartupData, will_snapshot: bool) -> Box<Self> {
+ pub fn new(startup_data: StartupData, will_snapshot: bool) -> Self {
+ static DENO_INIT: Once = Once::new();
DENO_INIT.call_once(|| {
unsafe { v8_init() };
});
@@ -210,44 +233,29 @@ impl CoreIsolate {
(isolate, None)
};
- let shared = SharedQueue::new(RECOMMENDED_SIZE);
- let needs_init = true;
-
- let core_isolate = Self {
- v8_isolate: None,
+ isolate.set_slot(Rc::new(RefCell::new(CoreIsolateState {
global_context,
resource_table: Rc::new(RefCell::new(ResourceTable::default())),
pending_promise_exceptions: HashMap::new(),
shared_ab: v8::Global::<v8::SharedArrayBuffer>::new(),
js_recv_cb: v8::Global::<v8::Function>::new(),
js_macrotask_cb: v8::Global::<v8::Function>::new(),
- snapshot_creator: maybe_snapshot_creator,
- has_snapshotted: false,
- shared_isolate_handle: Arc::new(Mutex::new(None)),
js_error_create_fn: Box::new(JSError::create),
- shared,
- needs_init,
+ shared: SharedQueue::new(RECOMMENDED_SIZE),
pending_ops: FuturesUnordered::new(),
pending_unref_ops: FuturesUnordered::new(),
have_unpolled_ops: false,
- startup_script,
op_registry: OpRegistry::new(),
waker: AtomicWaker::new(),
- error_handler: None,
- };
+ })));
- let mut boxed_isolate = Box::new(core_isolate);
- {
- let core_isolate_ptr: *mut Self = Box::into_raw(boxed_isolate);
- unsafe { isolate.set_data(0, core_isolate_ptr as *mut c_void) };
- boxed_isolate = unsafe { Box::from_raw(core_isolate_ptr) };
- let shared_handle_ptr = &mut *isolate;
- *boxed_isolate.shared_isolate_handle.lock().unwrap() =
- Some(shared_handle_ptr);
- boxed_isolate.v8_isolate = Some(isolate);
+ Self {
+ v8_isolate: Some(isolate),
+ snapshot_creator: maybe_snapshot_creator,
+ has_snapshotted: false,
+ needs_init: true,
+ startup_script,
}
-
- boxed_isolate
}
fn setup_isolate(mut isolate: v8::OwnedIsolate) -> v8::OwnedIsolate {
@@ -256,30 +264,13 @@ impl CoreIsolate {
isolate
}
- /// Defines the how Deno.core.dispatch() acts.
- /// Called whenever Deno.core.dispatch() is called in JavaScript. zero_copy_buf
- /// corresponds to the second argument of Deno.core.dispatch().
- ///
- /// Requires runtime to explicitly ask for op ids before using any of the ops.
- pub fn register_op<F>(&mut self, name: &str, op: F) -> OpId
- where
- F: Fn(&mut CoreIsolate, &[u8], Option<ZeroCopyBuf>) -> Op + 'static,
- {
- self.op_registry.register(name, op)
- }
-
- /// Allows a callback to be set whenever a V8 exception is made. This allows
- /// the caller to wrap the JSError into an error. By default this callback
- /// is set to JSError::create.
- pub fn set_js_error_create_fn(
- &mut self,
- f: impl Fn(JSError) -> ErrBox + 'static,
- ) {
- self.js_error_create_fn = Box::new(f);
+ pub fn state(isolate: &v8::Isolate) -> Rc<RefCell<CoreIsolateState>> {
+ let s = isolate.get_slot::<Rc<RefCell<CoreIsolateState>>>().unwrap();
+ s.clone()
}
/// Executes a bit of built-in JavaScript to provide Deno.sharedQueue.
- pub(crate) fn shared_init(&mut self) {
+ fn shared_init(&mut self) {
if self.needs_init {
self.needs_init = false;
js_check(self.execute("core.js", include_str!("core.js")));
@@ -290,46 +281,6 @@ impl CoreIsolate {
}
}
- pub fn dispatch_op<'s>(
- &mut self,
- scope: &mut impl v8::ToLocal<'s>,
- op_id: OpId,
- control_buf: &[u8],
- zero_copy_buf: Option<ZeroCopyBuf>,
- ) -> Option<(OpId, Box<[u8]>)> {
- let op = if let Some(dispatcher) = self.op_registry.get(op_id) {
- dispatcher(self, control_buf, zero_copy_buf)
- } else {
- let message =
- v8::String::new(scope, &format!("Unknown op id: {}", op_id)).unwrap();
- let exception = v8::Exception::type_error(scope, message);
- scope.isolate().throw_exception(exception);
- return None;
- };
-
- debug_assert_eq!(self.shared.size(), 0);
- match op {
- Op::Sync(buf) => {
- // For sync messages, we always return the response via Deno.core.send's
- // return value. Sync messages ignore the op_id.
- let op_id = 0;
- Some((op_id, buf))
- }
- Op::Async(fut) => {
- let fut2 = fut.map(move |buf| (op_id, buf));
- self.pending_ops.push(fut2.boxed_local());
- self.have_unpolled_ops = true;
- None
- }
- Op::AsyncUnref(fut) => {
- let fut2 = fut.map(move |buf| (op_id, buf));
- self.pending_unref_ops.push(fut2.boxed_local());
- self.have_unpolled_ops = true;
- None
- }
- }
- }
-
/// Executes traditional JavaScript code (traditional = not ES modules)
///
/// ErrBox can be downcast to a type that exposes additional information about
@@ -342,16 +293,17 @@ impl CoreIsolate {
) -> Result<(), ErrBox> {
self.shared_init();
- let js_error_create_fn = &*self.js_error_create_fn;
- let v8_isolate = self.v8_isolate.as_mut().unwrap();
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
- let mut hs = v8::HandleScope::new(v8_isolate);
+ let mut hs = v8::HandleScope::new(self.v8_isolate.as_mut().unwrap());
let scope = hs.enter();
- assert!(!self.global_context.is_empty());
- let context = self.global_context.get(scope).unwrap();
+ let context = state.global_context.get(scope).unwrap();
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
+ drop(state);
+
let source = v8::String::new(scope, js_source).unwrap();
let name = v8::String::new(scope, js_filename).unwrap();
let origin = bindings::script_origin(scope, name);
@@ -364,7 +316,7 @@ impl CoreIsolate {
Some(script) => script,
None => {
let exception = tc.exception().unwrap();
- return exception_to_err_result(scope, exception, js_error_create_fn);
+ return exception_to_err_result(scope, exception);
}
};
@@ -373,7 +325,7 @@ impl CoreIsolate {
None => {
assert!(tc.has_caught());
let exception = tc.exception().unwrap();
- exception_to_err_result(scope, exception, js_error_create_fn)
+ exception_to_err_result(scope, exception)
}
}
}
@@ -386,6 +338,7 @@ impl CoreIsolate {
/// different type if CoreIsolate::set_js_error_create_fn() has been used.
pub fn snapshot(&mut self) -> v8::StartupData {
assert!(self.snapshot_creator.is_some());
+ let state = Self::state(self);
// Note: create_blob() method must not be called from within a HandleScope.
// The HandleScope created here is exited at the end of the block.
@@ -394,7 +347,7 @@ impl CoreIsolate {
let v8_isolate = self.v8_isolate.as_mut().unwrap();
let mut hs = v8::HandleScope::new(v8_isolate);
let scope = hs.enter();
- self.global_context.reset(scope);
+ state.borrow_mut().global_context.reset(scope);
}
let snapshot_creator = self.snapshot_creator.as_mut().unwrap();
@@ -405,47 +358,59 @@ impl CoreIsolate {
snapshot
}
+
+ /// Defines the how Deno.core.dispatch() acts.
+ /// Called whenever Deno.core.dispatch() is called in JavaScript. zero_copy_buf
+ /// corresponds to the second argument of Deno.core.dispatch().
+ ///
+ /// Requires runtime to explicitly ask for op ids before using any of the ops.
+ pub fn register_op<F>(&mut self, name: &str, op: F) -> OpId
+ where
+ F: Fn(&mut CoreIsolateState, &[u8], Option<ZeroCopyBuf>) -> Op + 'static,
+ {
+ let state_rc = Self::state(self);
+ let mut state = state_rc.borrow_mut();
+ state.op_registry.register(name, op)
+ }
}
impl Future for CoreIsolate {
type Output = Result<(), ErrBox>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
- let inner = self.get_mut();
- inner.waker.register(cx.waker());
- inner.shared_init();
+ let core_isolate = self.get_mut();
+ core_isolate.shared_init();
- let v8_isolate = inner.v8_isolate.as_mut().unwrap();
- let js_error_create_fn = &*inner.js_error_create_fn;
- let js_recv_cb = &inner.js_recv_cb;
- let js_macrotask_cb = &inner.js_macrotask_cb;
- let pending_promise_exceptions = &mut inner.pending_promise_exceptions;
+ let state_rc = Self::state(core_isolate);
+ {
+ let state = state_rc.borrow();
+ state.waker.register(cx.waker());
+ }
- let mut hs = v8::HandleScope::new(v8_isolate);
+ let mut hs = v8::HandleScope::new(core_isolate);
let scope = hs.enter();
- let context = inner.global_context.get(scope).unwrap();
+ let context = {
+ let state = state_rc.borrow();
+ state.global_context.get(scope).unwrap()
+ };
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
- check_promise_exceptions(
- scope,
- pending_promise_exceptions,
- js_error_create_fn,
- )?;
+ check_promise_exceptions(scope)?;
let mut overflow_response: Option<(OpId, Buf)> = None;
loop {
+ let mut state = state_rc.borrow_mut();
// Now handle actual ops.
- inner.have_unpolled_ops = false;
- #[allow(clippy::match_wild_err_arm)]
- match select(&mut inner.pending_ops, &mut inner.pending_unref_ops)
- .poll_next_unpin(cx)
- {
+ state.have_unpolled_ops = false;
+
+ let pending_r = state.pending_ops.poll_next_unpin(cx);
+ match pending_r {
Poll::Ready(None) => break,
Poll::Pending => break,
Poll::Ready(Some((op_id, buf))) => {
- let successful_push = inner.shared.push(op_id, &buf);
+ let successful_push = state.shared.push(op_id, &buf);
if !successful_push {
// If we couldn't push the response to the shared queue, because
// there wasn't enough size, we will return the buffer via the
@@ -454,55 +419,142 @@ impl Future for CoreIsolate {
break;
}
}
- }
+ };
}
- if inner.shared.size() > 0 {
- async_op_response(scope, None, js_recv_cb, js_error_create_fn)?;
- // The other side should have shifted off all the messages.
- assert_eq!(inner.shared.size(), 0);
+ loop {
+ let mut state = state_rc.borrow_mut();
+ let unref_r = state.pending_unref_ops.poll_next_unpin(cx);
+ #[allow(clippy::match_wild_err_arm)]
+ match unref_r {
+ Poll::Ready(None) => break,
+ Poll::Pending => break,
+ Poll::Ready(Some((op_id, buf))) => {
+ let successful_push = state.shared.push(op_id, &buf);
+ if !successful_push {
+ // If we couldn't push the response to the shared queue, because
+ // there wasn't enough size, we will return the buffer via the
+ // legacy route, using the argument of deno_respond.
+ overflow_response = Some((op_id, buf));
+ break;
+ }
+ }
+ };
}
- if let Some((op_id, buf)) = overflow_response.take() {
- async_op_response(
- scope,
- Some((op_id, buf)),
- js_recv_cb,
- js_error_create_fn,
- )?;
+ {
+ let state = state_rc.borrow();
+ if state.shared.size() > 0 {
+ drop(state);
+ async_op_response(scope, None)?;
+ // The other side should have shifted off all the messages.
+ let state = state_rc.borrow();
+ assert_eq!(state.shared.size(), 0);
+ }
}
- drain_macrotasks(scope, js_macrotask_cb, js_error_create_fn)?;
+ {
+ if let Some((op_id, buf)) = overflow_response.take() {
+ async_op_response(scope, Some((op_id, buf)))?;
+ }
+
+ drain_macrotasks(scope)?;
- check_promise_exceptions(
- scope,
- pending_promise_exceptions,
- js_error_create_fn,
- )?;
+ check_promise_exceptions(scope)?;
+ }
+ let state = state_rc.borrow();
// We're idle if pending_ops is empty.
- if inner.pending_ops.is_empty() {
+ if state.pending_ops.is_empty() {
Poll::Ready(Ok(()))
} else {
- if inner.have_unpolled_ops {
- inner.waker.wake();
+ if state.have_unpolled_ops {
+ state.waker.wake();
}
Poll::Pending
}
}
}
+impl CoreIsolateState {
+ /// Defines the how Deno.core.dispatch() acts.
+ /// Called whenever Deno.core.dispatch() is called in JavaScript. zero_copy_buf
+ /// corresponds to the second argument of Deno.core.dispatch().
+ ///
+ /// Requires runtime to explicitly ask for op ids before using any of the ops.
+ pub fn register_op<F>(&mut self, name: &str, op: F) -> OpId
+ where
+ F: Fn(&mut CoreIsolateState, &[u8], Option<ZeroCopyBuf>) -> Op + 'static,
+ {
+ self.op_registry.register(name, op)
+ }
+
+ /// Allows a callback to be set whenever a V8 exception is made. This allows
+ /// the caller to wrap the JSError into an error. By default this callback
+ /// is set to JSError::create.
+ pub fn set_js_error_create_fn(
+ &mut self,
+ f: impl Fn(JSError) -> ErrBox + 'static,
+ ) {
+ self.js_error_create_fn = Box::new(f);
+ }
+
+ pub fn dispatch_op<'s>(
+ &mut self,
+ scope: &mut impl v8::ToLocal<'s>,
+ op_id: OpId,
+ control_buf: &[u8],
+ zero_copy_buf: Option<ZeroCopyBuf>,
+ ) -> Option<(OpId, Box<[u8]>)> {
+ let op = if let Some(dispatcher) = self.op_registry.get(op_id) {
+ dispatcher(self, control_buf, zero_copy_buf)
+ } else {
+ let message =
+ v8::String::new(scope, &format!("Unknown op id: {}", op_id)).unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.isolate().throw_exception(exception);
+ return None;
+ };
+
+ debug_assert_eq!(self.shared.size(), 0);
+ match op {
+ Op::Sync(buf) => {
+ // For sync messages, we always return the response via Deno.core.send's
+ // return value. Sync messages ignore the op_id.
+ let op_id = 0;
+ Some((op_id, buf))
+ }
+ Op::Async(fut) => {
+ let fut2 = fut.map(move |buf| (op_id, buf));
+ self.pending_ops.push(fut2.boxed_local());
+ self.have_unpolled_ops = true;
+ None
+ }
+ Op::AsyncUnref(fut) => {
+ let fut2 = fut.map(move |buf| (op_id, buf));
+ self.pending_unref_ops.push(fut2.boxed_local());
+ self.have_unpolled_ops = true;
+ None
+ }
+ }
+ }
+}
+
fn async_op_response<'s>(
scope: &mut impl v8::ToLocal<'s>,
maybe_buf: Option<(OpId, Box<[u8]>)>,
- js_recv_cb: &v8::Global<v8::Function>,
- js_error_create_fn: &JSErrorCreateFn,
) -> Result<(), ErrBox> {
let context = scope.get_current_context().unwrap();
let global: v8::Local<v8::Value> = context.global(scope).into();
- let js_recv_cb = js_recv_cb
+
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow_mut();
+
+ let js_recv_cb = state
+ .js_recv_cb
.get(scope)
.expect("Deno.core.recv has not been called.");
+ drop(state);
// TODO(piscisaureus): properly integrate TryCatch in the scope chain.
let mut try_catch = v8::TryCatch::new(scope);
@@ -521,20 +573,21 @@ fn async_op_response<'s>(
match tc.exception() {
None => Ok(()),
- Some(exception) => {
- exception_to_err_result(scope, exception, js_error_create_fn)
- }
+ Some(exception) => exception_to_err_result(scope, exception),
}
}
fn drain_macrotasks<'s>(
scope: &mut impl v8::ToLocal<'s>,
- js_macrotask_cb: &v8::Global<v8::Function>,
- js_error_create_fn: &JSErrorCreateFn,
) -> Result<(), ErrBox> {
let context = scope.get_current_context().unwrap();
let global: v8::Local<v8::Value> = context.global(scope).into();
- let js_macrotask_cb = js_macrotask_cb.get(scope);
+
+ let js_macrotask_cb = {
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow_mut();
+ state.js_macrotask_cb.get(scope)
+ };
if js_macrotask_cb.is_none() {
return Ok(());
}
@@ -550,7 +603,7 @@ fn drain_macrotasks<'s>(
let is_done = js_macrotask_cb.call(scope, context, global, &[]);
if let Some(exception) = tc.exception() {
- return exception_to_err_result(scope, exception, js_error_create_fn);
+ return exception_to_err_result(scope, exception);
}
let is_done = is_done.unwrap();
@@ -565,7 +618,6 @@ fn drain_macrotasks<'s>(
pub(crate) fn exception_to_err_result<'s, T>(
scope: &mut impl v8::ToLocal<'s>,
exception: v8::Local<v8::Value>,
- js_error_create_fn: &JSErrorCreateFn,
) -> Result<T, ErrBox> {
// TODO(piscisaureus): in rusty_v8, `is_execution_terminating()` should
// also be implemented on `struct Isolate`.
@@ -593,7 +645,10 @@ pub(crate) fn exception_to_err_result<'s, T>(
}
let js_error = JSError::from_v8_exception(scope, exception);
- let js_error = (js_error_create_fn)(js_error);
+
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let state = state_rc.borrow();
+ let js_error = (state.js_error_create_fn)(js_error);
if is_terminating_exception {
// Re-enable exception termination.
@@ -607,13 +662,15 @@ pub(crate) fn exception_to_err_result<'s, T>(
fn check_promise_exceptions<'s>(
scope: &mut impl v8::ToLocal<'s>,
- pending_promise_exceptions: &mut HashMap<i32, v8::Global<v8::Value>>,
- js_error_create_fn: &JSErrorCreateFn,
) -> Result<(), ErrBox> {
- if let Some(&key) = pending_promise_exceptions.keys().next() {
- let handle = pending_promise_exceptions.remove(&key).unwrap();
+ let state_rc = CoreIsolate::state(scope.isolate());
+ let mut state = state_rc.borrow_mut();
+
+ if let Some(&key) = state.pending_promise_exceptions.keys().next() {
+ let handle = state.pending_promise_exceptions.remove(&key).unwrap();
+ drop(state);
let exception = handle.get(scope).expect("empty error handle");
- exception_to_err_result(scope, exception, js_error_create_fn)
+ exception_to_err_result(scope, exception)
} else {
Ok(())
}
@@ -632,6 +689,7 @@ pub mod tests {
use futures::future::lazy;
use std::ops::FnOnce;
use std::sync::atomic::{AtomicUsize, Ordering};
+ use std::sync::Arc;
pub fn run_in_task<F>(f: F)
where
@@ -666,13 +724,13 @@ pub mod tests {
OverflowResAsync,
}
- pub fn setup(mode: Mode) -> (Box<CoreIsolate>, Arc<AtomicUsize>) {
+ pub fn setup(mode: Mode) -> (CoreIsolate, Arc<AtomicUsize>) {
let dispatch_count = Arc::new(AtomicUsize::new(0));
let dispatch_count_ = dispatch_count.clone();
let mut isolate = CoreIsolate::new(StartupData::None, false);
- let dispatcher = move |_isolate: &mut CoreIsolate,
+ let dispatcher = move |_state: &mut CoreIsolateState,
control: &[u8],
_zero_copy: Option<ZeroCopyBuf>|
-> Op {
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);
diff --git a/core/examples/http_bench.rs b/core/examples/http_bench.rs
index f728b2a69..a52f69fcb 100644
--- a/core/examples/http_bench.rs
+++ b/core/examples/http_bench.rs
@@ -4,6 +4,7 @@ extern crate derive_deref;
extern crate log;
use deno_core::CoreIsolate;
+use deno_core::CoreIsolateState;
use deno_core::Op;
use deno_core::ResourceTable;
use deno_core::Script;
@@ -77,7 +78,7 @@ impl From<Record> for RecordBuf {
}
struct Isolate {
- core_isolate: Box<CoreIsolate>, // Unclear why CoreIsolate::new() returns a box.
+ core_isolate: CoreIsolate,
state: State,
}
@@ -115,7 +116,7 @@ impl Isolate {
F: 'static + Fn(State, u32, Option<ZeroCopyBuf>) -> Result<u32, Error>,
{
let state = self.state.clone();
- let core_handler = move |_isolate: &mut CoreIsolate,
+ let core_handler = move |_isolate_state: &mut CoreIsolateState,
control_buf: &[u8],
zero_copy_buf: Option<ZeroCopyBuf>|
-> Op {
@@ -145,7 +146,7 @@ impl Isolate {
<F::Ok as TryInto<i32>>::Error: Debug,
{
let state = self.state.clone();
- let core_handler = move |_isolate: &mut CoreIsolate,
+ let core_handler = move |_isolate_state: &mut CoreIsolateState,
control_buf: &[u8],
zero_copy_buf: Option<ZeroCopyBuf>|
-> Op {
diff --git a/core/lib.rs b/core/lib.rs
index 49d49e19d..47ded645e 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -25,12 +25,14 @@ pub use rusty_v8 as v8;
pub use crate::core_isolate::js_check;
pub use crate::core_isolate::CoreIsolate;
+pub use crate::core_isolate::CoreIsolateState;
pub use crate::core_isolate::Script;
pub use crate::core_isolate::Snapshot;
pub use crate::core_isolate::StartupData;
pub use crate::errors::ErrBox;
pub use crate::errors::JSError;
pub use crate::es_isolate::EsIsolate;
+pub use crate::es_isolate::EsIsolateState;
pub use crate::flags::v8_set_flags;
pub use crate::module_specifier::ModuleResolutionError;
pub use crate::module_specifier::ModuleSpecifier;
diff --git a/core/modules.rs b/core/modules.rs
index 5a00d92cc..ca850d0bb 100644
--- a/core/modules.rs
+++ b/core/modules.rs
@@ -745,7 +745,9 @@ mod tests {
]
);
- let modules = &isolate.modules;
+ let state_rc = EsIsolate::state(&isolate);
+ let state = state_rc.borrow();
+ let modules = &state.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();
@@ -807,7 +809,9 @@ mod tests {
]
);
- let modules = &isolate.modules;
+ let state_rc = EsIsolate::state(&isolate);
+ let state = state_rc.borrow();
+ let modules = &state.modules;
assert_eq!(modules.get_id("file:///circular1.js"), Some(circular1_id));
let circular2_id = modules.get_id("file:///circular2.js").unwrap();
@@ -878,7 +882,9 @@ mod tests {
]
);
- let modules = &isolate.modules;
+ let state_rc = EsIsolate::state(&isolate);
+ let state = state_rc.borrow();
+ let modules = &state.modules;
assert_eq!(modules.get_id("file:///redirect1.js"), Some(redirect1_id));
@@ -1016,7 +1022,9 @@ mod tests {
vec!["file:///b.js", "file:///c.js", "file:///d.js"]
);
- let modules = &isolate.modules;
+ let state_rc = EsIsolate::state(&isolate);
+ let state = state_rc.borrow();
+ let modules = &state.modules;
assert_eq!(modules.get_id("file:///main_with_code.js"), Some(main_id));
let b_id = modules.get_id("file:///b.js").unwrap();
diff --git a/core/ops.rs b/core/ops.rs
index 0361f5ee9..ecece7355 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-use crate::CoreIsolate;
+use crate::core_isolate::CoreIsolateState;
use crate::ZeroCopyBuf;
use futures::Future;
use std::collections::HashMap;
@@ -22,7 +22,7 @@ pub enum Op {
/// Main type describing op
pub type OpDispatcher =
- dyn Fn(&mut CoreIsolate, &[u8], Option<ZeroCopyBuf>) -> Op + 'static;
+ dyn Fn(&mut CoreIsolateState, &[u8], Option<ZeroCopyBuf>) -> Op + 'static;
#[derive(Default)]
pub struct OpRegistry {
@@ -33,8 +33,8 @@ pub struct OpRegistry {
impl OpRegistry {
pub fn new() -> Self {
let mut registry = Self::default();
- let op_id = registry.register("ops", |isolate, _, _| {
- let buf = isolate.op_registry.json_map();
+ let op_id = registry.register("ops", |state, _, _| {
+ let buf = state.op_registry.json_map();
Op::Sync(buf)
});
assert_eq!(op_id, 0);
@@ -43,7 +43,7 @@ impl OpRegistry {
pub fn register<F>(&mut self, name: &str, op: F) -> OpId
where
- F: Fn(&mut CoreIsolate, &[u8], Option<ZeroCopyBuf>) -> Op + 'static,
+ F: Fn(&mut CoreIsolateState, &[u8], Option<ZeroCopyBuf>) -> Op + 'static,
{
let op_id = self.dispatchers.len() as u32;
@@ -68,6 +68,7 @@ impl OpRegistry {
#[test]
fn test_op_registry() {
+ use crate::CoreIsolate;
use std::sync::atomic;
use std::sync::Arc;
let mut op_registry = OpRegistry::new();
@@ -86,10 +87,12 @@ fn test_op_registry() {
expected.insert("test".to_string(), 1);
assert_eq!(op_registry.name_to_id, expected);
- let mut isolate = CoreIsolate::new(crate::StartupData::None, false);
+ let isolate = CoreIsolate::new(crate::StartupData::None, false);
let dispatch = op_registry.get(test_id).unwrap();
- let res = dispatch(&mut isolate, &[], None);
+ let state_rc = CoreIsolate::state(&isolate);
+ let mut state = state_rc.borrow_mut();
+ let res = dispatch(&mut state, &[], None);
if let Op::Sync(buf) = res {
assert_eq!(buf.len(), 0);
} else {
@@ -102,6 +105,7 @@ fn test_op_registry() {
#[test]
fn register_op_during_call() {
+ use crate::CoreIsolate;
use std::sync::atomic;
use std::sync::Arc;
use std::sync::Mutex;
@@ -126,13 +130,17 @@ fn register_op_during_call() {
};
assert!(test_id != 0);
- let mut isolate = CoreIsolate::new(crate::StartupData::None, false);
+ let isolate = CoreIsolate::new(crate::StartupData::None, false);
let dispatcher1 = {
let g = op_registry.lock().unwrap();
g.get(test_id).unwrap()
};
- dispatcher1(&mut isolate, &[], None);
+ {
+ let state_rc = CoreIsolate::state(&isolate);
+ let mut state = state_rc.borrow_mut();
+ dispatcher1(&mut state, &[], None);
+ }
let mut expected = HashMap::new();
expected.insert("ops".to_string(), 0);
@@ -147,7 +155,9 @@ fn register_op_during_call() {
let g = op_registry.lock().unwrap();
g.get(2).unwrap()
};
- let res = dispatcher2(&mut isolate, &[], None);
+ let state_rc = CoreIsolate::state(&isolate);
+ let mut state = state_rc.borrow_mut();
+ let res = dispatcher2(&mut state, &[], None);
if let Op::Sync(buf) = res {
assert_eq!(buf.len(), 0);
} else {