mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
Revert realms from deno_core (#16366)
This revert has been discussed at length out-of-band (including with @andreubotella). The realms work in impeding ongoing event loop and performance work. We very much want to land realms but it needs to wait until these lower-level refactors are complete. We hope to bring realms back in a couple weeks.
This commit is contained in:
parent
4b6168d5e3
commit
c007657cfd
5 changed files with 97 additions and 455 deletions
|
@ -7,7 +7,6 @@ use crate::modules::validate_import_assertions;
|
||||||
use crate::modules::ImportAssertionsKind;
|
use crate::modules::ImportAssertionsKind;
|
||||||
use crate::modules::ModuleMap;
|
use crate::modules::ModuleMap;
|
||||||
use crate::ops::OpCtx;
|
use crate::ops::OpCtx;
|
||||||
use crate::JsRealm;
|
|
||||||
use crate::JsRuntime;
|
use crate::JsRuntime;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::option::Option;
|
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`
|
// SAFETY: `CallbackScope` can be safely constructed from `&PromiseRejectMessage`
|
||||||
let scope = &mut unsafe { v8::CallbackScope::new(&message) };
|
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) =
|
if let Some(js_promise_reject_cb) = state.js_promise_reject_cb.clone() {
|
||||||
realm_state_rc.borrow().js_promise_reject_cb.clone()
|
|
||||||
{
|
|
||||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||||
let undefined: v8::Local<v8::Value> = v8::undefined(tc_scope).into();
|
let undefined: v8::Local<v8::Value> = v8::undefined(tc_scope).into();
|
||||||
let type_ = v8::Integer::new(tc_scope, message.get_event() as i32);
|
let type_ = v8::Integer::new(tc_scope, message.get_event() as i32);
|
||||||
let promise = message.get_promise();
|
let promise = message.get_promise();
|
||||||
|
drop(state); // Drop borrow, callbacks can call back into runtime.
|
||||||
|
|
||||||
let reason = match message.get_event() {
|
let reason = match message.get_event() {
|
||||||
PromiseRejectWithNoHandler
|
PromiseRejectWithNoHandler
|
||||||
|
@ -456,7 +455,6 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if has_unhandled_rejection_handler {
|
if has_unhandled_rejection_handler {
|
||||||
let state_rc = JsRuntime::state(tc_scope);
|
|
||||||
let mut state = state_rc.borrow_mut();
|
let mut state = state_rc.borrow_mut();
|
||||||
if let Some(pending_mod_evaluate) = state.pending_mod_evaluate.as_mut() {
|
if let Some(pending_mod_evaluate) = state.pending_mod_evaluate.as_mut() {
|
||||||
if !pending_mod_evaluate.has_evaluated {
|
if !pending_mod_evaluate.has_evaluated {
|
||||||
|
@ -467,8 +465,6 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let state_rc = JsRuntime::state(scope);
|
|
||||||
let mut state = state_rc.borrow_mut();
|
|
||||||
let promise = message.get_promise();
|
let promise = message.get_promise();
|
||||||
let promise_global = v8::Global::new(scope, promise);
|
let promise_global = v8::Global::new(scope, promise);
|
||||||
match message.get_event() {
|
match message.get_event() {
|
||||||
|
@ -487,7 +483,7 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
|
||||||
// Should not warn. See #1272
|
// Should not warn. See #1272
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This binding should be used if there's a custom console implementation
|
/// This binding should be used if there's a custom console implementation
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use crate::runtime::GetErrorClassFn;
|
use crate::runtime::GetErrorClassFn;
|
||||||
use crate::runtime::JsRealm;
|
|
||||||
use crate::runtime::JsRuntime;
|
use crate::runtime::JsRuntime;
|
||||||
use crate::source_map::apply_source_map;
|
use crate::source_map::apply_source_map;
|
||||||
use crate::source_map::get_source_line;
|
use crate::source_map::get_source_line;
|
||||||
|
@ -98,7 +97,7 @@ pub fn to_v8_error<'a>(
|
||||||
get_class: GetErrorClassFn,
|
get_class: GetErrorClassFn,
|
||||||
error: &Error,
|
error: &Error,
|
||||||
) -> v8::Local<'a, v8::Value> {
|
) -> v8::Local<'a, v8::Value> {
|
||||||
let cb = JsRealm::state_from_scope(scope)
|
let cb = JsRuntime::state(scope)
|
||||||
.borrow()
|
.borrow()
|
||||||
.js_build_custom_error_cb
|
.js_build_custom_error_cb
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -218,10 +217,10 @@ impl JsError {
|
||||||
let msg = v8::Exception::create_message(scope, exception);
|
let msg = v8::Exception::create_message(scope, exception);
|
||||||
|
|
||||||
let mut exception_message = None;
|
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 =
|
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 {
|
if let Some(format_exception_cb) = js_format_exception_cb {
|
||||||
let format_exception_cb = format_exception_cb.open(scope);
|
let format_exception_cb = format_exception_cb.open(scope);
|
||||||
let this = v8::undefined(scope).into();
|
let this = v8::undefined(scope).into();
|
||||||
|
@ -286,7 +285,6 @@ impl JsError {
|
||||||
let mut source_line = None;
|
let mut source_line = None;
|
||||||
let mut source_line_frame_index = None;
|
let mut source_line_frame_index = None;
|
||||||
{
|
{
|
||||||
let state_rc = JsRuntime::state(scope);
|
|
||||||
let state = &mut *state_rc.borrow_mut();
|
let state = &mut *state_rc.borrow_mut();
|
||||||
|
|
||||||
// When the stack frame array is empty, but the source location given by
|
// When the stack frame array is empty, but the source location given by
|
||||||
|
|
|
@ -8,7 +8,6 @@ use crate::ops_builtin::WasmStreamingResource;
|
||||||
use crate::resolve_url_or_path;
|
use crate::resolve_url_or_path;
|
||||||
use crate::serde_v8::from_v8;
|
use crate::serde_v8::from_v8;
|
||||||
use crate::source_map::apply_source_map as apply_source_map_;
|
use crate::source_map::apply_source_map as apply_source_map_;
|
||||||
use crate::JsRealm;
|
|
||||||
use crate::JsRuntime;
|
use crate::JsRuntime;
|
||||||
use crate::OpDecl;
|
use crate::OpDecl;
|
||||||
use crate::ZeroCopyBuf;
|
use crate::ZeroCopyBuf;
|
||||||
|
@ -74,14 +73,14 @@ fn to_v8_local_fn(
|
||||||
|
|
||||||
#[op(v8)]
|
#[op(v8)]
|
||||||
fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) {
|
fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) {
|
||||||
let context_state = JsRealm::state_from_scope(scope);
|
let state_rc = JsRuntime::state(scope);
|
||||||
context_state.borrow_mut().unrefed_ops.remove(&promise_id);
|
state_rc.borrow_mut().unrefed_ops.remove(&promise_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(v8)]
|
#[op(v8)]
|
||||||
fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) {
|
fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) {
|
||||||
let context_state = JsRealm::state_from_scope(scope);
|
let state_rc = JsRuntime::state(scope);
|
||||||
context_state.borrow_mut().unrefed_ops.insert(promise_id);
|
state_rc.borrow_mut().unrefed_ops.insert(promise_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op(v8)]
|
#[op(v8)]
|
||||||
|
@ -112,8 +111,8 @@ fn op_set_promise_reject_callback<'a>(
|
||||||
cb: serde_v8::Value,
|
cb: serde_v8::Value,
|
||||||
) -> Result<Option<serde_v8::Value<'a>>, Error> {
|
) -> Result<Option<serde_v8::Value<'a>>, Error> {
|
||||||
let cb = to_v8_fn(scope, cb)?;
|
let cb = to_v8_fn(scope, cb)?;
|
||||||
let realm_state_rc = JsRealm::state_from_scope(scope);
|
let state_rc = JsRuntime::state(scope);
|
||||||
let old = realm_state_rc.borrow_mut().js_promise_reject_cb.replace(cb);
|
let old = state_rc.borrow_mut().js_promise_reject_cb.replace(cb);
|
||||||
let old = old.map(|v| v8::Local::new(scope, v));
|
let old = old.map(|v| v8::Local::new(scope, v));
|
||||||
Ok(old.map(|v| from_v8(scope, v.into()).unwrap()))
|
Ok(old.map(|v| from_v8(scope, v.into()).unwrap()))
|
||||||
}
|
}
|
||||||
|
@ -675,28 +674,22 @@ fn op_set_wasm_streaming_callback(
|
||||||
cb: serde_v8::Value,
|
cb: serde_v8::Value,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let cb = to_v8_fn(scope, cb)?;
|
let cb = to_v8_fn(scope, cb)?;
|
||||||
let realm_state_rc = JsRealm::state_from_scope(scope);
|
let state_rc = JsRuntime::state(scope);
|
||||||
let mut realm_state = realm_state_rc.borrow_mut();
|
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
|
// 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
|
// borrow or move any local variables. Therefore, we're storing the JS
|
||||||
// callback in a JsRuntimeState slot.
|
// 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"));
|
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| {
|
scope.set_wasm_streaming_callback(|scope, arg, wasm_streaming| {
|
||||||
let (cb_handle, streaming_rid) = {
|
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 state_rc = JsRuntime::state(scope);
|
||||||
let streaming_rid = state_rc
|
let state = state_rc.borrow();
|
||||||
.borrow()
|
let cb_handle = state.js_wasm_streaming_cb.as_ref().unwrap().clone();
|
||||||
|
let streaming_rid = state
|
||||||
.op_state
|
.op_state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.resource_table
|
.resource_table
|
||||||
|
@ -829,11 +822,8 @@ fn op_set_format_exception_callback<'a>(
|
||||||
cb: serde_v8::Value<'a>,
|
cb: serde_v8::Value<'a>,
|
||||||
) -> Result<Option<serde_v8::Value<'a>>, Error> {
|
) -> Result<Option<serde_v8::Value<'a>>, Error> {
|
||||||
let cb = to_v8_fn(scope, cb)?;
|
let cb = to_v8_fn(scope, cb)?;
|
||||||
let realm_state_rc = JsRealm::state_from_scope(scope);
|
let state_rc = JsRuntime::state(scope);
|
||||||
let old = realm_state_rc
|
let old = state_rc.borrow_mut().js_format_exception_cb.replace(cb);
|
||||||
.borrow_mut()
|
|
||||||
.js_format_exception_cb
|
|
||||||
.replace(cb);
|
|
||||||
let old = old.map(|v| v8::Local::new(scope, v));
|
let old = old.map(|v| v8::Local::new(scope, v));
|
||||||
Ok(old.map(|v| from_v8(scope, v.into()).unwrap()))
|
Ok(old.map(|v| from_v8(scope, v.into()).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
461
core/runtime.rs
461
core/runtime.rs
|
@ -47,8 +47,7 @@ use std::task::Context;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use v8::OwnedIsolate;
|
use v8::OwnedIsolate;
|
||||||
|
|
||||||
type PendingOpFuture =
|
type PendingOpFuture = OpCall<(PromiseId, OpId, OpResult)>;
|
||||||
OpCall<(v8::Global<v8::Context>, PromiseId, OpId, OpResult)>;
|
|
||||||
|
|
||||||
pub enum Snapshot {
|
pub enum Snapshot {
|
||||||
Static(&'static [u8]),
|
Static(&'static [u8]),
|
||||||
|
@ -143,24 +142,18 @@ pub type SharedArrayBufferStore =
|
||||||
|
|
||||||
pub type CompiledWasmModuleStore = CrossIsolateStore<v8::CompiledWasmModule>;
|
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
|
/// Internal state for JsRuntime which is stored in one of v8::Isolate's
|
||||||
/// embedder slots.
|
/// embedder slots.
|
||||||
pub(crate) struct JsRuntimeState {
|
pub(crate) struct JsRuntimeState {
|
||||||
global_realm: Option<JsRealm>,
|
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_macrotask_cbs: Vec<v8::Global<v8::Function>>,
|
||||||
pub(crate) js_nexttick_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) has_tick_scheduled: bool,
|
||||||
|
pub(crate) js_wasm_streaming_cb: Option<v8::Global<v8::Function>>,
|
||||||
pub(crate) pending_promise_exceptions:
|
pub(crate) pending_promise_exceptions:
|
||||||
HashMap<v8::Global<v8::Promise>, v8::Global<v8::Value>>,
|
HashMap<v8::Global<v8::Promise>, v8::Global<v8::Value>>,
|
||||||
pub(crate) pending_dyn_mod_evaluate: Vec<DynImportModEvaluate>,
|
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_getter: Option<Box<dyn SourceMapGetter>>,
|
||||||
pub(crate) source_map_cache: SourceMapCache,
|
pub(crate) source_map_cache: SourceMapCache,
|
||||||
pub(crate) pending_ops: FuturesUnordered<PendingOpFuture>,
|
pub(crate) pending_ops: FuturesUnordered<PendingOpFuture>,
|
||||||
|
pub(crate) unrefed_ops: HashSet<i32>,
|
||||||
pub(crate) have_unpolled_ops: bool,
|
pub(crate) have_unpolled_ops: bool,
|
||||||
pub(crate) op_state: Rc<RefCell<OpState>>,
|
pub(crate) op_state: Rc<RefCell<OpState>>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -408,20 +402,24 @@ impl JsRuntime {
|
||||||
.module_loader
|
.module_loader
|
||||||
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
|
.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 {
|
let state_rc = Rc::new(RefCell::new(JsRuntimeState {
|
||||||
global_realm: Some(JsRealm(global_context.clone())),
|
global_realm: Some(JsRealm(global_context)),
|
||||||
known_realms,
|
|
||||||
pending_promise_exceptions: HashMap::new(),
|
pending_promise_exceptions: HashMap::new(),
|
||||||
pending_dyn_mod_evaluate: vec![],
|
pending_dyn_mod_evaluate: vec![],
|
||||||
pending_mod_evaluate: None,
|
pending_mod_evaluate: None,
|
||||||
dyn_module_evaluate_idle_counter: 0,
|
dyn_module_evaluate_idle_counter: 0,
|
||||||
|
js_recv_cb: None,
|
||||||
js_macrotask_cbs: vec![],
|
js_macrotask_cbs: vec![],
|
||||||
js_nexttick_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,
|
has_tick_scheduled: false,
|
||||||
|
js_wasm_streaming_cb: None,
|
||||||
source_map_getter: options.source_map_getter,
|
source_map_getter: options.source_map_getter,
|
||||||
source_map_cache: Default::default(),
|
source_map_cache: Default::default(),
|
||||||
pending_ops: FuturesUnordered::new(),
|
pending_ops: FuturesUnordered::new(),
|
||||||
|
unrefed_ops: HashSet::new(),
|
||||||
shared_array_buffer_store: options.shared_array_buffer_store,
|
shared_array_buffer_store: options.shared_array_buffer_store,
|
||||||
compiled_wasm_module_store: options.compiled_wasm_module_store,
|
compiled_wasm_module_store: options.compiled_wasm_module_store,
|
||||||
op_state: op_state.clone(),
|
op_state: op_state.clone(),
|
||||||
|
@ -436,10 +434,6 @@ impl JsRuntime {
|
||||||
Rc::into_raw(state_rc) as *mut c_void,
|
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)));
|
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state)));
|
||||||
isolate.set_data(
|
isolate.set_data(
|
||||||
Self::MODULE_MAP_DATA_OFFSET,
|
Self::MODULE_MAP_DATA_OFFSET,
|
||||||
|
@ -464,8 +458,7 @@ impl JsRuntime {
|
||||||
js_runtime.init_extension_js(&realm).unwrap();
|
js_runtime.init_extension_js(&realm).unwrap();
|
||||||
}
|
}
|
||||||
// Init callbacks (opresolve)
|
// Init callbacks (opresolve)
|
||||||
let global_realm = js_runtime.global_realm();
|
js_runtime.init_cbs();
|
||||||
js_runtime.init_cbs(&global_realm);
|
|
||||||
|
|
||||||
js_runtime
|
js_runtime
|
||||||
}
|
}
|
||||||
|
@ -528,22 +521,12 @@ impl JsRuntime {
|
||||||
&Self::state(self.v8_isolate()).borrow().op_ctxs,
|
&Self::state(self.v8_isolate()).borrow().op_ctxs,
|
||||||
self.built_from_snapshot,
|
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))
|
JsRealm::new(v8::Global::new(scope, context))
|
||||||
};
|
};
|
||||||
|
|
||||||
if !self.built_from_snapshot {
|
if !self.built_from_snapshot {
|
||||||
self.init_extension_js(&realm)?;
|
self.init_extension_js(&realm)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.init_cbs(&realm);
|
|
||||||
|
|
||||||
Ok(realm)
|
Ok(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,24 +695,20 @@ impl JsRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grabs a reference to core.js' opresolve & syncOpsCache()
|
/// Grabs a reference to core.js' opresolve & syncOpsCache()
|
||||||
fn init_cbs(&mut self, realm: &JsRealm) {
|
fn init_cbs(&mut self) {
|
||||||
let (recv_cb, build_custom_error_cb) = {
|
let scope = &mut self.handle_scope();
|
||||||
let scope = &mut realm.handle_scope(self.v8_isolate());
|
|
||||||
let recv_cb =
|
let recv_cb =
|
||||||
Self::grab_global::<v8::Function>(scope, "Deno.core.opresolve")
|
Self::grab_global::<v8::Function>(scope, "Deno.core.opresolve").unwrap();
|
||||||
.expect("Deno.core.opresolve is undefined in the realm");
|
|
||||||
let recv_cb = v8::Global::new(scope, recv_cb);
|
let recv_cb = v8::Global::new(scope, recv_cb);
|
||||||
let build_custom_error_cb =
|
let build_custom_error_cb =
|
||||||
Self::grab_global::<v8::Function>(scope, "Deno.core.buildCustomError")
|
Self::grab_global::<v8::Function>(scope, "Deno.core.buildCustomError")
|
||||||
.expect("Deno.core.buildCustomError is undefined in the realm");
|
.expect("Deno.core.buildCustomError is undefined in the realm");
|
||||||
let build_custom_error_cb = v8::Global::new(scope, build_custom_error_cb);
|
let build_custom_error_cb = v8::Global::new(scope, build_custom_error_cb);
|
||||||
(recv_cb, build_custom_error_cb)
|
// Put global handles in state
|
||||||
};
|
let state_rc = JsRuntime::state(scope);
|
||||||
// Put global handle in callback state
|
let mut state = state_rc.borrow_mut();
|
||||||
let state = realm.state(self.v8_isolate());
|
state.js_recv_cb.replace(recv_cb);
|
||||||
state.borrow_mut().js_recv_cb.replace(recv_cb);
|
|
||||||
state
|
state
|
||||||
.borrow_mut()
|
|
||||||
.js_build_custom_error_cb
|
.js_build_custom_error_cb
|
||||||
.replace(build_custom_error_cb);
|
.replace(build_custom_error_cb);
|
||||||
}
|
}
|
||||||
|
@ -794,25 +773,14 @@ impl JsRuntime {
|
||||||
let v8_isolate = self.v8_isolate();
|
let v8_isolate = self.v8_isolate();
|
||||||
Self::drop_state_and_module_map(v8_isolate);
|
Self::drop_state_and_module_map(v8_isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop other v8::Global handles before snapshotting
|
// 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();
|
let mut state = state.borrow_mut();
|
||||||
state.known_realms.clear();
|
std::mem::take(&mut state.js_recv_cb);
|
||||||
// Free up additional global handles before creating the snapshot
|
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_macrotask_cbs.clear();
|
||||||
state.js_nexttick_cbs.clear();
|
state.js_nexttick_cbs.clear();
|
||||||
}
|
}
|
||||||
|
@ -1125,16 +1093,8 @@ Pending dynamic modules:\n".to_string();
|
||||||
let state = state_rc.borrow_mut();
|
let state = state_rc.borrow_mut();
|
||||||
let module_map = module_map_rc.borrow();
|
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 {
|
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_imports: module_map.has_pending_dynamic_imports(),
|
||||||
has_pending_dyn_module_evaluation: !state
|
has_pending_dyn_module_evaluation: !state
|
||||||
.pending_dyn_mod_evaluate
|
.pending_dyn_mod_evaluate
|
||||||
|
@ -1903,67 +1863,8 @@ impl JsRuntime {
|
||||||
fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> {
|
fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> {
|
||||||
let state_rc = Self::state(self.v8_isolate());
|
let state_rc = Self::state(self.v8_isolate());
|
||||||
|
|
||||||
// We keep a list of promise IDs and OpResults per realm. Since v8::Context
|
let js_recv_cb_handle = state_rc.borrow().js_recv_cb.clone().unwrap();
|
||||||
// isn't hashable, `results_per_realm` is a vector of (context, list) tuples
|
let scope = &mut self.handle_scope();
|
||||||
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
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
results
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now handle actual ops.
|
|
||||||
{
|
|
||||||
let mut state = state_rc.borrow_mut();
|
|
||||||
state.have_unpolled_ops = false;
|
|
||||||
|
|
||||||
while let Poll::Ready(Some(item)) = state.pending_ops.poll_next_unpin(cx)
|
|
||||||
{
|
|
||||||
let (context, promise_id, op_id, resp) = item;
|
|
||||||
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),
|
// We return async responses to JS in unbounded batches (may change),
|
||||||
// each batch is a flat vector of tuples:
|
// each batch is a flat vector of tuples:
|
||||||
|
@ -1972,10 +1873,19 @@ impl JsRuntime {
|
||||||
// which contains a value OR an error, encoded as a tuple.
|
// which contains a value OR an error, encoded as a tuple.
|
||||||
// This batch is received in JS via the special `arguments` variable
|
// This batch is received in JS via the special `arguments` variable
|
||||||
// and then each tuple is used to resolve or reject promises
|
// and then each tuple is used to resolve or reject promises
|
||||||
let mut args = vec![];
|
let mut args: Vec<v8::Local<v8::Value>> = vec![];
|
||||||
|
|
||||||
for (promise_id, mut resp) in results.into_iter() {
|
// Now handle actual ops.
|
||||||
args.push(v8::Integer::new(scope, promise_id).into());
|
{
|
||||||
|
let mut state = state_rc.borrow_mut();
|
||||||
|
state.have_unpolled_ops = false;
|
||||||
|
|
||||||
|
while let Poll::Ready(Some(item)) = state.pending_ops.poll_next_unpin(cx)
|
||||||
|
{
|
||||||
|
let (promise_id, op_id, mut resp) = item;
|
||||||
|
state.unrefed_ops.remove(&promise_id);
|
||||||
|
state.op_state.borrow().tracker.track_async_completed(op_id);
|
||||||
|
args.push(v8::Integer::new(scope, promise_id as i32).into());
|
||||||
args.push(match resp.to_v8(scope) {
|
args.push(match resp.to_v8(scope) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => OpResult::Err(OpError::new(&|_| "TypeError", e.into()))
|
Err(e) => OpResult::Err(OpError::new(&|_| "TypeError", e.into()))
|
||||||
|
@ -1983,20 +1893,23 @@ impl JsRuntime {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||||
let js_recv_cb = js_recv_cb_handle.open(tc_scope);
|
let js_recv_cb = js_recv_cb_handle.open(tc_scope);
|
||||||
let this = v8::undefined(tc_scope).into();
|
let this = v8::undefined(tc_scope).into();
|
||||||
js_recv_cb.call(tc_scope, this, args.as_slice());
|
js_recv_cb.call(tc_scope, this, args.as_slice());
|
||||||
|
|
||||||
if let Some(exception) = tc_scope.exception() {
|
match tc_scope.exception() {
|
||||||
return exception_to_err_result(tc_scope, exception, false);
|
None => Ok(()),
|
||||||
|
Some(exception) => exception_to_err_result(tc_scope, exception, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drain_macrotasks(&mut self) -> Result<(), Error> {
|
fn drain_macrotasks(&mut self) -> Result<(), Error> {
|
||||||
let state = Self::state(self.v8_isolate());
|
let state = Self::state(self.v8_isolate());
|
||||||
|
|
||||||
|
@ -2108,28 +2021,6 @@ impl JsRealm {
|
||||||
&self.0
|
&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>(
|
pub fn handle_scope<'s>(
|
||||||
&self,
|
&self,
|
||||||
isolate: &'s mut v8::Isolate,
|
isolate: &'s mut v8::Isolate,
|
||||||
|
@ -2200,8 +2091,7 @@ pub fn queue_async_op(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
scope: &mut v8::HandleScope,
|
scope: &mut v8::HandleScope,
|
||||||
deferred: bool,
|
deferred: bool,
|
||||||
op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)>
|
op: impl Future<Output = (PromiseId, OpId, OpResult)> + 'static,
|
||||||
+ 'static,
|
|
||||||
) {
|
) {
|
||||||
match OpCall::eager(op) {
|
match OpCall::eager(op) {
|
||||||
// This calls promise.resolve() before the control goes back to userland JS. It works something
|
// 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();
|
// const p = setPromise();
|
||||||
// op.op_async(promiseId, ...); // Calls `opresolve`
|
// op.op_async(promiseId, ...); // Calls `opresolve`
|
||||||
// return p;
|
// return p;
|
||||||
EagerPollResult::Ready((context, promise_id, op_id, mut resp))
|
EagerPollResult::Ready((promise_id, op_id, mut resp)) if !deferred => {
|
||||||
if !deferred =>
|
|
||||||
{
|
|
||||||
let args = &[
|
let args = &[
|
||||||
v8::Integer::new(scope, promise_id).into(),
|
v8::Integer::new(scope, promise_id).into(),
|
||||||
resp.to_v8(scope).unwrap(),
|
resp.to_v8(scope).unwrap(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let realm = JsRealm::new(context);
|
|
||||||
let js_recv_cb_handle =
|
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);
|
state.borrow().tracker.track_async_completed(op_id);
|
||||||
|
|
||||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||||
|
@ -2361,11 +2248,11 @@ pub mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
{
|
{
|
||||||
let realm = runtime.global_realm();
|
|
||||||
let isolate = runtime.v8_isolate();
|
let isolate = runtime.v8_isolate();
|
||||||
let state_rc = JsRuntime::state(isolate);
|
let state_rc = JsRuntime::state(isolate);
|
||||||
assert_eq!(state_rc.borrow().pending_ops.len(), 2);
|
let state = state_rc.borrow();
|
||||||
assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 0);
|
assert_eq!(state.pending_ops.len(), 2);
|
||||||
|
assert_eq!(state.unrefed_ops.len(), 0);
|
||||||
}
|
}
|
||||||
runtime
|
runtime
|
||||||
.execute_script(
|
.execute_script(
|
||||||
|
@ -2377,11 +2264,11 @@ pub mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
{
|
{
|
||||||
let realm = runtime.global_realm();
|
|
||||||
let isolate = runtime.v8_isolate();
|
let isolate = runtime.v8_isolate();
|
||||||
let state_rc = JsRuntime::state(isolate);
|
let state_rc = JsRuntime::state(isolate);
|
||||||
assert_eq!(state_rc.borrow().pending_ops.len(), 2);
|
let state = state_rc.borrow();
|
||||||
assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 2);
|
assert_eq!(state.pending_ops.len(), 2);
|
||||||
|
assert_eq!(state.unrefed_ops.len(), 2);
|
||||||
}
|
}
|
||||||
runtime
|
runtime
|
||||||
.execute_script(
|
.execute_script(
|
||||||
|
@ -2393,11 +2280,11 @@ pub mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
{
|
{
|
||||||
let realm = runtime.global_realm();
|
|
||||||
let isolate = runtime.v8_isolate();
|
let isolate = runtime.v8_isolate();
|
||||||
let state_rc = JsRuntime::state(isolate);
|
let state_rc = JsRuntime::state(isolate);
|
||||||
assert_eq!(state_rc.borrow().pending_ops.len(), 2);
|
let state = state_rc.borrow();
|
||||||
assert_eq!(realm.state(isolate).borrow().unrefed_ops.len(), 0);
|
assert_eq!(state.pending_ops.len(), 2);
|
||||||
|
assert_eq!(state.unrefed_ops.len(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3623,54 +3510,6 @@ Deno.core.opAsync('op_async_serialize_object_with_numbers_as_keys', {
|
||||||
assert_eq!(2, PROMISE_REJECT.load(Ordering::Relaxed));
|
assert_eq!(2, PROMISE_REJECT.load(Ordering::Relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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]
|
#[tokio::test]
|
||||||
async fn test_set_promise_reject_callback_top_level_await() {
|
async fn test_set_promise_reject_callback_top_level_await() {
|
||||||
static PROMISE_REJECT: AtomicUsize = AtomicUsize::new(0);
|
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());
|
let scope = &mut realm.handle_scope(runtime.v8_isolate());
|
||||||
assert_eq!(ret, serde_v8::to_v8(scope, "Test").unwrap());
|
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(()))
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ fn codegen_v8_async(
|
||||||
quote! {
|
quote! {
|
||||||
let result = match result {
|
let result = match result {
|
||||||
Ok(fut) => fut.await,
|
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 {
|
} else {
|
||||||
|
@ -258,16 +258,11 @@ fn codegen_v8_async(
|
||||||
state.get_error_class_fn
|
state.get_error_class_fn
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = {
|
|
||||||
let local = scope.get_current_context();
|
|
||||||
#core::v8::Global::new(scope, local)
|
|
||||||
};
|
|
||||||
|
|
||||||
#pre_result
|
#pre_result
|
||||||
#core::_ops::queue_async_op(state, scope, #deferred, async move {
|
#core::_ops::queue_async_op(state, scope, #deferred, async move {
|
||||||
let result = #result_fut
|
let result = #result_fut
|
||||||
#result_wrapper
|
#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))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue