mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
feat(core): send "executionContextDestroyed" notification on program end (#16831)
This commit changes "JsRuntime" to send "executionContextDestroyed" notification when the program finishes and shows a prompt informing that runtime is waiting for inspector to disconnect.
This commit is contained in:
parent
6344b9e0a5
commit
d4f659d1d3
3 changed files with 73 additions and 17 deletions
|
@ -303,7 +303,6 @@ mod inspector {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let msg = ws_read_msg(&mut socket).await;
|
let msg = ws_read_msg(&mut socket).await;
|
||||||
println!("response msg 1 {}", msg);
|
|
||||||
assert_starts_with!(msg, r#"{"id":6,"result":{"debuggerId":"#);
|
assert_starts_with!(msg, r#"{"id":6,"result":{"debuggerId":"#);
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|
@ -312,7 +311,6 @@ mod inspector {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let msg = ws_read_msg(&mut socket).await;
|
let msg = ws_read_msg(&mut socket).await;
|
||||||
println!("response msg 2 {}", msg);
|
|
||||||
assert_eq!(msg, r#"{"id":31,"result":{}}"#);
|
assert_eq!(msg, r#"{"id":31,"result":{}}"#);
|
||||||
|
|
||||||
child.kill().unwrap();
|
child.kill().unwrap();
|
||||||
|
@ -546,6 +544,10 @@ mod inspector {
|
||||||
.filter(|s| !s.starts_with("Deno "));
|
.filter(|s| !s.starts_with("Deno "));
|
||||||
|
|
||||||
assert_stderr_for_inspect(&mut stderr_lines);
|
assert_stderr_for_inspect(&mut stderr_lines);
|
||||||
|
assert_eq!(
|
||||||
|
&stdout_lines.next().unwrap(),
|
||||||
|
"exit using ctrl+d, ctrl+c, or close()"
|
||||||
|
);
|
||||||
|
|
||||||
assert_inspector_messages(
|
assert_inspector_messages(
|
||||||
&mut socket_tx,
|
&mut socket_tx,
|
||||||
|
@ -564,11 +566,6 @@ mod inspector {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
&stdout_lines.next().unwrap(),
|
|
||||||
"exit using ctrl+d, ctrl+c, or close()"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_inspector_messages(
|
assert_inspector_messages(
|
||||||
&mut socket_tx,
|
&mut socket_tx,
|
||||||
&[
|
&[
|
||||||
|
@ -577,7 +574,6 @@ mod inspector {
|
||||||
&mut socket_rx,
|
&mut socket_rx,
|
||||||
&[r#"{"id":3,"result":{}}"#], &[]
|
&[r#"{"id":3,"result":{}}"#], &[]
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
assert_inspector_messages(
|
assert_inspector_messages(
|
||||||
&mut socket_tx,
|
&mut socket_tx,
|
||||||
&[
|
&[
|
||||||
|
@ -587,7 +583,6 @@ mod inspector {
|
||||||
&[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
|
&[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
|
||||||
&[],
|
&[],
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
assert_inspector_messages(
|
assert_inspector_messages(
|
||||||
&mut socket_tx,
|
&mut socket_tx,
|
||||||
&[
|
&[
|
||||||
|
@ -597,9 +592,7 @@ mod inspector {
|
||||||
&[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
|
&[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
|
||||||
&[r#"{"method":"Runtime.consoleAPICalled"#],
|
&[r#"{"method":"Runtime.consoleAPICalled"#],
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
assert_eq!(&stderr_lines.next().unwrap(), "done");
|
assert_eq!(&stderr_lines.next().unwrap(), "done");
|
||||||
|
|
||||||
drop(stdin);
|
drop(stdin);
|
||||||
child.wait().unwrap();
|
child.wait().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -905,6 +898,26 @@ mod inspector {
|
||||||
assert_eq!(&stdout_lines.next().unwrap(), "hello");
|
assert_eq!(&stdout_lines.next().unwrap(), "hello");
|
||||||
assert_eq!(&stdout_lines.next().unwrap(), "world");
|
assert_eq!(&stdout_lines.next().unwrap(), "world");
|
||||||
|
|
||||||
|
assert_inspector_messages(
|
||||||
|
&mut socket_tx,
|
||||||
|
&[],
|
||||||
|
&mut socket_rx,
|
||||||
|
&[],
|
||||||
|
&[
|
||||||
|
r#"{"method":"Debugger.resumed","params":{}}"#,
|
||||||
|
r#"{"method":"Runtime.consoleAPICalled","#,
|
||||||
|
r#"{"method":"Runtime.consoleAPICalled","#,
|
||||||
|
r#"{"method":"Runtime.executionContextDestroyed","params":{"executionContextId":1}}"#,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let line = &stdout_lines.next().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
line,
|
||||||
|
"Program finished. Waiting for inspector to disconnect to exit the process..."
|
||||||
|
);
|
||||||
|
|
||||||
child.kill().unwrap();
|
child.kill().unwrap();
|
||||||
child.wait().unwrap();
|
child.wait().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ use std::ptr::NonNull;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use v8::HandleScope;
|
||||||
|
|
||||||
pub enum InspectorMsgKind {
|
pub enum InspectorMsgKind {
|
||||||
Notification,
|
Notification,
|
||||||
|
@ -213,10 +214,28 @@ impl JsRuntimeInspector {
|
||||||
self__
|
self__
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context_destroyed(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut HandleScope,
|
||||||
|
context: v8::Global<v8::Context>,
|
||||||
|
) {
|
||||||
|
let context = v8::Local::new(scope, context);
|
||||||
|
self
|
||||||
|
.v8_inspector
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.context_destroyed(context);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_active_sessions(&self) -> bool {
|
pub fn has_active_sessions(&self) -> bool {
|
||||||
self.sessions.borrow().has_active_sessions()
|
self.sessions.borrow().has_active_sessions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_blocking_sessions(&self) -> bool {
|
||||||
|
self.sessions.borrow().has_blocking_sessions()
|
||||||
|
}
|
||||||
|
|
||||||
fn poll_sessions(
|
fn poll_sessions(
|
||||||
&self,
|
&self,
|
||||||
mut invoker_cx: Option<&mut Context>,
|
mut invoker_cx: Option<&mut Context>,
|
||||||
|
@ -262,8 +281,11 @@ impl JsRuntimeInspector {
|
||||||
// Accept new connections.
|
// Accept new connections.
|
||||||
let poll_result = sessions.session_rx.poll_next_unpin(cx);
|
let poll_result = sessions.session_rx.poll_next_unpin(cx);
|
||||||
if let Poll::Ready(Some(session_proxy)) = poll_result {
|
if let Poll::Ready(Some(session_proxy)) = poll_result {
|
||||||
let session =
|
let session = InspectorSession::new(
|
||||||
InspectorSession::new(sessions.v8_inspector.clone(), session_proxy);
|
sessions.v8_inspector.clone(),
|
||||||
|
session_proxy,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let prev = sessions.handshake.replace(session);
|
let prev = sessions.handshake.replace(session);
|
||||||
assert!(prev.is_none());
|
assert!(prev.is_none());
|
||||||
}
|
}
|
||||||
|
@ -378,7 +400,7 @@ impl JsRuntimeInspector {
|
||||||
// InspectorSessions for a local session is added directly to the "established"
|
// InspectorSessions for a local session is added directly to the "established"
|
||||||
// sessions, so it doesn't need to go through the session sender.
|
// sessions, so it doesn't need to go through the session sender.
|
||||||
let inspector_session =
|
let inspector_session =
|
||||||
InspectorSession::new(self.v8_inspector.clone(), proxy);
|
InspectorSession::new(self.v8_inspector.clone(), proxy, true);
|
||||||
self
|
self
|
||||||
.sessions
|
.sessions
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -432,6 +454,10 @@ impl SessionContainer {
|
||||||
!self.established.is_empty() || self.handshake.is_some()
|
!self.established.is_empty() || self.handshake.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_blocking_sessions(&self) -> bool {
|
||||||
|
self.established.iter().any(|s| s.blocking)
|
||||||
|
}
|
||||||
|
|
||||||
/// A temporary placeholder that should be used before actual
|
/// A temporary placeholder that should be used before actual
|
||||||
/// instance of V8Inspector is created. It's used in favor
|
/// instance of V8Inspector is created. It's used in favor
|
||||||
/// of `Default` implementation to signal that it's not meant
|
/// of `Default` implementation to signal that it's not meant
|
||||||
|
@ -528,6 +554,9 @@ struct InspectorSession {
|
||||||
v8_channel: v8::inspector::ChannelBase,
|
v8_channel: v8::inspector::ChannelBase,
|
||||||
v8_session: v8::UniqueRef<v8::inspector::V8InspectorSession>,
|
v8_session: v8::UniqueRef<v8::inspector::V8InspectorSession>,
|
||||||
proxy: InspectorSessionProxy,
|
proxy: InspectorSessionProxy,
|
||||||
|
// Describes if session should keep event loop alive, eg. a local REPL
|
||||||
|
// session should keep event loop alive, but a Websocket session shouldn't.
|
||||||
|
blocking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InspectorSession {
|
impl InspectorSession {
|
||||||
|
@ -536,6 +565,7 @@ impl InspectorSession {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
v8_inspector_rc: Rc<RefCell<v8::UniquePtr<v8::inspector::V8Inspector>>>,
|
v8_inspector_rc: Rc<RefCell<v8::UniquePtr<v8::inspector::V8Inspector>>>,
|
||||||
session_proxy: InspectorSessionProxy,
|
session_proxy: InspectorSessionProxy,
|
||||||
|
blocking: bool,
|
||||||
) -> Box<Self> {
|
) -> Box<Self> {
|
||||||
new_box_with(move |self_ptr| {
|
new_box_with(move |self_ptr| {
|
||||||
let v8_channel = v8::inspector::ChannelBase::new::<Self>();
|
let v8_channel = v8::inspector::ChannelBase::new::<Self>();
|
||||||
|
@ -556,6 +586,7 @@ impl InspectorSession {
|
||||||
v8_channel,
|
v8_channel,
|
||||||
v8_session,
|
v8_session,
|
||||||
proxy: session_proxy,
|
proxy: session_proxy,
|
||||||
|
blocking,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1099,9 +1099,21 @@ impl JsRuntime {
|
||||||
let pending_state = self.event_loop_pending_state();
|
let pending_state = self.event_loop_pending_state();
|
||||||
if !pending_state.is_pending() && !maybe_scheduling {
|
if !pending_state.is_pending() && !maybe_scheduling {
|
||||||
if has_inspector {
|
if has_inspector {
|
||||||
let inspector_has_active_sessions =
|
let inspector = self.inspector();
|
||||||
self.inspector().borrow_mut().has_active_sessions();
|
let has_active_sessions = inspector.borrow().has_active_sessions();
|
||||||
if wait_for_inspector && inspector_has_active_sessions {
|
let has_blocking_sessions = inspector.borrow().has_blocking_sessions();
|
||||||
|
|
||||||
|
if wait_for_inspector && has_active_sessions {
|
||||||
|
// If there are no blocking sessions (eg. REPL) we can now notify
|
||||||
|
// debugger that the program has finished running and we're ready
|
||||||
|
// to exit the process once debugger disconnects.
|
||||||
|
if !has_blocking_sessions {
|
||||||
|
let context = self.global_context();
|
||||||
|
let scope = &mut self.handle_scope();
|
||||||
|
inspector.borrow_mut().context_destroyed(scope, context);
|
||||||
|
println!("Program finished. Waiting for inspector to disconnect to exit the process...");
|
||||||
|
}
|
||||||
|
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue