mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
refactor: move lifecycle events dispatch to Rust (#23358)
This commit moves logic of dispatching lifecycle events ( "load", "beforeunload", "unload") to be triggered from Rust. Before that we were executing scripts from Rust, but now we are storing references to functions from "99_main.js" and calling them directly. Prerequisite for https://github.com/denoland/deno/issues/23342
This commit is contained in:
parent
f36a8951a4
commit
a080acc1b4
5 changed files with 112 additions and 71 deletions
|
@ -26,7 +26,6 @@ use deno_core::error::JsError;
|
|||
use deno_core::futures::future;
|
||||
use deno_core::futures::stream;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::serde_v8;
|
||||
use deno_core::unsync::spawn;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
|
@ -220,7 +219,7 @@ async fn bench_specifier_inner(
|
|||
// Ensure that there are no pending exceptions before we start running tests
|
||||
worker.run_up_to_duration(Duration::from_millis(0)).await?;
|
||||
|
||||
worker.dispatch_load_event(located_script_name!())?;
|
||||
worker.dispatch_load_event()?;
|
||||
|
||||
let benchmarks = {
|
||||
let state_rc = worker.js_runtime.op_state();
|
||||
|
@ -269,8 +268,8 @@ async fn bench_specifier_inner(
|
|||
|
||||
// Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the
|
||||
// event loop to continue beyond what's needed to await results.
|
||||
worker.dispatch_beforeunload_event(located_script_name!())?;
|
||||
worker.dispatch_unload_event(located_script_name!())?;
|
||||
worker.dispatch_beforeunload_event()?;
|
||||
worker.dispatch_unload_event()?;
|
||||
|
||||
// Ensure the worker has settled so we can catch any remaining unhandled rejections. We don't
|
||||
// want to wait forever here.
|
||||
|
|
|
@ -654,15 +654,15 @@ async fn test_specifier_inner(
|
|||
// Ensure that there are no pending exceptions before we start running tests
|
||||
worker.run_up_to_duration(Duration::from_millis(0)).await?;
|
||||
|
||||
worker.dispatch_load_event(located_script_name!())?;
|
||||
worker.dispatch_load_event()?;
|
||||
|
||||
run_tests_for_worker(&mut worker, &specifier, &options, &fail_fast_tracker)
|
||||
.await?;
|
||||
|
||||
// Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the
|
||||
// event loop to continue beyond what's needed to await results.
|
||||
worker.dispatch_beforeunload_event(located_script_name!())?;
|
||||
worker.dispatch_unload_event(located_script_name!())?;
|
||||
worker.dispatch_beforeunload_event()?;
|
||||
worker.dispatch_unload_event()?;
|
||||
|
||||
// Ensure all output has been flushed
|
||||
_ = sender.flush();
|
||||
|
|
|
@ -10,7 +10,6 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::v8;
|
||||
|
@ -182,7 +181,7 @@ impl CliMainWorker {
|
|||
self.execute_main_module_possibly_with_npm().await?;
|
||||
}
|
||||
|
||||
self.worker.dispatch_load_event(located_script_name!())?;
|
||||
self.worker.dispatch_load_event()?;
|
||||
|
||||
loop {
|
||||
if let Some(hmr_runner) = maybe_hmr_runner.as_mut() {
|
||||
|
@ -213,15 +212,12 @@ impl CliMainWorker {
|
|||
.await?;
|
||||
}
|
||||
|
||||
if !self
|
||||
.worker
|
||||
.dispatch_beforeunload_event(located_script_name!())?
|
||||
{
|
||||
if !self.worker.dispatch_beforeunload_event()? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.worker.dispatch_unload_event(located_script_name!())?;
|
||||
self.worker.dispatch_unload_event()?;
|
||||
|
||||
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
|
||||
self
|
||||
|
@ -268,10 +264,7 @@ impl CliMainWorker {
|
|||
/// respectively.
|
||||
pub async fn execute(&mut self) -> Result<(), AnyError> {
|
||||
self.inner.execute_main_module_possibly_with_npm().await?;
|
||||
self
|
||||
.inner
|
||||
.worker
|
||||
.dispatch_load_event(located_script_name!())?;
|
||||
self.inner.worker.dispatch_load_event()?;
|
||||
self.pending_unload = true;
|
||||
|
||||
let result = loop {
|
||||
|
@ -279,11 +272,7 @@ impl CliMainWorker {
|
|||
Ok(()) => {}
|
||||
Err(error) => break Err(error),
|
||||
}
|
||||
match self
|
||||
.inner
|
||||
.worker
|
||||
.dispatch_beforeunload_event(located_script_name!())
|
||||
{
|
||||
match self.inner.worker.dispatch_beforeunload_event() {
|
||||
Ok(default_prevented) if default_prevented => {} // continue loop
|
||||
Ok(_) => break Ok(()),
|
||||
Err(error) => break Err(error),
|
||||
|
@ -293,10 +282,7 @@ impl CliMainWorker {
|
|||
|
||||
result?;
|
||||
|
||||
self
|
||||
.inner
|
||||
.worker
|
||||
.dispatch_unload_event(located_script_name!())?;
|
||||
self.inner.worker.dispatch_unload_event()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -305,10 +291,7 @@ impl CliMainWorker {
|
|||
impl Drop for FileWatcherModuleExecutor {
|
||||
fn drop(&mut self) {
|
||||
if self.pending_unload {
|
||||
let _ = self
|
||||
.inner
|
||||
.worker
|
||||
.dispatch_unload_event(located_script_name!());
|
||||
let _ = self.inner.worker.dispatch_unload_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -528,6 +528,20 @@ function processRejectionHandled(promise, reason) {
|
|||
}
|
||||
}
|
||||
|
||||
function dispatchLoadEvent() {
|
||||
globalThis_.dispatchEvent(new Event("load"));
|
||||
}
|
||||
|
||||
function dispatchBeforeUnloadEvent() {
|
||||
return globalThis_.dispatchEvent(
|
||||
new Event("beforeunload", { cancelable: true }),
|
||||
);
|
||||
}
|
||||
|
||||
function dispatchUnloadEvent() {
|
||||
globalThis_.dispatchEvent(new Event("unload"));
|
||||
}
|
||||
|
||||
let hasBootstrapped = false;
|
||||
// Delete the `console` object that V8 automaticaly adds onto the global wrapper
|
||||
// object on context creation. We don't want this console object to shadow the
|
||||
|
@ -995,6 +1009,9 @@ delete globalThis.nodeBootstrap;
|
|||
globalThis.bootstrap = {
|
||||
mainRuntime: bootstrapMainRuntime,
|
||||
workerRuntime: bootstrapWorkerRuntime,
|
||||
dispatchLoadEvent,
|
||||
dispatchUnloadEvent,
|
||||
dispatchBeforeUnloadEvent,
|
||||
};
|
||||
|
||||
event.setEventTargetData(globalThis);
|
||||
|
|
|
@ -11,7 +11,6 @@ use std::time::Instant;
|
|||
use deno_broadcast_channel::InMemoryBroadcastChannel;
|
||||
use deno_cache::CreateCache;
|
||||
use deno_cache::SqliteBackedCache;
|
||||
use deno_core::ascii_str;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::JsError;
|
||||
use deno_core::merge_op_metrics;
|
||||
|
@ -115,6 +114,9 @@ pub struct MainWorker {
|
|||
should_wait_for_inspector_session: bool,
|
||||
exit_code: ExitCode,
|
||||
bootstrap_fn_global: Option<v8::Global<v8::Function>>,
|
||||
dispatch_load_event_fn_global: v8::Global<v8::Function>,
|
||||
dispatch_beforeunload_event_fn_global: v8::Global<v8::Function>,
|
||||
dispatch_unload_event_fn_global: v8::Global<v8::Function>,
|
||||
}
|
||||
|
||||
pub struct WorkerOptions {
|
||||
|
@ -523,7 +525,12 @@ impl MainWorker {
|
|||
let inspector = js_runtime.inspector();
|
||||
op_state.borrow_mut().put(inspector);
|
||||
}
|
||||
let bootstrap_fn_global = {
|
||||
let (
|
||||
bootstrap_fn_global,
|
||||
dispatch_load_event_fn_global,
|
||||
dispatch_beforeunload_event_fn_global,
|
||||
dispatch_unload_event_fn_global,
|
||||
) = {
|
||||
let context = js_runtime.main_context();
|
||||
let scope = &mut js_runtime.handle_scope();
|
||||
let context_local = v8::Local::new(scope, context);
|
||||
|
@ -541,7 +548,40 @@ impl MainWorker {
|
|||
bootstrap_ns.get(scope, main_runtime_str.into()).unwrap();
|
||||
let bootstrap_fn =
|
||||
v8::Local::<v8::Function>::try_from(bootstrap_fn).unwrap();
|
||||
v8::Global::new(scope, bootstrap_fn)
|
||||
let dispatch_load_event_fn_str =
|
||||
v8::String::new_external_onebyte_static(scope, b"dispatchLoadEvent")
|
||||
.unwrap();
|
||||
let dispatch_load_event_fn = bootstrap_ns
|
||||
.get(scope, dispatch_load_event_fn_str.into())
|
||||
.unwrap();
|
||||
let dispatch_load_event_fn =
|
||||
v8::Local::<v8::Function>::try_from(dispatch_load_event_fn).unwrap();
|
||||
let dispatch_beforeunload_event_fn_str =
|
||||
v8::String::new_external_onebyte_static(
|
||||
scope,
|
||||
b"dispatchBeforeUnloadEvent",
|
||||
)
|
||||
.unwrap();
|
||||
let dispatch_beforeunload_event_fn = bootstrap_ns
|
||||
.get(scope, dispatch_beforeunload_event_fn_str.into())
|
||||
.unwrap();
|
||||
let dispatch_beforeunload_event_fn =
|
||||
v8::Local::<v8::Function>::try_from(dispatch_beforeunload_event_fn)
|
||||
.unwrap();
|
||||
let dispatch_unload_event_fn_str =
|
||||
v8::String::new_external_onebyte_static(scope, b"dispatchUnloadEvent")
|
||||
.unwrap();
|
||||
let dispatch_unload_event_fn = bootstrap_ns
|
||||
.get(scope, dispatch_unload_event_fn_str.into())
|
||||
.unwrap();
|
||||
let dispatch_unload_event_fn =
|
||||
v8::Local::<v8::Function>::try_from(dispatch_unload_event_fn).unwrap();
|
||||
(
|
||||
v8::Global::new(scope, bootstrap_fn),
|
||||
v8::Global::new(scope, dispatch_load_event_fn),
|
||||
v8::Global::new(scope, dispatch_beforeunload_event_fn),
|
||||
v8::Global::new(scope, dispatch_unload_event_fn),
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
|
@ -551,6 +591,9 @@ impl MainWorker {
|
|||
.should_wait_for_inspector_session,
|
||||
exit_code,
|
||||
bootstrap_fn_global: Some(bootstrap_fn_global),
|
||||
dispatch_load_event_fn_global,
|
||||
dispatch_beforeunload_event_fn_global,
|
||||
dispatch_unload_event_fn_global,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -708,54 +751,53 @@ impl MainWorker {
|
|||
/// Dispatches "load" event to the JavaScript runtime.
|
||||
///
|
||||
/// Does not poll event loop, and thus not await any of the "load" event handlers.
|
||||
pub fn dispatch_load_event(
|
||||
&mut self,
|
||||
script_name: &'static str,
|
||||
) -> Result<(), AnyError> {
|
||||
self.js_runtime.execute_script(
|
||||
script_name,
|
||||
// NOTE(@bartlomieju): not using `globalThis` here, because user might delete
|
||||
// it. Instead we're using global `dispatchEvent` function which will
|
||||
// used a saved reference to global scope.
|
||||
ascii_str!("dispatchEvent(new Event('load'))"),
|
||||
)?;
|
||||
pub fn dispatch_load_event(&mut self) -> Result<(), AnyError> {
|
||||
let scope = &mut self.js_runtime.handle_scope();
|
||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||
let dispatch_load_event_fn =
|
||||
v8::Local::new(tc_scope, &self.dispatch_load_event_fn_global);
|
||||
let undefined = v8::undefined(tc_scope);
|
||||
dispatch_load_event_fn.call(tc_scope, undefined.into(), &[]);
|
||||
if let Some(exception) = tc_scope.exception() {
|
||||
let error = JsError::from_v8_exception(tc_scope, exception);
|
||||
return Err(error.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dispatches "unload" event to the JavaScript runtime.
|
||||
///
|
||||
/// Does not poll event loop, and thus not await any of the "unload" event handlers.
|
||||
pub fn dispatch_unload_event(
|
||||
&mut self,
|
||||
script_name: &'static str,
|
||||
) -> Result<(), AnyError> {
|
||||
self.js_runtime.execute_script(
|
||||
script_name,
|
||||
// NOTE(@bartlomieju): not using `globalThis` here, because user might delete
|
||||
// it. Instead we're using global `dispatchEvent` function which will
|
||||
// used a saved reference to global scope.
|
||||
ascii_str!("dispatchEvent(new Event('unload'))"),
|
||||
)?;
|
||||
pub fn dispatch_unload_event(&mut self) -> Result<(), AnyError> {
|
||||
let scope = &mut self.js_runtime.handle_scope();
|
||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||
let dispatch_unload_event_fn =
|
||||
v8::Local::new(tc_scope, &self.dispatch_unload_event_fn_global);
|
||||
let undefined = v8::undefined(tc_scope);
|
||||
dispatch_unload_event_fn.call(tc_scope, undefined.into(), &[]);
|
||||
if let Some(exception) = tc_scope.exception() {
|
||||
let error = JsError::from_v8_exception(tc_scope, exception);
|
||||
return Err(error.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dispatches "beforeunload" event to the JavaScript runtime. Returns a boolean
|
||||
/// indicating if the event was prevented and thus event loop should continue
|
||||
/// running.
|
||||
pub fn dispatch_beforeunload_event(
|
||||
&mut self,
|
||||
script_name: &'static str,
|
||||
) -> Result<bool, AnyError> {
|
||||
let value = self.js_runtime.execute_script(
|
||||
script_name,
|
||||
// NOTE(@bartlomieju): not using `globalThis` here, because user might delete
|
||||
// it. Instead we're using global `dispatchEvent` function which will
|
||||
// used a saved reference to global scope.
|
||||
ascii_str!(
|
||||
"dispatchEvent(new Event('beforeunload', { cancelable: true }));"
|
||||
),
|
||||
)?;
|
||||
let local_value = value.open(&mut self.js_runtime.handle_scope());
|
||||
Ok(local_value.is_false())
|
||||
pub fn dispatch_beforeunload_event(&mut self) -> Result<bool, AnyError> {
|
||||
let scope = &mut self.js_runtime.handle_scope();
|
||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||
let dispatch_beforeunload_event_fn =
|
||||
v8::Local::new(tc_scope, &self.dispatch_beforeunload_event_fn_global);
|
||||
let undefined = v8::undefined(tc_scope);
|
||||
let ret_val =
|
||||
dispatch_beforeunload_event_fn.call(tc_scope, undefined.into(), &[]);
|
||||
if let Some(exception) = tc_scope.exception() {
|
||||
let error = JsError::from_v8_exception(tc_scope, exception);
|
||||
return Err(error.into());
|
||||
}
|
||||
let ret_val = ret_val.unwrap();
|
||||
Ok(ret_val.is_false())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue