diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2020-09-10 09:57:45 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-10 09:57:45 -0400 |
commit | 7c2e7c660804afca823d60e6496aa853f75db16c (patch) | |
tree | b7746b181c1564c6b1abd2e906662f9e6b008417 /core/runtime.rs | |
parent | 6f70e6e72ba2d5c1de7495adac37c1e4f4e86b24 (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.rs | 251 |
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(); |