Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): send "executionContextDestroyed" notification on program end #16831

Merged
merged 12 commits into from
Nov 26, 2022
35 changes: 24 additions & 11 deletions cli/tests/inspector_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ mod inspector {
.unwrap();

let msg = ws_read_msg(&mut socket).await;
println!("response msg 1 {}", msg);
assert_starts_with!(msg, r#"{"id":6,"result":{"debuggerId":"#);

socket
Expand All @@ -312,7 +311,6 @@ mod inspector {
.unwrap();

let msg = ws_read_msg(&mut socket).await;
println!("response msg 2 {}", msg);
assert_eq!(msg, r#"{"id":31,"result":{}}"#);

child.kill().unwrap();
Expand Down Expand Up @@ -546,6 +544,10 @@ mod inspector {
.filter(|s| !s.starts_with("Deno "));

assert_stderr_for_inspect(&mut stderr_lines);
assert_eq!(
&stdout_lines.next().unwrap(),
"exit using ctrl+d, ctrl+c, or close()"
);

assert_inspector_messages(
&mut socket_tx,
Expand All @@ -564,11 +566,6 @@ mod inspector {
)
.await;

assert_eq!(
&stdout_lines.next().unwrap(),
"exit using ctrl+d, ctrl+c, or close()"
);

assert_inspector_messages(
&mut socket_tx,
&[
Expand All @@ -577,7 +574,6 @@ mod inspector {
&mut socket_rx,
&[r#"{"id":3,"result":{}}"#], &[]
).await;

assert_inspector_messages(
&mut socket_tx,
&[
Expand All @@ -587,7 +583,6 @@ mod inspector {
&[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
&[],
).await;

assert_inspector_messages(
&mut socket_tx,
&[
Expand All @@ -597,9 +592,7 @@ mod inspector {
&[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
&[r#"{"method":"Runtime.consoleAPICalled"#],
).await;

assert_eq!(&stderr_lines.next().unwrap(), "done");

drop(stdin);
child.wait().unwrap();
}
Expand Down Expand Up @@ -905,6 +898,26 @@ mod inspector {
assert_eq!(&stdout_lines.next().unwrap(), "hello");
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.wait().unwrap();
}
Expand Down
37 changes: 34 additions & 3 deletions core/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::Arc;
use std::thread;
use v8::HandleScope;

pub enum InspectorMsgKind {
Notification,
Expand Down Expand Up @@ -213,10 +214,28 @@ impl JsRuntimeInspector {
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 {
self.sessions.borrow().has_active_sessions()
}

pub fn has_blocking_sessions(&self) -> bool {
self.sessions.borrow().has_blocking_sessions()
}

fn poll_sessions(
&self,
mut invoker_cx: Option<&mut Context>,
Expand Down Expand Up @@ -262,8 +281,11 @@ impl JsRuntimeInspector {
// Accept new connections.
let poll_result = sessions.session_rx.poll_next_unpin(cx);
if let Poll::Ready(Some(session_proxy)) = poll_result {
let session =
InspectorSession::new(sessions.v8_inspector.clone(), session_proxy);
let session = InspectorSession::new(
sessions.v8_inspector.clone(),
session_proxy,
false,
);
let prev = sessions.handshake.replace(session);
assert!(prev.is_none());
}
Expand Down Expand Up @@ -378,7 +400,7 @@ impl JsRuntimeInspector {
// InspectorSessions for a local session is added directly to the "established"
// sessions, so it doesn't need to go through the session sender.
let inspector_session =
InspectorSession::new(self.v8_inspector.clone(), proxy);
InspectorSession::new(self.v8_inspector.clone(), proxy, true);
self
.sessions
.borrow_mut()
Expand Down Expand Up @@ -432,6 +454,10 @@ impl SessionContainer {
!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
/// instance of V8Inspector is created. It's used in favor
/// of `Default` implementation to signal that it's not meant
Expand Down Expand Up @@ -528,6 +554,9 @@ struct InspectorSession {
v8_channel: v8::inspector::ChannelBase,
v8_session: v8::UniqueRef<v8::inspector::V8InspectorSession>,
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 {
Expand All @@ -536,6 +565,7 @@ impl InspectorSession {
pub fn new(
v8_inspector_rc: Rc<RefCell<v8::UniquePtr<v8::inspector::V8Inspector>>>,
session_proxy: InspectorSessionProxy,
blocking: bool,
) -> Box<Self> {
new_box_with(move |self_ptr| {
let v8_channel = v8::inspector::ChannelBase::new::<Self>();
Expand All @@ -556,6 +586,7 @@ impl InspectorSession {
v8_channel,
v8_session,
proxy: session_proxy,
blocking,
}
})
}
Expand Down
18 changes: 15 additions & 3 deletions core/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1099,9 +1099,21 @@ impl JsRuntime {
let pending_state = self.event_loop_pending_state();
if !pending_state.is_pending() && !maybe_scheduling {
if has_inspector {
let inspector_has_active_sessions =
self.inspector().borrow_mut().has_active_sessions();
if wait_for_inspector && inspector_has_active_sessions {
let inspector = self.inspector();
let has_active_sessions = inspector.borrow().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;
}
}
Expand Down