summaryrefslogtreecommitdiff
path: root/core/runtime.rs
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2020-09-10 09:57:45 -0400
committerGitHub <noreply@github.com>2020-09-10 09:57:45 -0400
commit7c2e7c660804afca823d60e6496aa853f75db16c (patch)
treeb7746b181c1564c6b1abd2e906662f9e6b008417 /core/runtime.rs
parent6f70e6e72ba2d5c1de7495adac37c1e4f4e86b24 (diff)
Use gotham-like state for ops (#7385)
Provides a concrete state type that can be dynamically added. This is necessary for op crates. * renames BasicState to OpState * async ops take `Rc<RefCell<OpState>>` * sync ops take `&mut OpState` * removes `OpRegistry`, `OpRouter` traits * `get_error_class_fn` moved to OpState * ResourceTable moved to OpState
Diffstat (limited to 'core/runtime.rs')
-rw-r--r--core/runtime.rs251
1 files changed, 117 insertions, 134 deletions
diff --git a/core/runtime.rs b/core/runtime.rs
index 2f574b195..e6fc23084 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -19,9 +19,10 @@ use crate::modules::RecursiveModuleLoad;
use crate::ops::*;
use crate::shared_queue::SharedQueue;
use crate::shared_queue::RECOMMENDED_SIZE;
+use crate::BufVec;
use crate::ErrBox;
use crate::JsError;
-use crate::OpRouter;
+use crate::OpState;
use futures::stream::FuturesUnordered;
use futures::stream::StreamExt;
use futures::stream::StreamFuture;
@@ -94,7 +95,7 @@ impl StartupData<'_> {
type JsErrorCreateFn = dyn Fn(JsError) -> ErrBox;
-pub type GetErrorClassFn = dyn for<'e> Fn(&'e ErrBox) -> &'static str;
+pub type GetErrorClassFn = &'static dyn for<'e> Fn(&'e ErrBox) -> &'static str;
/// Objects that need to live as long as the isolate
#[derive(Default)]
@@ -137,7 +138,8 @@ pub struct JsRuntimeState {
pub(crate) pending_ops: FuturesUnordered<PendingOpFuture>,
pub(crate) pending_unref_ops: FuturesUnordered<PendingOpFuture>,
pub(crate) have_unpolled_ops: Cell<bool>,
- pub(crate) op_router: Rc<dyn OpRouter>,
+ //pub(crate) op_table: OpTable,
+ pub(crate) op_state: Rc<RefCell<OpState>>,
loader: Rc<dyn ModuleLoader>,
pub modules: Modules,
pub(crate) dyn_import_map:
@@ -219,7 +221,6 @@ pub struct HeapLimits {
pub(crate) struct IsolateOptions {
loader: Rc<dyn ModuleLoader>,
- op_router: Rc<dyn OpRouter>,
startup_script: Option<OwnedScript>,
startup_snapshot: Option<Snapshot>,
will_snapshot: bool,
@@ -229,15 +230,10 @@ pub(crate) struct IsolateOptions {
impl JsRuntime {
/// startup_data defines the snapshot or script used at startup to initialize
/// the isolate.
- pub fn new(
- op_router: Rc<dyn OpRouter>,
- startup_data: StartupData,
- will_snapshot: bool,
- ) -> Self {
+ pub fn new(startup_data: StartupData, will_snapshot: bool) -> Self {
let (startup_script, startup_snapshot) = startup_data.into_options();
let options = IsolateOptions {
loader: Rc::new(NoopModuleLoader),
- op_router,
startup_script,
startup_snapshot,
will_snapshot,
@@ -251,14 +247,12 @@ impl JsRuntime {
/// Create new isolate that can load and execute ESModules.
pub fn new_with_loader(
loader: Rc<dyn ModuleLoader>,
- op_router: Rc<dyn OpRouter>,
startup_data: StartupData,
will_snapshot: bool,
) -> Self {
let (startup_script, startup_snapshot) = startup_data.into_options();
let options = IsolateOptions {
loader,
- op_router,
startup_script,
startup_snapshot,
will_snapshot,
@@ -275,14 +269,12 @@ impl JsRuntime {
/// Make sure to use [`add_near_heap_limit_callback`](#method.add_near_heap_limit_callback)
/// to prevent v8 from crashing when reaching the upper limit.
pub fn with_heap_limits(
- op_router: Rc<dyn OpRouter>,
startup_data: StartupData,
heap_limits: HeapLimits,
) -> Self {
let (startup_script, startup_snapshot) = startup_data.into_options();
let options = IsolateOptions {
loader: Rc::new(NoopModuleLoader),
- op_router,
startup_script,
startup_snapshot,
will_snapshot: false,
@@ -347,6 +339,8 @@ impl JsRuntime {
(isolate, None)
};
+ let op_state = OpState::default();
+
isolate.set_slot(Rc::new(RefCell::new(JsRuntimeState {
global_context: Some(global_context),
pending_promise_exceptions: HashMap::new(),
@@ -357,8 +351,8 @@ impl JsRuntime {
shared: SharedQueue::new(RECOMMENDED_SIZE),
pending_ops: FuturesUnordered::new(),
pending_unref_ops: FuturesUnordered::new(),
+ op_state: Rc::new(RefCell::new(op_state)),
have_unpolled_ops: Cell::new(false),
- op_router: options.op_router,
modules: Modules::new(),
loader: options.loader,
dyn_import_map: HashMap::new(),
@@ -406,6 +400,12 @@ impl JsRuntime {
}
}
+ pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
+ let state_rc = Self::state(self);
+ let state = state_rc.borrow();
+ state.op_state.clone()
+ }
+
/// Executes traditional JavaScript code (traditional = not ES modules)
///
/// ErrBox can be downcast to a type that exposes additional information about
@@ -477,6 +477,18 @@ impl JsRuntime {
snapshot
}
+ pub fn register_op<F>(&mut self, name: &str, op_fn: F) -> OpId
+ where
+ F: Fn(Rc<RefCell<OpState>>, BufVec) -> Op + 'static,
+ {
+ Self::state(self)
+ .borrow_mut()
+ .op_state
+ .borrow_mut()
+ .op_table
+ .register_op(name, op_fn)
+ }
+
/// Registers a callback on the isolate when the memory limits are approached.
/// Use this to prevent V8 from crashing the process when reaching the limit.
///
@@ -1283,8 +1295,6 @@ impl JsRuntime {
pub mod tests {
use super::*;
use crate::modules::ModuleSourceFuture;
- use crate::ops::*;
- use crate::BasicState;
use crate::BufVec;
use futures::future::lazy;
use futures::FutureExt;
@@ -1328,89 +1338,89 @@ pub mod tests {
OverflowResAsync,
}
- struct TestOpRouter {
+ struct TestState {
mode: Mode,
dispatch_count: Arc<AtomicUsize>,
}
- impl OpRouter for TestOpRouter {
- fn route_op(self: Rc<Self>, op_id: OpId, bufs: BufVec) -> Op {
- if op_id != 1 {
- return Op::NotFound;
+ fn dispatch(op_state: Rc<RefCell<OpState>>, bufs: BufVec) -> Op {
+ let op_state_ = op_state.borrow();
+ let test_state = op_state_.borrow::<TestState>();
+ test_state.dispatch_count.fetch_add(1, Ordering::Relaxed);
+ match test_state.mode {
+ Mode::Async => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 1);
+ assert_eq!(bufs[0][0], 42);
+ let buf = vec![43u8].into_boxed_slice();
+ Op::Async(futures::future::ready(buf).boxed())
}
- self.dispatch_count.fetch_add(1, Ordering::Relaxed);
- match self.mode {
- Mode::Async => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 1);
- assert_eq!(bufs[0][0], 42);
- let buf = vec![43u8].into_boxed_slice();
- Op::Async(futures::future::ready(buf).boxed())
- }
- Mode::AsyncUnref => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 1);
- assert_eq!(bufs[0][0], 42);
- let fut = async {
- // This future never finish.
- futures::future::pending::<()>().await;
- vec![43u8].into_boxed_slice()
- };
- Op::AsyncUnref(fut.boxed())
- }
- Mode::AsyncZeroCopy(count) => {
- assert_eq!(bufs.len(), count as usize);
- bufs.iter().enumerate().for_each(|(idx, buf)| {
- assert_eq!(buf.len(), 1);
- assert_eq!(idx, buf[0] as usize);
- });
-
- let buf = vec![43u8].into_boxed_slice();
- Op::Async(futures::future::ready(buf).boxed())
- }
- Mode::OverflowReqSync => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 100 * 1024 * 1024);
- let buf = vec![43u8].into_boxed_slice();
- Op::Sync(buf)
- }
- Mode::OverflowResSync => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 1);
- assert_eq!(bufs[0][0], 42);
- let mut vec = Vec::<u8>::new();
- vec.resize(100 * 1024 * 1024, 0);
- vec[0] = 99;
- let buf = vec.into_boxed_slice();
- Op::Sync(buf)
- }
- Mode::OverflowReqAsync => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 100 * 1024 * 1024);
- let buf = vec![43u8].into_boxed_slice();
- Op::Async(futures::future::ready(buf).boxed())
- }
- Mode::OverflowResAsync => {
- assert_eq!(bufs.len(), 1);
- assert_eq!(bufs[0].len(), 1);
- assert_eq!(bufs[0][0], 42);
- let mut vec = Vec::<u8>::new();
- vec.resize(100 * 1024 * 1024, 0);
- vec[0] = 4;
- let buf = vec.into_boxed_slice();
- Op::Async(futures::future::ready(buf).boxed())
- }
+ Mode::AsyncUnref => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 1);
+ assert_eq!(bufs[0][0], 42);
+ let fut = async {
+ // This future never finish.
+ futures::future::pending::<()>().await;
+ vec![43u8].into_boxed_slice()
+ };
+ Op::AsyncUnref(fut.boxed())
+ }
+ Mode::AsyncZeroCopy(count) => {
+ assert_eq!(bufs.len(), count as usize);
+ bufs.iter().enumerate().for_each(|(idx, buf)| {
+ assert_eq!(buf.len(), 1);
+ assert_eq!(idx, buf[0] as usize);
+ });
+
+ let buf = vec![43u8].into_boxed_slice();
+ Op::Async(futures::future::ready(buf).boxed())
+ }
+ Mode::OverflowReqSync => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 100 * 1024 * 1024);
+ let buf = vec![43u8].into_boxed_slice();
+ Op::Sync(buf)
+ }
+ Mode::OverflowResSync => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 1);
+ assert_eq!(bufs[0][0], 42);
+ let mut vec = Vec::<u8>::new();
+ vec.resize(100 * 1024 * 1024, 0);
+ vec[0] = 99;
+ let buf = vec.into_boxed_slice();
+ Op::Sync(buf)
+ }
+ Mode::OverflowReqAsync => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 100 * 1024 * 1024);
+ let buf = vec![43u8].into_boxed_slice();
+ Op::Async(futures::future::ready(buf).boxed())
+ }
+ Mode::OverflowResAsync => {
+ assert_eq!(bufs.len(), 1);
+ assert_eq!(bufs[0].len(), 1);
+ assert_eq!(bufs[0][0], 42);
+ let mut vec = Vec::<u8>::new();
+ vec.resize(100 * 1024 * 1024, 0);
+ vec[0] = 4;
+ let buf = vec.into_boxed_slice();
+ Op::Async(futures::future::ready(buf).boxed())
}
}
}
fn setup(mode: Mode) -> (JsRuntime, Arc<AtomicUsize>) {
let dispatch_count = Arc::new(AtomicUsize::new(0));
- let test_state = Rc::new(TestOpRouter {
+ let mut runtime = JsRuntime::new(StartupData::None, false);
+ let op_state = runtime.op_state();
+ op_state.borrow_mut().put(TestState {
mode,
dispatch_count: dispatch_count.clone(),
});
- let mut runtime = JsRuntime::new(test_state, StartupData::None, false);
+
+ runtime.register_op("test", dispatch);
js_check(runtime.execute(
"setup.js",
@@ -1774,8 +1784,7 @@ pub mod tests {
#[test]
fn syntax_error() {
- let mut runtime =
- JsRuntime::new(BasicState::new(), StartupData::None, false);
+ let mut runtime = JsRuntime::new(StartupData::None, false);
let src = "hocuspocus(";
let r = runtime.execute("i.js", src);
let e = r.unwrap_err();
@@ -1800,29 +1809,27 @@ pub mod tests {
#[test]
fn will_snapshot() {
let snapshot = {
- let mut runtime =
- JsRuntime::new(BasicState::new(), StartupData::None, true);
+ let mut runtime = JsRuntime::new(StartupData::None, true);
js_check(runtime.execute("a.js", "a = 1 + 2"));
runtime.snapshot()
};
let startup_data = StartupData::Snapshot(Snapshot::JustCreated(snapshot));
- let mut runtime2 = JsRuntime::new(BasicState::new(), startup_data, false);
+ let mut runtime2 = JsRuntime::new(startup_data, false);
js_check(runtime2.execute("check.js", "if (a != 3) throw Error('x')"));
}
#[test]
fn test_from_boxed_snapshot() {
let snapshot = {
- let mut runtime =
- JsRuntime::new(BasicState::new(), StartupData::None, true);
+ let mut runtime = JsRuntime::new(StartupData::None, true);
js_check(runtime.execute("a.js", "a = 1 + 2"));
let snap: &[u8] = &*runtime.snapshot();
Vec::from(snap).into_boxed_slice()
};
let startup_data = StartupData::Snapshot(Snapshot::Boxed(snapshot));
- let mut runtime2 = JsRuntime::new(BasicState::new(), startup_data, false);
+ let mut runtime2 = JsRuntime::new(startup_data, false);
js_check(runtime2.execute("check.js", "if (a != 3) throw Error('x')"));
}
@@ -1832,11 +1839,8 @@ pub mod tests {
initial: 0,
max: 20 * 1024, // 20 kB
};
- let mut runtime = JsRuntime::with_heap_limits(
- BasicState::new(),
- StartupData::None,
- heap_limits,
- );
+ let mut runtime =
+ JsRuntime::with_heap_limits(StartupData::None, heap_limits);
let cb_handle = runtime.thread_safe_handle();
let callback_invoke_count = Rc::new(AtomicUsize::default());
@@ -1864,8 +1868,7 @@ pub mod tests {
#[test]
fn test_heap_limit_cb_remove() {
- let mut runtime =
- JsRuntime::new(BasicState::new(), StartupData::None, false);
+ let mut runtime = JsRuntime::new(StartupData::None, false);
runtime.add_near_heap_limit_callback(|current_limit, _initial_limit| {
current_limit * 2
@@ -1880,11 +1883,8 @@ pub mod tests {
initial: 0,
max: 20 * 1024, // 20 kB
};
- let mut runtime = JsRuntime::with_heap_limits(
- BasicState::new(),
- StartupData::None,
- heap_limits,
- );
+ let mut runtime =
+ JsRuntime::with_heap_limits(StartupData::None, heap_limits);
let cb_handle = runtime.thread_safe_handle();
let callback_invoke_count_first = Rc::new(AtomicUsize::default());
@@ -1952,13 +1952,12 @@ pub mod tests {
}
let loader = Rc::new(ModsLoader::default());
- let state = BasicState::new();
let resolve_count = loader.count.clone();
let dispatch_count = Arc::new(AtomicUsize::new(0));
let dispatch_count_ = dispatch_count.clone();
- let dispatcher = move |_state: Rc<BasicState>, bufs: BufVec| -> Op {
+ let dispatcher = move |_state: Rc<RefCell<OpState>>, bufs: BufVec| -> Op {
dispatch_count_.fetch_add(1, Ordering::Relaxed);
assert_eq!(bufs.len(), 1);
assert_eq!(bufs[0].len(), 1);
@@ -1966,10 +1965,10 @@ pub mod tests {
let buf = [43u8, 0, 0, 0][..].into();
Op::Async(futures::future::ready(buf).boxed())
};
- state.register_op("test", dispatcher);
let mut runtime =
- JsRuntime::new_with_loader(loader, state, StartupData::None, false);
+ JsRuntime::new_with_loader(loader, StartupData::None, false);
+ runtime.register_op("test", dispatcher);
js_check(runtime.execute(
"setup.js",
@@ -2063,12 +2062,8 @@ pub mod tests {
run_in_task(|cx| {
let loader = Rc::new(DynImportErrLoader::default());
let count = loader.count.clone();
- let mut runtime = JsRuntime::new_with_loader(
- loader,
- BasicState::new(),
- StartupData::None,
- false,
- );
+ let mut runtime =
+ JsRuntime::new_with_loader(loader, StartupData::None, false);
js_check(runtime.execute(
"file:///dyn_import2.js",
@@ -2145,12 +2140,8 @@ pub mod tests {
let prepare_load_count = loader.prepare_load_count.clone();
let resolve_count = loader.resolve_count.clone();
let load_count = loader.load_count.clone();
- let mut runtime = JsRuntime::new_with_loader(
- loader,
- BasicState::new(),
- StartupData::None,
- false,
- );
+ let mut runtime =
+ JsRuntime::new_with_loader(loader, StartupData::None, false);
// Dynamically import mod_b
js_check(runtime.execute(
@@ -2190,12 +2181,8 @@ pub mod tests {
run_in_task(|cx| {
let loader = Rc::new(DynImportOkLoader::default());
let prepare_load_count = loader.prepare_load_count.clone();
- let mut runtime = JsRuntime::new_with_loader(
- loader,
- BasicState::new(),
- StartupData::None,
- false,
- );
+ let mut runtime =
+ JsRuntime::new_with_loader(loader, StartupData::None, false);
js_check(runtime.execute(
"file:///dyn_import3.js",
r#"
@@ -2246,12 +2233,8 @@ pub mod tests {
}
let loader = std::rc::Rc::new(ModsLoader::default());
- let mut runtime = JsRuntime::new_with_loader(
- loader,
- BasicState::new(),
- StartupData::None,
- true,
- );
+ let mut runtime =
+ JsRuntime::new_with_loader(loader, StartupData::None, true);
let specifier = ModuleSpecifier::resolve_url("file:///main.js").unwrap();
let source_code = "Deno.core.print('hello\\n')".to_string();