diff options
-rw-r--r-- | core/bindings.rs | 14 | ||||
-rw-r--r-- | core/error.rs | 8 | ||||
-rw-r--r-- | core/ops_builtin_v8.rs | 40 | ||||
-rw-r--r-- | core/runtime.rs | 479 | ||||
-rw-r--r-- | ops/lib.rs | 9 |
5 files changed, 96 insertions, 454 deletions
diff --git a/core/bindings.rs b/core/bindings.rs index a28a3a132..741ab6336 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -7,7 +7,6 @@ use crate::modules::validate_import_assertions; use crate::modules::ImportAssertionsKind; use crate::modules::ModuleMap; use crate::ops::OpCtx; -use crate::JsRealm; use crate::JsRuntime; use log::debug; use std::option::Option; @@ -425,15 +424,15 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { // SAFETY: `CallbackScope` can be safely constructed from `&PromiseRejectMessage` let scope = &mut unsafe { v8::CallbackScope::new(&message) }; - let realm_state_rc = JsRealm::state_from_scope(scope); + let state_rc = JsRuntime::state(scope); + let mut state = state_rc.borrow_mut(); - if let Some(js_promise_reject_cb) = - realm_state_rc.borrow().js_promise_reject_cb.clone() - { + if let Some(js_promise_reject_cb) = state.js_promise_reject_cb.clone() { let tc_scope = &mut v8::TryCatch::new(scope); let undefined: v8::Local<v8::Value> = v8::undefined(tc_scope).into(); let type_ = v8::Integer::new(tc_scope, message.get_event() as i32); let promise = message.get_promise(); + drop(state); // Drop borrow, callbacks can call back into runtime. let reason = match message.get_event() { PromiseRejectWithNoHandler @@ -456,7 +455,6 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { }; if has_unhandled_rejection_handler { - let state_rc = JsRuntime::state(tc_scope); let mut state = state_rc.borrow_mut(); if let Some(pending_mod_evaluate) = state.pending_mod_evaluate.as_mut() { if !pending_mod_evaluate.has_evaluated { @@ -467,8 +465,6 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { } } } else { - let state_rc = JsRuntime::state(scope); - let mut state = state_rc.borrow_mut(); let promise = message.get_promise(); let promise_global = v8::Global::new(scope, promise); match message.get_event() { @@ -487,7 +483,7 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { // Should not warn. See #1272 } } - }; + } } /// This binding should be used if there's a custom console implementation diff --git a/core/error.rs b/core/error.rs index 626f9b6f2..7df5c1efa 100644 --- a/core/error.rs +++ b/core/error.rs @@ -1,7 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use crate::runtime::GetErrorClassFn; -use crate::runtime::JsRealm; use crate::runtime::JsRuntime; use crate::source_map::apply_source_map; use crate::source_map::get_source_line; @@ -98,7 +97,7 @@ pub fn to_v8_error<'a>( get_class: GetErrorClassFn, error: &Error, ) -> v8::Local<'a, v8::Value> { - let cb = JsRealm::state_from_scope(scope) + let cb = JsRuntime::state(scope) .borrow() .js_build_custom_error_cb .clone() @@ -218,10 +217,10 @@ impl JsError { let msg = v8::Exception::create_message(scope, exception); let mut exception_message = None; - let realm_state_rc = JsRealm::state_from_scope(scope); + let state_rc = JsRuntime::state(scope); let js_format_exception_cb = - realm_state_rc.borrow().js_format_exception_cb.clone(); + state_rc.borrow().js_format_exception_cb.clone(); if let Some(format_exception_cb) = js_format_exception_cb { let format_exception_cb = format_exception_cb.open(scope); let this = v8::undefined(scope).into(); @@ -286,7 +285,6 @@ impl JsError { let mut source_line = None; let mut source_line_frame_index = None; { - let state_rc = JsRuntime::state(scope); let state = &mut *state_rc.borrow_mut(); // When the stack frame array is empty, but the source location given by diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs index 8629bbdbe..29ba4ec2b 100644 --- a/core/ops_builtin_v8.rs +++ b/core/ops_builtin_v8.rs @@ -8,7 +8,6 @@ use crate::ops_builtin::WasmStreamingResource; use crate::resolve_url_or_path; use crate::serde_v8::from_v8; use crate::source_map::apply_source_map as apply_source_map_; -use crate::JsRealm; use crate::JsRuntime; use crate::OpDecl; use crate::ZeroCopyBuf; @@ -74,14 +73,14 @@ fn to_v8_local_fn( #[op(v8)] fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.borrow_mut().unrefed_ops.remove(&promise_id); + let state_rc = JsRuntime::state(scope); + state_rc.borrow_mut().unrefed_ops.remove(&promise_id); } #[op(v8)] fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.borrow_mut().unrefed_ops.insert(promise_id); + let state_rc = JsRuntime::state(scope); + state_rc.borrow_mut().unrefed_ops.insert(promise_id); } #[op(v8)] @@ -112,8 +111,8 @@ fn op_set_promise_reject_callback<'a>( cb: serde_v8::Value, ) -> Result<Option<serde_v8::Value<'a>>, Error> { let cb = to_v8_fn(scope, cb)?; - let realm_state_rc = JsRealm::state_from_scope(scope); - let old = realm_state_rc.borrow_mut().js_promise_reject_cb.replace(cb); + let state_rc = JsRuntime::state(scope); + let old = state_rc.borrow_mut().js_promise_reject_cb.replace(cb); let old = old.map(|v| v8::Local::new(scope, v)); Ok(old.map(|v| from_v8(scope, v.into()).unwrap())) } @@ -675,28 +674,22 @@ fn op_set_wasm_streaming_callback( cb: serde_v8::Value, ) -> Result<(), Error> { let cb = to_v8_fn(scope, cb)?; - let realm_state_rc = JsRealm::state_from_scope(scope); - let mut realm_state = realm_state_rc.borrow_mut(); + let state_rc = JsRuntime::state(scope); + let mut state = state_rc.borrow_mut(); // The callback to pass to the v8 API has to be a unit type, so it can't // borrow or move any local variables. Therefore, we're storing the JS // callback in a JsRuntimeState slot. - if realm_state.js_wasm_streaming_cb.is_some() { + if state.js_wasm_streaming_cb.is_some() { return Err(type_error("op_set_wasm_streaming_callback already called")); } - realm_state.js_wasm_streaming_cb = Some(cb); + state.js_wasm_streaming_cb = Some(cb); scope.set_wasm_streaming_callback(|scope, arg, wasm_streaming| { let (cb_handle, streaming_rid) = { - let realm_state_rc = JsRealm::state_from_scope(scope); - let cb_handle = realm_state_rc - .borrow() - .js_wasm_streaming_cb - .as_ref() - .unwrap() - .clone(); let state_rc = JsRuntime::state(scope); - let streaming_rid = state_rc - .borrow() + let state = state_rc.borrow(); + let cb_handle = state.js_wasm_streaming_cb.as_ref().unwrap().clone(); + let streaming_rid = state .op_state .borrow_mut() .resource_table @@ -829,11 +822,8 @@ fn op_set_format_exception_callback<'a>( cb: serde_v8::Value<'a>, ) -> Result<Option<serde_v8::Value<'a>>, Error> { let cb = to_v8_fn(scope, cb)?; - let realm_state_rc = JsRealm::state_from_scope(scope); - let old = realm_state_rc - .borrow_mut() - .js_format_exception_cb - .replace(cb); + let state_rc = JsRuntime::state(scope); + let old = state_rc.borrow_mut().js_format_exception_cb.replace(cb); let old = old.map(|v| v8::Local::new(scope, v)); Ok(old.map(|v| from_v8(scope, v.into()).unwrap())) } diff --git a/core/runtime.rs b/core/runtime.rs index 60e187af5..c1ef6d1a4 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -47,8 +47,7 @@ use std::task::Context; use std::task::Poll; use v8::OwnedIsolate; -type PendingOpFuture = - OpCall<(v8::Global<v8::Context>, PromiseId, OpId, OpResult)>; +type PendingOpFuture = OpCall<(PromiseId, OpId, OpResult)>; pub enum Snapshot { Static(&'static [u8]), @@ -143,24 +142,18 @@ pub type SharedArrayBufferStore = pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>; -#[derive(Default)] -pub(crate) struct ContextState { - js_recv_cb: Option<v8::Global<v8::Function>>, - pub(crate) js_build_custom_error_cb: Option<v8::Global<v8::Function>>, - pub(crate) js_promise_reject_cb: Option<v8::Global<v8::Function>>, - pub(crate) js_format_exception_cb: Option<v8::Global<v8::Function>>, - pub(crate) js_wasm_streaming_cb: Option<v8::Global<v8::Function>>, - pub(crate) unrefed_ops: HashSet<i32>, -} - /// Internal state for JsRuntime which is stored in one of v8::Isolate's /// embedder slots. pub(crate) struct JsRuntimeState { global_realm: Option<JsRealm>, - known_realms: Vec<v8::Weak<v8::Context>>, + pub(crate) js_recv_cb: Option<v8::Global<v8::Function>>, pub(crate) js_macrotask_cbs: Vec<v8::Global<v8::Function>>, pub(crate) js_nexttick_cbs: Vec<v8::Global<v8::Function>>, + pub(crate) js_promise_reject_cb: Option<v8::Global<v8::Function>>, + pub(crate) js_format_exception_cb: Option<v8::Global<v8::Function>>, + pub(crate) js_build_custom_error_cb: Option<v8::Global<v8::Function>>, pub(crate) has_tick_scheduled: bool, + pub(crate) js_wasm_streaming_cb: Option<v8::Global<v8::Function>>, pub(crate) pending_promise_exceptions: HashMap<v8::Global<v8::Promise>, v8::Global<v8::Value>>, pub(crate) pending_dyn_mod_evaluate: Vec<DynImportModEvaluate>, @@ -171,6 +164,7 @@ pub(crate) struct JsRuntimeState { pub(crate) source_map_getter: Option<Box<dyn SourceMapGetter>>, pub(crate) source_map_cache: SourceMapCache, pub(crate) pending_ops: FuturesUnordered<PendingOpFuture>, + pub(crate) unrefed_ops: HashSet<i32>, pub(crate) have_unpolled_ops: bool, pub(crate) op_state: Rc<RefCell<OpState>>, #[allow(dead_code)] @@ -408,20 +402,24 @@ impl JsRuntime { .module_loader .unwrap_or_else(|| Rc::new(NoopModuleLoader)); - let known_realms = vec![v8::Weak::new(&mut isolate, &global_context)]; let state_rc = Rc::new(RefCell::new(JsRuntimeState { - global_realm: Some(JsRealm(global_context.clone())), - known_realms, + global_realm: Some(JsRealm(global_context)), pending_promise_exceptions: HashMap::new(), pending_dyn_mod_evaluate: vec![], pending_mod_evaluate: None, dyn_module_evaluate_idle_counter: 0, + js_recv_cb: None, js_macrotask_cbs: vec![], js_nexttick_cbs: vec![], + js_promise_reject_cb: None, + js_format_exception_cb: None, + js_build_custom_error_cb: None, has_tick_scheduled: false, + js_wasm_streaming_cb: None, source_map_getter: options.source_map_getter, source_map_cache: Default::default(), pending_ops: FuturesUnordered::new(), + unrefed_ops: HashSet::new(), shared_array_buffer_store: options.shared_array_buffer_store, compiled_wasm_module_store: options.compiled_wasm_module_store, op_state: op_state.clone(), @@ -436,10 +434,6 @@ impl JsRuntime { Rc::into_raw(state_rc) as *mut c_void, ); - global_context - .open(&mut isolate) - .set_slot(&mut isolate, Rc::<RefCell<ContextState>>::default()); - let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state))); isolate.set_data( Self::MODULE_MAP_DATA_OFFSET, @@ -464,8 +458,7 @@ impl JsRuntime { js_runtime.init_extension_js(&realm).unwrap(); } // Init callbacks (opresolve) - let global_realm = js_runtime.global_realm(); - js_runtime.init_cbs(&global_realm); + js_runtime.init_cbs(); js_runtime } @@ -528,22 +521,12 @@ impl JsRuntime { &Self::state(self.v8_isolate()).borrow().op_ctxs, self.built_from_snapshot, ); - context.set_slot(scope, Rc::<RefCell<ContextState>>::default()); - - Self::state(scope) - .borrow_mut() - .known_realms - .push(v8::Weak::new(scope, &context)); - JsRealm::new(v8::Global::new(scope, context)) }; if !self.built_from_snapshot { self.init_extension_js(&realm)?; } - - self.init_cbs(&realm); - Ok(realm) } @@ -712,24 +695,20 @@ impl JsRuntime { } /// Grabs a reference to core.js' opresolve & syncOpsCache() - fn init_cbs(&mut self, realm: &JsRealm) { - let (recv_cb, build_custom_error_cb) = { - let scope = &mut realm.handle_scope(self.v8_isolate()); - let recv_cb = - Self::grab_global::<v8::Function>(scope, "Deno.core.opresolve") - .expect("Deno.core.opresolve is undefined in the realm"); - let recv_cb = v8::Global::new(scope, recv_cb); - let build_custom_error_cb = - Self::grab_global::<v8::Function>(scope, "Deno.core.buildCustomError") - .expect("Deno.core.buildCustomError is undefined in the realm"); - let build_custom_error_cb = v8::Global::new(scope, build_custom_error_cb); - (recv_cb, build_custom_error_cb) - }; - // Put global handle in callback state - let state = realm.state(self.v8_isolate()); - state.borrow_mut().js_recv_cb.replace(recv_cb); + fn init_cbs(&mut self) { + let scope = &mut self.handle_scope(); + let recv_cb = + Self::grab_global::<v8::Function>(scope, "Deno.core.opresolve").unwrap(); + let recv_cb = v8::Global::new(scope, recv_cb); + let build_custom_error_cb = + Self::grab_global::<v8::Function>(scope, "Deno.core.buildCustomError") + .expect("Deno.core.buildCustomError is undefined in the realm"); + let build_custom_error_cb = v8::Global::new(scope, build_custom_error_cb); + // Put global handles in state + let state_rc = JsRuntime::state(scope); + let mut state = state_rc.borrow_mut(); + state.js_recv_cb.replace(recv_cb); state - .borrow_mut() .js_build_custom_error_cb .replace(build_custom_error_cb); } @@ -794,25 +773,14 @@ impl JsRuntime { let v8_isolate = self.v8_isolate(); Self::drop_state_and_module_map(v8_isolate); } - // Drop other v8::Global handles before snapshotting { - for weak_context in &state.borrow().known_realms { - if let Some(context) = weak_context.to_global(self.v8_isolate()) { - let realm = JsRealm::new(context.clone()); - let realm_state = realm.state(self.v8_isolate()); - std::mem::take(&mut realm_state.borrow_mut().js_recv_cb); - std::mem::take( - &mut realm_state.borrow_mut().js_build_custom_error_cb, - ); - context - .open(self.v8_isolate()) - .clear_all_slots(self.v8_isolate()); - } - } let mut state = state.borrow_mut(); - state.known_realms.clear(); - // Free up additional global handles before creating the snapshot + std::mem::take(&mut state.js_recv_cb); + std::mem::take(&mut state.js_promise_reject_cb); + std::mem::take(&mut state.js_format_exception_cb); + std::mem::take(&mut state.js_wasm_streaming_cb); + std::mem::take(&mut state.js_build_custom_error_cb); state.js_macrotask_cbs.clear(); state.js_nexttick_cbs.clear(); } @@ -1125,16 +1093,8 @@ Pending dynamic modules:\n".to_string(); let state = state_rc.borrow_mut(); let module_map = module_map_rc.borrow(); - let mut num_unrefed_ops = 0; - for weak_context in &state.known_realms { - if let Some(context) = weak_context.to_global(isolate) { - let realm = JsRealm::new(context); - num_unrefed_ops += realm.state(isolate).borrow().unrefed_ops.len(); - } - } - EventLoopPendingState { - has_pending_refed_ops: state.pending_ops.len() > num_unrefed_ops, + has_pending_refed_ops: state.pending_ops.len() > state.unrefed_ops.len(), has_pending_dyn_imports: module_map.has_pending_dynamic_imports(), has_pending_dyn_module_evaluation: !state .pending_dyn_mod_evaluate @@ -1903,30 +1863,17 @@ impl JsRuntime { fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> { let state_rc = Self::state(self.v8_isolate()); - // We keep a list of promise IDs and OpResults per realm. Since v8::Context - // isn't hashable, `results_per_realm` is a vector of (context, list) tuples - type ResultList = Vec<(i32, OpResult)>; - let mut results_per_realm: Vec<(v8::Global<v8::Context>, ResultList)> = { - let known_realms = &mut state_rc.borrow_mut().known_realms; - let mut results = Vec::with_capacity(known_realms.len()); - - // Avoid calling the method multiple times - let isolate = self.v8_isolate(); - - // Remove GC'd realms from `known_realms` at the same time as we populate - // `results` with those that are still alive. - known_realms.retain(|weak| { - if !weak.is_empty() { - let context = weak.to_global(isolate).unwrap(); - results.push((context, vec![])); - true - } else { - false - } - }); + let js_recv_cb_handle = state_rc.borrow().js_recv_cb.clone().unwrap(); + let scope = &mut self.handle_scope(); - results - }; + // We return async responses to JS in unbounded batches (may change), + // each batch is a flat vector of tuples: + // `[promise_id1, op_result1, promise_id2, op_result2, ...]` + // promise_id is a simple integer, op_result is an ops::OpResult + // which contains a value OR an error, encoded as a tuple. + // This batch is received in JS via the special `arguments` variable + // and then each tuple is used to resolve or reject promises + let mut args: Vec<v8::Local<v8::Value>> = vec![]; // Now handle actual ops. { @@ -1935,47 +1882,10 @@ impl JsRuntime { while let Poll::Ready(Some(item)) = state.pending_ops.poll_next_unpin(cx) { - let (context, promise_id, op_id, resp) = item; + let (promise_id, op_id, mut resp) = item; + state.unrefed_ops.remove(&promise_id); state.op_state.borrow().tracker.track_async_completed(op_id); - for (context2, results) in results_per_realm.iter_mut() { - if context == *context2 { - results.push((promise_id, resp)); - break; - } - } - JsRealm::new(context) - .state(self.v8_isolate()) - .borrow_mut() - .unrefed_ops - .remove(&promise_id); - } - } - - for (context, results) in results_per_realm { - if results.is_empty() { - continue; - } - - let realm = JsRealm::new(context); - let js_recv_cb_handle = realm - .state(self.v8_isolate()) - .borrow() - .js_recv_cb - .clone() - .unwrap(); - let scope = &mut realm.handle_scope(self.v8_isolate()); - - // We return async responses to JS in unbounded batches (may change), - // each batch is a flat vector of tuples: - // `[promise_id1, op_result1, promise_id2, op_result2, ...]` - // promise_id is a simple integer, op_result is an ops::OpResult - // which contains a value OR an error, encoded as a tuple. - // This batch is received in JS via the special `arguments` variable - // and then each tuple is used to resolve or reject promises - let mut args = vec![]; - - for (promise_id, mut resp) in results.into_iter() { - args.push(v8::Integer::new(scope, promise_id).into()); + args.push(v8::Integer::new(scope, promise_id as i32).into()); args.push(match resp.to_v8(scope) { Ok(v) => v, Err(e) => OpResult::Err(OpError::new(&|_| "TypeError", e.into())) @@ -1983,18 +1893,21 @@ impl JsRuntime { .unwrap(), }); } + } - let tc_scope = &mut v8::TryCatch::new(scope); - let js_recv_cb = js_recv_cb_handle.open(tc_scope); - let this = v8::undefined(tc_scope).into(); - js_recv_cb.call(tc_scope, this, args.as_slice()); - - if let Some(exception) = tc_scope.exception() { - return exception_to_err_result(tc_scope, exception, false); - } + if args.is_empty() { + return Ok(()); } - Ok(()) + let tc_scope = &mut v8::TryCatch::new(scope); + let js_recv_cb = js_recv_cb_handle.open(tc_scope); + let this = v8::undefined(tc_scope).into(); + js_recv_cb.call(tc_scope, this, args.as_slice()); + + match tc_scope.exception() { + None => Ok(()), + Some(exception) => exception_to_err_result(tc_scope, exception, false), + } } fn drain_macrotasks(&mut self) -> Result<(), Error> { @@ -2108,28 +2021,6 @@ impl JsRealm { &self.0 } - pub(crate) fn state( - &self, - isolate: &mut v8::Isolate, - ) -> Rc<RefCell<ContextState>> { - self - .context() - .open(isolate) - .get_slot::<Rc<RefCell<ContextState>>>(isolate) - .unwrap() - .clone() - } - - pub(crate) fn state_from_scope( - scope: &mut v8::HandleScope, - ) -> Rc<RefCell<ContextState>> { - let context = scope.get_current_context(); - context - .get_slot::<Rc<RefCell<ContextState>>>(scope) - .unwrap() - .clone() - } - pub fn handle_scope<'s>( &self, isolate: &'s mut v8::Isolate, @@ -2200,8 +2091,7 @@ pub fn queue_async_op( state: Rc<RefCell<OpState>>, scope: &mut v8::HandleScope, deferred: bool, - op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)> - + 'static, + op: impl Future<Output = (PromiseId, OpId, OpResult)> + 'static, ) { match OpCall::eager(op) { // This calls promise.resolve() before the control goes back to userland JS. It works something @@ -2213,17 +2103,14 @@ pub fn queue_async_op( // const p = setPromise(); // op.op_async(promiseId, ...); // Calls `opresolve` // return p; - EagerPollResult::Ready((context, promise_id, op_id, mut resp)) - if !deferred => - { + EagerPollResult::Ready((promise_id, op_id, mut resp)) if !deferred => { let args = &[ v8::Integer::new(scope, promise_id).into(), resp.to_v8(scope).unwrap(), ]; - let realm = JsRealm::new(context); let js_recv_cb_handle = - realm.state(scope).borrow().js_recv_cb.clone().unwrap(); + JsRuntime::state(scope).borrow().js_recv_cb.clone().unwrap(); state.borrow().tracker.track_async_completed(op_id); let tc_scope = &mut v8::TryCatch::new(scope); @@ -2361,11 +2248,11 @@ pub mod tests { ) .unwrap(); { - let realm = runtime.global_realm(); let isolate = runtime.v8_isolate(); let state_rc = JsRuntime::state(isolate); - assert_eq!(state_rc.borrow().pending_ops.len(), 2); - assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 0); + let state = state_rc.borrow(); + assert_eq!(state.pending_ops.len(), 2); + assert_eq!(state.unrefed_ops.len(), 0); } runtime .execute_script( @@ -2377,11 +2264,11 @@ pub mod tests { ) .unwrap(); { - let realm = runtime.global_realm(); let isolate = runtime.v8_isolate(); let state_rc = JsRuntime::state(isolate); - assert_eq!(state_rc.borrow().pending_ops.len(), 2); - assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 2); + let state = state_rc.borrow(); + assert_eq!(state.pending_ops.len(), 2); + assert_eq!(state.unrefed_ops.len(), 2); } runtime .execute_script( @@ -2393,11 +2280,11 @@ pub mod tests { ) .unwrap(); { - let realm = runtime.global_realm(); let isolate = runtime.v8_isolate(); let state_rc = JsRuntime::state(isolate); - assert_eq!(state_rc.borrow().pending_ops.len(), 2); - assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 0); + let state = state_rc.borrow(); + assert_eq!(state.pending_ops.len(), 2); + assert_eq!(state.unrefed_ops.len(), 0); } } @@ -3624,54 +3511,6 @@ Deno.core.opAsync('op_async_serialize_object_with_numbers_as_keys', { } #[tokio::test] - async fn test_set_promise_reject_callback_realms() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - let global_realm = runtime.global_realm(); - let realm1 = runtime.create_realm().unwrap(); - let realm2 = runtime.create_realm().unwrap(); - - let realm_expectations = &[ - (&global_realm, "global_realm", 42), - (&realm1, "realm1", 140), - (&realm2, "realm2", 720), - ]; - - // Set up promise reject callbacks. - for (realm, realm_name, number) in realm_expectations { - realm - .execute_script( - runtime.v8_isolate(), - "", - &format!( - r#" - globalThis.rejectValue = undefined; - Deno.core.setPromiseRejectCallback((_type, _promise, reason) => {{ - globalThis.rejectValue = `{realm_name}/${{reason}}`; - }}); - Deno.core.opAsync("op_void_async").then(() => Promise.reject({number})); - "#, - realm_name=realm_name, - number=number - ), - ) - .unwrap(); - } - - runtime.run_event_loop(false).await.unwrap(); - - for (realm, realm_name, number) in realm_expectations { - let reject_value = realm - .execute_script(runtime.v8_isolate(), "", "globalThis.rejectValue") - .unwrap(); - let scope = &mut realm.handle_scope(runtime.v8_isolate()); - let reject_value = v8::Local::new(scope, reject_value); - assert!(reject_value.is_string()); - let reject_value_string = reject_value.to_rust_string_lossy(scope); - assert_eq!(reject_value_string, format!("{}/{}", realm_name, number)); - } - } - - #[tokio::test] async fn test_set_promise_reject_callback_top_level_await() { static PROMISE_REJECT: AtomicUsize = AtomicUsize::new(0); @@ -3990,180 +3829,4 @@ Deno.core.opAsync('op_async_serialize_object_with_numbers_as_keys', { let scope = &mut realm.handle_scope(runtime.v8_isolate()); assert_eq!(ret, serde_v8::to_v8(scope, "Test").unwrap()); } - - #[test] - fn js_realm_sync_ops() { - // Test that returning a ZeroCopyBuf and throwing an exception from a sync - // op result in objects with prototypes from the right realm. Note that we - // don't test the result of returning structs, because they will be - // serialized to objects with null prototype. - - #[op] - fn op_test(fail: bool) -> Result<ZeroCopyBuf, Error> { - if !fail { - Ok(ZeroCopyBuf::empty()) - } else { - Err(crate::error::type_error("Test")) - } - } - - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![Extension::builder().ops(vec![op_test::decl()]).build()], - get_error_class_fn: Some(&|error| { - crate::error::get_custom_error_class(error).unwrap() - }), - ..Default::default() - }); - let new_realm = runtime.create_realm().unwrap(); - - // Test in both realms - for realm in [runtime.global_realm(), new_realm].into_iter() { - let ret = realm - .execute_script( - runtime.v8_isolate(), - "", - r#" - const buf = Deno.core.ops.op_test(false); - let err; - try { - Deno.core.ops.op_test(true); - } catch(e) { - err = e; - } - buf instanceof Uint8Array && buf.byteLength === 0 && - err instanceof TypeError && err.message === "Test" - "#, - ) - .unwrap(); - assert!(ret.open(runtime.v8_isolate()).is_true()); - } - } - - #[tokio::test] - async fn js_realm_async_ops() { - // Test that returning a ZeroCopyBuf and throwing an exception from a async - // op result in objects with prototypes from the right realm. Note that we - // don't test the result of returning structs, because they will be - // serialized to objects with null prototype. - - #[op] - async fn op_test(fail: bool) -> Result<ZeroCopyBuf, Error> { - if !fail { - Ok(ZeroCopyBuf::empty()) - } else { - Err(crate::error::type_error("Test")) - } - } - - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![Extension::builder().ops(vec![op_test::decl()]).build()], - get_error_class_fn: Some(&|error| { - crate::error::get_custom_error_class(error).unwrap() - }), - ..Default::default() - }); - - let global_realm = runtime.global_realm(); - let new_realm = runtime.create_realm().unwrap(); - - let mut rets = vec![]; - - // Test in both realms - for realm in [global_realm, new_realm].into_iter() { - let ret = realm - .execute_script( - runtime.v8_isolate(), - "", - r#" - (async function () { - const buf = await Deno.core.opAsync("op_test", false); - let err; - try { - await Deno.core.opAsync("op_test", true); - } catch(e) { - err = e; - } - return buf instanceof Uint8Array && buf.byteLength === 0 && - err instanceof TypeError && err.message === "Test" ; - })(); - "#, - ) - .unwrap(); - rets.push((realm, ret)); - } - - runtime.run_event_loop(false).await.unwrap(); - - for ret in rets { - let scope = &mut ret.0.handle_scope(runtime.v8_isolate()); - let value = v8::Local::new(scope, ret.1); - let promise = v8::Local::<v8::Promise>::try_from(value).unwrap(); - let result = promise.result(scope); - - assert!(result.is_boolean() && result.is_true()); - } - } - - #[tokio::test] - async fn js_realm_ref_unref_ops() { - run_in_task(|cx| { - // Never resolves. - #[op] - async fn op_pending() { - futures::future::pending().await - } - - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![Extension::builder() - .ops(vec![op_pending::decl()]) - .build()], - ..Default::default() - }); - let main_realm = runtime.global_realm(); - let other_realm = runtime.create_realm().unwrap(); - - main_realm - .execute_script( - runtime.v8_isolate(), - "", - "var promise = Deno.core.opAsync('op_pending');", - ) - .unwrap(); - other_realm - .execute_script( - runtime.v8_isolate(), - "", - "var promise = Deno.core.opAsync('op_pending');", - ) - .unwrap(); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - - main_realm - .execute_script( - runtime.v8_isolate(), - "", - r#" - let promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId"); - Deno.core.ops.op_unref_op(promise[promiseIdSymbol]); - "#, - ) - .unwrap(); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - - other_realm - .execute_script( - runtime.v8_isolate(), - "", - r#" - let promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId"); - Deno.core.ops.op_unref_op(promise[promiseIdSymbol]); - "#, - ) - .unwrap(); - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(())) - )); - }); - } } diff --git a/ops/lib.rs b/ops/lib.rs index e8ea1e969..0794bdf33 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -215,7 +215,7 @@ fn codegen_v8_async( quote! { let result = match result { Ok(fut) => fut.await, - Err(e) => return (context, promise_id, op_id, #core::_ops::to_op_result::<()>(get_class, Err(e))), + Err(e) => return (promise_id, op_id, #core::_ops::to_op_result::<()>(get_class, Err(e))), }; } } else { @@ -258,16 +258,11 @@ fn codegen_v8_async( state.get_error_class_fn }; - let context = { - let local = scope.get_current_context(); - #core::v8::Global::new(scope, local) - }; - #pre_result #core::_ops::queue_async_op(state, scope, #deferred, async move { let result = #result_fut #result_wrapper - (context, promise_id, op_id, #core::_ops::to_op_result(get_class, result)) + (promise_id, op_id, #core::_ops::to_op_result(get_class, result)) }); } } |