mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(core): inspector works if no "Runtime.runIfWaitingForDebugger" message is sent (#13191)
This commit changes flow in inspector code to no longer require "Runtime.runIfWaitingForDebugger" message to complete a handshake. Even though clients like Chrome DevTools always send this message on startup, it is against the protocol to require this message to start an inspector session. Instead "Runtime.runIfWaitingForDebugger" is required only when running with "--inspect-brk" flag, which matches behavior of Node.js.
This commit is contained in:
parent
de9778949b
commit
1adf8ee545
2 changed files with 328 additions and 202 deletions
|
@ -2,12 +2,16 @@
|
|||
|
||||
use deno_core::futures;
|
||||
use deno_core::futures::prelude::*;
|
||||
use deno_core::futures::stream::SplitSink;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url;
|
||||
use deno_runtime::deno_fetch::reqwest;
|
||||
use deno_runtime::deno_websocket::tokio_tungstenite;
|
||||
use deno_runtime::deno_websocket::tokio_tungstenite::tungstenite;
|
||||
use std::io::BufRead;
|
||||
use std::pin::Pin;
|
||||
use test_util as util;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
fn inspect_flag_with_unique_port(flag_prefix: &str) -> String {
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
|
@ -28,6 +32,71 @@ fn extract_ws_url_from_stderr(
|
|||
url::Url::parse(ws_url).unwrap()
|
||||
}
|
||||
|
||||
fn assert_stderr_for_inspect(
|
||||
stderr_lines: &mut impl std::iter::Iterator<Item = String>,
|
||||
) {
|
||||
assert_eq!(
|
||||
&stderr_lines.next().unwrap(),
|
||||
"Visit chrome://inspect to connect to the debugger."
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_stderr_for_inspect_brk(
|
||||
stderr_lines: &mut impl std::iter::Iterator<Item = String>,
|
||||
) {
|
||||
assert_eq!(
|
||||
&stderr_lines.next().unwrap(),
|
||||
"Visit chrome://inspect to connect to the debugger."
|
||||
);
|
||||
assert_eq!(
|
||||
&stderr_lines.next().unwrap(),
|
||||
"Deno is waiting for debugger to connect."
|
||||
);
|
||||
}
|
||||
|
||||
async fn assert_inspector_messages(
|
||||
socket_tx: &mut SplitSink<
|
||||
tokio_tungstenite::WebSocketStream<
|
||||
tokio_tungstenite::MaybeTlsStream<TcpStream>,
|
||||
>,
|
||||
tungstenite::Message,
|
||||
>,
|
||||
messages: &[&str],
|
||||
socket_rx: &mut Pin<Box<dyn Stream<Item = String>>>,
|
||||
responses: &[&str],
|
||||
notifications: &[&str],
|
||||
) {
|
||||
for msg in messages {
|
||||
socket_tx.send(msg.to_string().into()).await.unwrap();
|
||||
}
|
||||
|
||||
let expected_messages = responses.len() + notifications.len();
|
||||
let mut responses_idx = 0;
|
||||
let mut notifications_idx = 0;
|
||||
|
||||
for _ in 0..expected_messages {
|
||||
let msg = socket_rx.next().await.unwrap();
|
||||
|
||||
if msg.starts_with(r#"{"id":"#) {
|
||||
assert!(
|
||||
msg.starts_with(responses[responses_idx]),
|
||||
"Doesn't start with {}, instead received {}",
|
||||
responses[responses_idx],
|
||||
msg
|
||||
);
|
||||
responses_idx += 1;
|
||||
} else {
|
||||
assert!(
|
||||
msg.starts_with(notifications[notifications_idx]),
|
||||
"Doesn't start with {}, instead received {}",
|
||||
notifications[notifications_idx],
|
||||
msg
|
||||
);
|
||||
notifications_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn inspector_connect() {
|
||||
let script = util::testdata_path().join("inspector1.js");
|
||||
|
@ -54,14 +123,6 @@ async fn inspector_connect() {
|
|||
child.wait().unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TestStep {
|
||||
StdOut(&'static str),
|
||||
StdErr(&'static str),
|
||||
WsRecv(&'static str),
|
||||
WsSend(&'static str),
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn inspector_break_on_first_line() {
|
||||
let script = util::testdata_path().join("inspector2.js");
|
||||
|
@ -85,48 +146,69 @@ async fn inspector_break_on_first_line() {
|
|||
assert_eq!(response.status(), 101); // Switching protocols.
|
||||
|
||||
let (mut socket_tx, socket_rx) = socket.split();
|
||||
let mut socket_rx =
|
||||
socket_rx.map(|msg| msg.unwrap().to_string()).filter(|msg| {
|
||||
let mut socket_rx = socket_rx
|
||||
.map(|msg| msg.unwrap().to_string())
|
||||
.filter(|msg| {
|
||||
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
||||
futures::future::ready(pass)
|
||||
});
|
||||
})
|
||||
.boxed_local();
|
||||
|
||||
let stdout = child.stdout.as_mut().unwrap();
|
||||
let mut stdout_lines =
|
||||
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
||||
|
||||
use TestStep::*;
|
||||
let test_steps = vec![
|
||||
StdErr("Visit chrome://inspect to connect to the debugger."),
|
||||
StdErr("Deno is waiting for debugger to connect."),
|
||||
WsSend(r#"{"id":1,"method":"Runtime.enable"}"#),
|
||||
WsSend(r#"{"id":2,"method":"Debugger.enable"}"#),
|
||||
WsRecv(
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
),
|
||||
WsRecv(r#"{"id":1,"result":{}}"#),
|
||||
WsRecv(r#"{"id":2,"result":{"debuggerId":"#),
|
||||
WsSend(r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#),
|
||||
WsRecv(r#"{"id":3,"result":{}}"#),
|
||||
WsRecv(r#"{"method":"Debugger.paused","#),
|
||||
WsSend(
|
||||
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
||||
),
|
||||
WsRecv(r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#),
|
||||
StdOut("hello from the inspector"),
|
||||
WsSend(r#"{"id":5,"method":"Debugger.resume"}"#),
|
||||
WsRecv(r#"{"id":5,"result":{}}"#),
|
||||
StdOut("hello from the script"),
|
||||
];
|
||||
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
||||
|
||||
for step in test_steps {
|
||||
match step {
|
||||
StdErr(s) => assert_eq!(&stderr_lines.next().unwrap(), s),
|
||||
StdOut(s) => assert_eq!(&stdout_lines.next().unwrap(), s),
|
||||
WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)),
|
||||
WsSend(s) => socket_tx.send(s.into()).await.unwrap(),
|
||||
}
|
||||
}
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":1,"method":"Runtime.enable"}"#,
|
||||
r#"{"id":2,"method":"Debugger.enable"}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[
|
||||
r#"{"id":1,"result":{}}"#,
|
||||
r#"{"id":2,"result":{"debuggerId":"#,
|
||||
],
|
||||
&[
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":3,"result":{}}"#],
|
||||
&[r#"{"method":"Debugger.paused","#],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
|
||||
&[],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(&stdout_lines.next().unwrap(), "hello from the inspector");
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":5,"method":"Debugger.resume"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":5,"result":{}}"#],
|
||||
&[],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(&stdout_lines.next().unwrap(), "hello from the script");
|
||||
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
|
@ -266,63 +348,84 @@ async fn inspector_does_not_hang() {
|
|||
assert_eq!(response.status(), 101); // Switching protocols.
|
||||
|
||||
let (mut socket_tx, socket_rx) = socket.split();
|
||||
let mut socket_rx =
|
||||
socket_rx.map(|msg| msg.unwrap().to_string()).filter(|msg| {
|
||||
let mut socket_rx = socket_rx
|
||||
.map(|msg| msg.unwrap().to_string())
|
||||
.filter(|msg| {
|
||||
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
||||
futures::future::ready(pass)
|
||||
});
|
||||
})
|
||||
.boxed_local();
|
||||
|
||||
let stdout = child.stdout.as_mut().unwrap();
|
||||
let mut stdout_lines =
|
||||
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
||||
|
||||
use TestStep::*;
|
||||
let test_steps = vec![
|
||||
StdErr("Visit chrome://inspect to connect to the debugger."),
|
||||
StdErr("Deno is waiting for debugger to connect."),
|
||||
WsSend(r#"{"id":1,"method":"Runtime.enable"}"#),
|
||||
WsSend(r#"{"id":2,"method":"Debugger.enable"}"#),
|
||||
WsRecv(
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
),
|
||||
WsRecv(r#"{"id":1,"result":{}}"#),
|
||||
WsRecv(r#"{"id":2,"result":{"debuggerId":"#),
|
||||
WsSend(r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#),
|
||||
WsRecv(r#"{"id":3,"result":{}}"#),
|
||||
WsRecv(r#"{"method":"Debugger.paused","#),
|
||||
WsSend(r#"{"id":4,"method":"Debugger.resume"}"#),
|
||||
WsRecv(r#"{"id":4,"result":{}}"#),
|
||||
WsRecv(r#"{"method":"Debugger.resumed","params":{}}"#),
|
||||
];
|
||||
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
||||
|
||||
for step in test_steps {
|
||||
match step {
|
||||
StdErr(s) => assert_eq!(&stderr_lines.next().unwrap(), s),
|
||||
WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)),
|
||||
WsSend(s) => socket_tx.send(s.into()).await.unwrap(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":1,"method":"Runtime.enable"}"#,
|
||||
r#"{"id":2,"method":"Debugger.enable"}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[
|
||||
r#"{"id":1,"result":{}}"#,
|
||||
r#"{"id":2,"result":{"debuggerId":"#
|
||||
],
|
||||
&[
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":3,"result":{}}"#],
|
||||
&[r#"{"method":"Debugger.paused","#],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":4,"method":"Debugger.resume"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":4,"result":{}}"#],
|
||||
&[r#"{"method":"Debugger.resumed","params":{}}"#],
|
||||
)
|
||||
.await;
|
||||
|
||||
for i in 0..128u32 {
|
||||
let request_id = i + 10;
|
||||
// Expect the number {i} on stdout.
|
||||
let s = i.to_string();
|
||||
assert_eq!(stdout_lines.next().unwrap(), s);
|
||||
// Expect console.log
|
||||
let s = r#"{"method":"Runtime.consoleAPICalled","#;
|
||||
assert!(socket_rx.next().await.unwrap().starts_with(s));
|
||||
// Expect hitting the `debugger` statement.
|
||||
let s = r#"{"method":"Debugger.paused","#;
|
||||
assert!(socket_rx.next().await.unwrap().starts_with(s));
|
||||
// Send the 'Debugger.resume' request.
|
||||
let s = format!(r#"{{"id":{},"method":"Debugger.resume"}}"#, request_id);
|
||||
socket_tx.send(s.into()).await.unwrap();
|
||||
// Expect confirmation of the 'Debugger.resume' request.
|
||||
let s = format!(r#"{{"id":{},"result":{{}}}}"#, request_id);
|
||||
assert_eq!(socket_rx.next().await.unwrap(), s);
|
||||
let s = r#"{"method":"Debugger.resumed","params":{}}"#;
|
||||
assert_eq!(socket_rx.next().await.unwrap(), s);
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[],
|
||||
&mut socket_rx,
|
||||
&[],
|
||||
&[
|
||||
r#"{"method":"Runtime.consoleAPICalled","#,
|
||||
r#"{"method":"Debugger.paused","#,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[&format!(
|
||||
r#"{{"id":{},"method":"Debugger.resume"}}"#,
|
||||
request_id
|
||||
)],
|
||||
&mut socket_rx,
|
||||
&[&format!(r#"{{"id":{},"result":{{}}}}"#, request_id)],
|
||||
&[r#"{"method":"Debugger.resumed","params":{}}"#],
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Check that we can gracefully close the websocket connection.
|
||||
|
@ -386,11 +489,13 @@ async fn inspector_runtime_evaluate_does_not_crash() {
|
|||
assert_eq!(response.status(), 101); // Switching protocols.
|
||||
|
||||
let (mut socket_tx, socket_rx) = socket.split();
|
||||
let mut socket_rx =
|
||||
socket_rx.map(|msg| msg.unwrap().to_string()).filter(|msg| {
|
||||
let mut socket_rx = socket_rx
|
||||
.map(|msg| msg.unwrap().to_string())
|
||||
.filter(|msg| {
|
||||
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
||||
futures::future::ready(pass)
|
||||
});
|
||||
})
|
||||
.boxed_local();
|
||||
|
||||
let stdin = child.stdin.take().unwrap();
|
||||
|
||||
|
@ -400,43 +505,60 @@ async fn inspector_runtime_evaluate_does_not_crash() {
|
|||
.map(|r| r.unwrap())
|
||||
.filter(|s| !s.starts_with("Deno "));
|
||||
|
||||
use TestStep::*;
|
||||
let test_steps = vec![
|
||||
StdErr("Visit chrome://inspect to connect to the debugger."),
|
||||
WsSend(r#"{"id":1,"method":"Runtime.enable"}"#),
|
||||
WsSend(r#"{"id":2,"method":"Debugger.enable"}"#),
|
||||
WsRecv(
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
),
|
||||
WsRecv(r#"{"id":1,"result":{}}"#),
|
||||
WsRecv(r#"{"id":2,"result":{"debuggerId":"#),
|
||||
WsSend(r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#),
|
||||
WsRecv(r#"{"id":3,"result":{}}"#),
|
||||
StdOut("exit using ctrl+d or close()"),
|
||||
WsSend(
|
||||
r#"{"id":4,"method":"Runtime.compileScript","params":{"expression":"Deno.cwd()","sourceURL":"","persistScript":false,"executionContextId":1}}"#,
|
||||
),
|
||||
WsRecv(r#"{"id":4,"result":{}}"#),
|
||||
WsSend(
|
||||
r#"{"id":5,"method":"Runtime.evaluate","params":{"expression":"Deno.cwd()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
||||
),
|
||||
WsRecv(r#"{"id":5,"result":{"result":{"type":"string","value":""#),
|
||||
WsSend(
|
||||
r#"{"id":6,"method":"Runtime.evaluate","params":{"expression":"console.error('done');","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
||||
),
|
||||
WsRecv(r#"{"method":"Runtime.consoleAPICalled"#),
|
||||
WsRecv(r#"{"id":6,"result":{"result":{"type":"undefined"}}}"#),
|
||||
StdErr("done"),
|
||||
];
|
||||
assert_stderr_for_inspect(&mut stderr_lines);
|
||||
|
||||
for step in test_steps {
|
||||
match step {
|
||||
StdOut(s) => assert_eq!(&stdout_lines.next().unwrap(), s),
|
||||
StdErr(s) => assert_eq!(&stderr_lines.next().unwrap(), s),
|
||||
WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)),
|
||||
WsSend(s) => socket_tx.send(s.into()).await.unwrap(),
|
||||
}
|
||||
}
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":1,"method":"Runtime.enable"}"#,
|
||||
r#"{"id":2,"method":"Debugger.enable"}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[
|
||||
r#"{"id":1,"result":{}}"#,
|
||||
r#"{"id":2,"result":{"debuggerId":"#,
|
||||
],
|
||||
&[
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
&stdout_lines.next().unwrap(),
|
||||
"exit using ctrl+d or close()"
|
||||
);
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":3,"method":"Runtime.compileScript","params":{"expression":"Deno.cwd()","sourceURL":"","persistScript":false,"executionContextId":1}}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":3,"result":{}}"#], &[]
|
||||
).await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.cwd()","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":4,"result":{"result":{"type":"string","value":""#],
|
||||
&[],
|
||||
).await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":5,"method":"Runtime.evaluate","params":{"expression":"console.error('done');","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":true,"generatePreview":true,"userGesture":true,"awaitPromise":false,"replMode":true}}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":5,"result":{"result":{"type":"undefined"}}}"#],
|
||||
&[r#"{"method":"Runtime.consoleAPICalled"#],
|
||||
).await;
|
||||
|
||||
assert_eq!(&stderr_lines.next().unwrap(), "done");
|
||||
|
||||
drop(stdin);
|
||||
child.wait().unwrap();
|
||||
|
@ -549,53 +671,76 @@ async fn inspector_break_on_first_line_in_test() {
|
|||
assert_eq!(response.status(), 101); // Switching protocols.
|
||||
|
||||
let (mut socket_tx, socket_rx) = socket.split();
|
||||
let mut socket_rx =
|
||||
socket_rx.map(|msg| msg.unwrap().to_string()).filter(|msg| {
|
||||
let mut socket_rx = socket_rx
|
||||
.map(|msg| msg.unwrap().to_string())
|
||||
.filter(|msg| {
|
||||
let pass = !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#);
|
||||
futures::future::ready(pass)
|
||||
});
|
||||
})
|
||||
.boxed_local();
|
||||
|
||||
let stdout = child.stdout.as_mut().unwrap();
|
||||
let mut stdout_lines =
|
||||
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
|
||||
|
||||
use TestStep::*;
|
||||
let test_steps = vec![
|
||||
StdErr("Visit chrome://inspect to connect to the debugger."),
|
||||
StdErr("Deno is waiting for debugger to connect."),
|
||||
WsSend(r#"{"id":1,"method":"Runtime.enable"}"#),
|
||||
WsSend(r#"{"id":2,"method":"Debugger.enable"}"#),
|
||||
WsRecv(
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
),
|
||||
WsRecv(r#"{"id":1,"result":{}}"#),
|
||||
WsRecv(r#"{"id":2,"result":{"debuggerId":"#),
|
||||
WsSend(r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#),
|
||||
WsRecv(r#"{"id":3,"result":{}}"#),
|
||||
WsRecv(r#"{"method":"Debugger.paused","#),
|
||||
WsSend(
|
||||
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
||||
),
|
||||
WsRecv(r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#),
|
||||
StdOut("hello from the inspector"),
|
||||
WsSend(r#"{"id":5,"method":"Debugger.resume"}"#),
|
||||
WsRecv(r#"{"id":5,"result":{}}"#),
|
||||
StdOut("running 1 test from"),
|
||||
StdOut("test has finished running"),
|
||||
];
|
||||
assert_stderr_for_inspect_brk(&mut stderr_lines);
|
||||
|
||||
for step in test_steps {
|
||||
match step {
|
||||
StdOut(s) => assert!(
|
||||
&stdout_lines.next().unwrap().contains(s),
|
||||
"Doesn't contain {}",
|
||||
s
|
||||
),
|
||||
StdErr(s) => assert_eq!(&stderr_lines.next().unwrap(), s),
|
||||
WsRecv(s) => assert!(socket_rx.next().await.unwrap().starts_with(s)),
|
||||
WsSend(s) => socket_tx.send(s.into()).await.unwrap(),
|
||||
}
|
||||
}
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":1,"method":"Runtime.enable"}"#,
|
||||
r#"{"id":2,"method":"Debugger.enable"}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[
|
||||
r#"{"id":1,"result":{}}"#,
|
||||
r#"{"id":2,"result":{"debuggerId":"#,
|
||||
],
|
||||
&[
|
||||
r#"{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"#,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":3,"method":"Runtime.runIfWaitingForDebugger"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":3,"result":{}}"#],
|
||||
&[r#"{"method":"Debugger.paused","#],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[
|
||||
r#"{"id":4,"method":"Runtime.evaluate","params":{"expression":"Deno.core.print(\"hello from the inspector\\n\")","contextId":1,"includeCommandLineAPI":true,"silent":false,"returnByValue":true}}"#,
|
||||
],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":4,"result":{"result":{"type":"undefined"}}}"#],
|
||||
&[],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(&stdout_lines.next().unwrap(), "hello from the inspector");
|
||||
|
||||
assert_inspector_messages(
|
||||
&mut socket_tx,
|
||||
&[r#"{"id":5,"method":"Debugger.resume"}"#],
|
||||
&mut socket_rx,
|
||||
&[r#"{"id":5,"result":{}}"#],
|
||||
&[],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(&stdout_lines
|
||||
.next()
|
||||
.unwrap()
|
||||
.starts_with("running 1 test from"));
|
||||
assert!(&stdout_lines
|
||||
.next()
|
||||
.unwrap()
|
||||
.contains("test has finished running"));
|
||||
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
|
|
|
@ -27,7 +27,6 @@ use std::cell::BorrowMutError;
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::replace;
|
||||
use std::mem::take;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::pin::Pin;
|
||||
|
@ -127,7 +126,7 @@ impl v8::inspector::V8InspectorClientImpl for JsRuntimeInspector {
|
|||
|
||||
fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {
|
||||
assert_eq!(context_group_id, JsRuntimeInspector::CONTEXT_GROUP_ID);
|
||||
self.flags.borrow_mut().session_handshake_done = true;
|
||||
self.flags.borrow_mut().waiting_for_session = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,34 +225,20 @@ impl JsRuntimeInspector {
|
|||
loop {
|
||||
loop {
|
||||
// Do one "handshake" with a newly connected session at a time.
|
||||
if let Some(session) = &mut sessions.handshake {
|
||||
let poll_result = session.poll_unpin(cx);
|
||||
let handshake_done =
|
||||
replace(&mut self.flags.borrow_mut().session_handshake_done, false);
|
||||
match poll_result {
|
||||
Poll::Pending if handshake_done => {
|
||||
let session = sessions.handshake.take().unwrap();
|
||||
sessions.established.push(session);
|
||||
take(&mut self.flags.borrow_mut().waiting_for_session);
|
||||
}
|
||||
Poll::Ready(_) => sessions.handshake = None,
|
||||
Poll::Pending => break,
|
||||
};
|
||||
if let Some(mut session) = sessions.handshake.take() {
|
||||
if session.poll_unpin(cx).is_pending() {
|
||||
sessions.established.push(session);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Accept new connections.
|
||||
match sessions.session_rx.poll_next_unpin(cx) {
|
||||
Poll::Ready(Some(session_proxy)) => {
|
||||
let session = InspectorSession::new(
|
||||
sessions.v8_inspector.clone(),
|
||||
session_proxy,
|
||||
);
|
||||
let prev = sessions.handshake.replace(session);
|
||||
assert!(prev.is_none());
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(None) => {}
|
||||
Poll::Pending => {}
|
||||
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 prev = sessions.handshake.replace(session);
|
||||
assert!(prev.is_none());
|
||||
}
|
||||
|
||||
// Poll established sessions.
|
||||
|
@ -264,9 +249,8 @@ impl JsRuntimeInspector {
|
|||
};
|
||||
}
|
||||
|
||||
let should_block = sessions.handshake.is_some()
|
||||
|| self.flags.borrow().on_pause
|
||||
|| self.flags.borrow().waiting_for_session;
|
||||
let should_block =
|
||||
self.flags.borrow().on_pause || self.flags.borrow().waiting_for_session;
|
||||
|
||||
let new_state = self.waker.update(|w| {
|
||||
match w.poll_state {
|
||||
|
@ -311,8 +295,11 @@ impl JsRuntimeInspector {
|
|||
}
|
||||
|
||||
/// This function blocks the thread until at least one inspector client has
|
||||
/// established a websocket connection and successfully completed the
|
||||
/// handshake. After that, it instructs V8 to pause at the next statement.
|
||||
/// established a websocket connection.
|
||||
///
|
||||
/// After that, it instructs V8 to pause at the next statement.
|
||||
/// Frontend must send "Runtime.runIfWaitingForDebugger" message to resume
|
||||
/// execution.
|
||||
pub fn wait_for_session_and_break_on_next_statement(&mut self) {
|
||||
loop {
|
||||
match self.sessions.get_mut().established.iter_mut().next() {
|
||||
|
@ -326,10 +313,6 @@ impl JsRuntimeInspector {
|
|||
}
|
||||
|
||||
/// Obtain a sender for proxy channels.
|
||||
///
|
||||
/// After a proxy is sent inspector will wait for a "handshake".
|
||||
/// Frontend must send "Runtime.runIfWaitingForDebugger" message to
|
||||
/// complete the handshake.
|
||||
pub fn get_session_sender(&self) -> UnboundedSender<InspectorSessionProxy> {
|
||||
self.new_session_tx.clone()
|
||||
}
|
||||
|
@ -362,8 +345,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 and handshake
|
||||
// phase.
|
||||
// sessions, so it doesn't need to go through the session sender.
|
||||
let inspector_session =
|
||||
InspectorSession::new(self.v8_inspector.clone(), proxy);
|
||||
self
|
||||
|
@ -380,7 +362,6 @@ impl JsRuntimeInspector {
|
|||
#[derive(Default)]
|
||||
struct InspectorFlags {
|
||||
waiting_for_session: bool,
|
||||
session_handshake_done: bool,
|
||||
on_pause: bool,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue