From 0c6aca0c11eb69056e9009c1234c02eda876bcc2 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Wed, 4 Nov 2020 22:54:03 +0000 Subject: [PATCH 01/30] initial auxtools debug client --- src/langserver/debugger/auxtools.rs | 207 ++++++++++++++++++++++ src/langserver/debugger/auxtools_types.rs | 48 +++++ src/langserver/debugger/mod.rs | 2 + 3 files changed, 257 insertions(+) create mode 100644 src/langserver/debugger/auxtools.rs create mode 100644 src/langserver/debugger/auxtools_types.rs diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs new file mode 100644 index 000000000..4f16078ee --- /dev/null +++ b/src/langserver/debugger/auxtools.rs @@ -0,0 +1,207 @@ +use super::auxtools_types::*; +use std::{sync::Arc, io::{Error, Read, Write}, net::SocketAddr, net::TcpStream, thread::JoinHandle}; +use std::sync::mpsc; +use std::thread; + +use super::SequenceNumber; +use super::dap_types; + +pub struct Auxtools { + requests: mpsc::Sender, + responses: mpsc::Receiver, + _thread: JoinHandle<()>, +} + +pub struct AuxtoolsThread { + seq: Arc, + requests: mpsc::Receiver, + responses: mpsc::Sender, + stream: TcpStream, +} + +impl Auxtools { + pub fn new(addr: &SocketAddr, seq: Arc) -> std::io::Result { + let (requests_sender, requests_receiver) = mpsc::channel(); + let (responses_sender, responses_receiver) = mpsc::channel(); + + let thread = AuxtoolsThread { + seq, + requests: requests_receiver, + responses: responses_sender, + stream: TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))? + }; + + Ok(Auxtools { + requests: requests_sender, + responses: responses_receiver, + _thread: thread.start_thread(), + }) + } + + fn read_response(&mut self) -> Result { + self.responses.recv_timeout(std::time::Duration::from_secs(5)) + } + + pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { + self.requests.send(Request::LineNumber { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + offset, + }).unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::LineNumber { line } => { + line + } + + // TODO: disconnect + _ => panic!("received wrong response") + } + } + + // TODO: disconnect + _ => panic!("timed out") + } + } + + pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Option { + self.requests.send(Request::Offset { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + line, + }).unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::Offset { offset } => { + offset + } + + // TODO: disconnect + _ => panic!("received wrong response") + } + } + + // TODO: disconnect + _ => panic!("timed out") + } + } + + pub fn set_breakpoint(&mut self, path: &str, override_id: u32, offset: u32) { + self.requests.send(Request::BreakpointSet { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + offset, + }).unwrap(); + + // We ignore the result + // TODO: disconnect + self.read_response().unwrap(); + } + + pub fn unset_breakpoint(&mut self, path: &str, override_id: u32, offset: u32) { + self.requests.send(Request::BreakpointUnset { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + offset, + }).unwrap(); + + // We ignore the result + // TODO: disconnect + self.read_response().unwrap(); + } + + pub fn continue_execution(&mut self) { + // TODO: disconnect + self.requests.send(Request::Continue { + kind: ContinueKind::Continue + }).unwrap(); + } + + pub fn step_over(&mut self) { + // TODO: disconnect + self.requests.send(Request::Continue { + kind: ContinueKind::StepOver + }).unwrap(); + } +} + +impl AuxtoolsThread { + fn send(&mut self, request: Request) { + let mut message = serde_json::to_vec(&request).unwrap(); + message.push(0); // null-terminator + self.stream.write_all(&message[..]).unwrap(); + } + + fn handle_message(&mut self, data: &[u8]) -> Result<(), Box> { + let response = serde_json::from_slice::(data)?; + + match response { + Response::BreakpointHit { reason } => { + let reason = match reason { + BreakpointReason::Step => dap_types::StoppedEvent::REASON_STEP, + BreakpointReason::Pause => dap_types::StoppedEvent::REASON_PAUSE, + BreakpointReason::Breakpoint => dap_types::StoppedEvent::REASON_BREAKPOINT, + }; + + self.seq.issue_event(dap_types::StoppedEvent { + threadId: Some(0), + reason: reason.to_owned(), + .. Default::default() + }); + }, + x => { + self.responses.send(x)?; + } + } + + Ok(()) + } + + pub fn start_thread(mut self) -> JoinHandle<()> { + thread::spawn(move || { + let mut buf = [0u8; 4096]; + let mut queued_data = vec![]; + + loop { + match self.stream.read(&mut buf) { + Ok(0) => (), + Ok(n) => { + queued_data.extend_from_slice(&buf[..n]); + } + Err(_) => panic!("Handle me!"), + } + + for message in queued_data.split(|x| *x == 0) { + // split can give us empty slices + if message.is_empty() { + continue; + } + + self.handle_message(message).unwrap(); + } + + // Clear any finished messages from the buffer + if let Some(idx) = queued_data.iter().rposition(|x| *x == 0) { + queued_data.drain(..idx); + } + + // Send any requests to the server + while let Ok(request) = self.requests.try_recv() { + self.send(request); + } + } + }) + } +} \ No newline at end of file diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs new file mode 100644 index 000000000..61a593fa5 --- /dev/null +++ b/src/langserver/debugger/auxtools_types.rs @@ -0,0 +1,48 @@ +// TODO: These will be shared properly + +use serde::{Deserialize, Serialize}; + +// Message from client -> server +#[derive(Serialize, Deserialize, Debug)] +pub enum Request { + BreakpointSet { proc: ProcRef, offset: u32, }, + BreakpointUnset { proc: ProcRef, offset: u32, }, + LineNumber { proc: ProcRef, offset: u32, }, + Offset { proc: ProcRef, line: u32, }, + Continue { kind: ContinueKind }, +} + +// Message from server -> client +#[derive(Serialize, Deserialize, Debug)] +pub enum Response { + BreakpointSet { success: bool }, + BreakpointUnset { success: bool }, + LineNumber { line: Option }, + Offset { offset: Option }, + + // Notifications (no `Request` counter-part) + // The server should send back a `Continue` request after getting this + // These should only be sent between Server/ServerThread on the notifications channel + BreakpointHit { reason: BreakpointReason }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ProcRef { + pub path: String, + pub override_id: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum BreakpointReason { + Breakpoint, + Step, + Pause, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum ContinueKind { + Continue, + StepOver, + // StepInto, + // StepOut, +} diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 78e739d3f..71cd6f7ba 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -29,6 +29,8 @@ macro_rules! debug_output { mod dap_types; mod launched; +mod auxtools_types; +mod auxtools; mod extools_types; mod extools; mod extools_bundle; From 8780c94489a9ed62dd67980386f60c4c1fef65d5 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 5 Nov 2020 03:15:23 +0000 Subject: [PATCH 02/30] milestone --- Cargo.toml | 2 +- src/langserver/Cargo.toml | 2 +- src/langserver/debugger/auxtools.rs | 524 ++++++---- src/langserver/debugger/auxtools_types.rs | 143 ++- src/langserver/debugger/dap_types.rs | 51 +- src/langserver/debugger/evaluate.rs | 57 +- src/langserver/debugger/extools.rs | 105 +- src/langserver/debugger/extools_bundle.rs | 2 +- src/langserver/debugger/extools_types.rs | 26 +- src/langserver/debugger/launched.rs | 18 +- src/langserver/debugger/mod.rs | 1099 +++++++++++++-------- 11 files changed, 1263 insertions(+), 766 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02b7b1edc..dd7a932c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [profile.dev] -opt-level = 2 +opt-level = 0 [profile.release] lto = true diff --git a/src/langserver/Cargo.toml b/src/langserver/Cargo.toml index a69d0ff09..10858b752 100644 --- a/src/langserver/Cargo.toml +++ b/src/langserver/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Tad Hardesty "] edition = "2018" [[bin]] -name = "dm-langserver" +name = "dm_langserver" path = "main.rs" [dependencies] diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 4f16078ee..e881cb07d 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -1,207 +1,317 @@ -use super::auxtools_types::*; -use std::{sync::Arc, io::{Error, Read, Write}, net::SocketAddr, net::TcpStream, thread::JoinHandle}; -use std::sync::mpsc; -use std::thread; - -use super::SequenceNumber; -use super::dap_types; - -pub struct Auxtools { - requests: mpsc::Sender, - responses: mpsc::Receiver, - _thread: JoinHandle<()>, -} - -pub struct AuxtoolsThread { - seq: Arc, - requests: mpsc::Receiver, - responses: mpsc::Sender, - stream: TcpStream, -} - -impl Auxtools { - pub fn new(addr: &SocketAddr, seq: Arc) -> std::io::Result { - let (requests_sender, requests_receiver) = mpsc::channel(); - let (responses_sender, responses_receiver) = mpsc::channel(); - - let thread = AuxtoolsThread { - seq, - requests: requests_receiver, - responses: responses_sender, - stream: TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))? - }; - - Ok(Auxtools { - requests: requests_sender, - responses: responses_receiver, - _thread: thread.start_thread(), - }) - } - - fn read_response(&mut self) -> Result { - self.responses.recv_timeout(std::time::Duration::from_secs(5)) - } - - pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { - self.requests.send(Request::LineNumber { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - offset, - }).unwrap(); - - match self.read_response() { - Ok(response) => { - match response { - Response::LineNumber { line } => { - line - } - - // TODO: disconnect - _ => panic!("received wrong response") - } - } - - // TODO: disconnect - _ => panic!("timed out") - } - } - - pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Option { - self.requests.send(Request::Offset { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - line, - }).unwrap(); - - match self.read_response() { - Ok(response) => { - match response { - Response::Offset { offset } => { - offset - } - - // TODO: disconnect - _ => panic!("received wrong response") - } - } - - // TODO: disconnect - _ => panic!("timed out") - } - } - - pub fn set_breakpoint(&mut self, path: &str, override_id: u32, offset: u32) { - self.requests.send(Request::BreakpointSet { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - offset, - }).unwrap(); - - // We ignore the result - // TODO: disconnect - self.read_response().unwrap(); - } - - pub fn unset_breakpoint(&mut self, path: &str, override_id: u32, offset: u32) { - self.requests.send(Request::BreakpointUnset { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - offset, - }).unwrap(); - - // We ignore the result - // TODO: disconnect - self.read_response().unwrap(); - } - - pub fn continue_execution(&mut self) { - // TODO: disconnect - self.requests.send(Request::Continue { - kind: ContinueKind::Continue - }).unwrap(); - } - - pub fn step_over(&mut self) { - // TODO: disconnect - self.requests.send(Request::Continue { - kind: ContinueKind::StepOver - }).unwrap(); - } -} - -impl AuxtoolsThread { - fn send(&mut self, request: Request) { - let mut message = serde_json::to_vec(&request).unwrap(); - message.push(0); // null-terminator - self.stream.write_all(&message[..]).unwrap(); - } - - fn handle_message(&mut self, data: &[u8]) -> Result<(), Box> { - let response = serde_json::from_slice::(data)?; - - match response { - Response::BreakpointHit { reason } => { - let reason = match reason { - BreakpointReason::Step => dap_types::StoppedEvent::REASON_STEP, - BreakpointReason::Pause => dap_types::StoppedEvent::REASON_PAUSE, - BreakpointReason::Breakpoint => dap_types::StoppedEvent::REASON_BREAKPOINT, - }; - - self.seq.issue_event(dap_types::StoppedEvent { - threadId: Some(0), - reason: reason.to_owned(), - .. Default::default() - }); - }, - x => { - self.responses.send(x)?; - } - } - - Ok(()) - } - - pub fn start_thread(mut self) -> JoinHandle<()> { - thread::spawn(move || { - let mut buf = [0u8; 4096]; - let mut queued_data = vec![]; - - loop { - match self.stream.read(&mut buf) { - Ok(0) => (), - Ok(n) => { - queued_data.extend_from_slice(&buf[..n]); - } - Err(_) => panic!("Handle me!"), - } - - for message in queued_data.split(|x| *x == 0) { - // split can give us empty slices - if message.is_empty() { - continue; - } - - self.handle_message(message).unwrap(); - } - - // Clear any finished messages from the buffer - if let Some(idx) = queued_data.iter().rposition(|x| *x == 0) { - queued_data.drain(..idx); - } - - // Send any requests to the server - while let Ok(request) = self.requests.try_recv() { - self.send(request); - } - } - }) - } -} \ No newline at end of file +use super::auxtools_types::*; +use std::sync::mpsc; +use std::thread; +use std::{ + collections::HashSet, + io::{Read, Write}, + net::Ipv4Addr, + net::SocketAddr, + net::TcpStream, + sync::Arc, + thread::JoinHandle, +}; + +use super::dap_types; +use super::SequenceNumber; + +pub struct Auxtools { + requests: mpsc::Sender, + responses: mpsc::Receiver, + breakpoints: HashSet, + _thread: JoinHandle<()>, +} + +pub struct AuxtoolsThread { + seq: Arc, + requests: mpsc::Receiver, + responses: mpsc::Sender, + stream: TcpStream, +} + +impl Auxtools { + pub fn new(seq: Arc, port: Option) -> std::io::Result { + let addr: SocketAddr = (Ipv4Addr::LOCALHOST, port.unwrap_or(DEFAULT_PORT)).into(); + let (requests_sender, requests_receiver) = mpsc::channel(); + let (responses_sender, responses_receiver) = mpsc::channel(); + + let stream = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))?; + stream.set_nonblocking(true)?; + + // Look at this little trouble-maker right here + seq.issue_event(dap_types::InitializedEvent); + + let thread = AuxtoolsThread { + seq, + requests: requests_receiver, + responses: responses_sender, + stream, + }; + + Ok(Auxtools { + requests: requests_sender, + responses: responses_receiver, + breakpoints: HashSet::new(), + _thread: thread.start_thread(), + }) + } + + fn read_response(&mut self) -> Result { + self.responses + .recv_timeout(std::time::Duration::from_secs(5)) + } + + pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { + self.requests + .send(Request::LineNumber { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + offset, + }) + .unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::LineNumber { line } => line, + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + + pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Option { + self.requests + .send(Request::Offset { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + line, + }) + .unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::Offset { offset } => offset, + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + + // Kinda lame: This function waits for a confirmation between set/unset + pub fn set_breakpoints(&mut self, new_breakpoints: &HashSet) { + let current_breakpoints = self.breakpoints.clone(); + + for ins in new_breakpoints { + if !current_breakpoints.contains(&ins) { + self.set_breakpoint(&ins); + } + } + + for ins in current_breakpoints { + if !new_breakpoints.contains(&ins) { + self.unset_breakpoint(&ins); + } + } + } + + fn set_breakpoint(&mut self, instruction: &InstructionRef) { + self.requests + .send(Request::BreakpointSet { + instruction: instruction.clone(), + }) + .unwrap(); + + // TODO: disconnect on fail + match self.read_response() { + Ok(response) => { + match response { + Response::BreakpointSet { success } => { + if success { + self.breakpoints.insert(instruction.clone()); + } + } + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + + fn unset_breakpoint(&mut self, instruction: &InstructionRef) { + self.requests + .send(Request::BreakpointUnset { + instruction: instruction.clone(), + }) + .unwrap(); + + // We ignore the result + // TODO: disconnect + match self.read_response() { + Ok(response) => { + match response { + Response::BreakpointUnset { success } => { + if success { + self.breakpoints.remove(instruction); + } + } + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + + pub fn continue_execution(&mut self) { + // TODO: disconnect + self.requests + .send(Request::Continue { + kind: ContinueKind::Continue, + }) + .unwrap(); + } + + pub fn next(&mut self) { + // TODO: disconnect + self.requests + .send(Request::Continue { + kind: ContinueKind::StepOver, + }) + .unwrap(); + } + + pub fn pause(&mut self) { + // TODO: disconnect + self.requests.send(Request::Pause).unwrap(); + } + + pub fn get_stack_frames( + &mut self, + thread_id: u32, + start_frame: Option, + count: Option, + ) -> (Vec, u32) { + self.requests.send(Request::StackFrames { + thread_id, + start_frame, + count, + }); + + match self.read_response() { + Ok(response) => { + match response { + Response::StackFrames { + frames, + total_count, + } => return (frames, total_count), + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + + (vec![], 0) + } +} + +impl AuxtoolsThread { + fn send(&mut self, request: Request) { + let mut message = serde_json::to_vec(&request).unwrap(); + message.push(0); // null-terminator + self.stream.write_all(&message[..]).unwrap(); + } + + fn handle_message(&mut self, data: &[u8]) -> Result<(), Box> { + let response = serde_json::from_slice::(data)?; + + match response { + Response::BreakpointHit { reason } => { + let reason = match reason { + BreakpointReason::Step => dap_types::StoppedEvent::REASON_STEP, + BreakpointReason::Pause => dap_types::StoppedEvent::REASON_PAUSE, + BreakpointReason::Breakpoint => dap_types::StoppedEvent::REASON_BREAKPOINT, + }; + + self.seq.issue_event(dap_types::StoppedEvent { + threadId: Some(0), + reason: reason.to_owned(), + ..Default::default() + }); + } + x => { + self.responses.send(x)?; + } + } + + Ok(()) + } + + pub fn start_thread(mut self) -> JoinHandle<()> { + thread::spawn(move || { + let mut buf = [0u8; 4096]; + let mut queued_data = vec![]; + + loop { + let mut got_data = false; + match self.stream.read(&mut buf) { + Ok(0) => (), + Ok(n) => { + queued_data.extend_from_slice(&buf[..n]); + got_data = true; + } + + // This is a crutch + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {} + Err(_) => panic!("Handle me!"), + } + + if got_data { + for message in queued_data.split(|x| *x == 0) { + // split can give us empty slices + if message.is_empty() { + continue; + } + + self.handle_message(message).unwrap(); + } + } + + // Clear any finished messages from the buffer + if let Some(idx) = queued_data.iter().rposition(|x| *x == 0) { + queued_data.drain(..idx); + } + + // Send any requests to the server + while let Ok(request) = self.requests.try_recv() { + self.send(request); + } + } + }) + } +} diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 61a593fa5..c7e3b42a9 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -1,48 +1,95 @@ -// TODO: These will be shared properly - -use serde::{Deserialize, Serialize}; - -// Message from client -> server -#[derive(Serialize, Deserialize, Debug)] -pub enum Request { - BreakpointSet { proc: ProcRef, offset: u32, }, - BreakpointUnset { proc: ProcRef, offset: u32, }, - LineNumber { proc: ProcRef, offset: u32, }, - Offset { proc: ProcRef, line: u32, }, - Continue { kind: ContinueKind }, -} - -// Message from server -> client -#[derive(Serialize, Deserialize, Debug)] -pub enum Response { - BreakpointSet { success: bool }, - BreakpointUnset { success: bool }, - LineNumber { line: Option }, - Offset { offset: Option }, - - // Notifications (no `Request` counter-part) - // The server should send back a `Continue` request after getting this - // These should only be sent between Server/ServerThread on the notifications channel - BreakpointHit { reason: BreakpointReason }, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ProcRef { - pub path: String, - pub override_id: u32, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum BreakpointReason { - Breakpoint, - Step, - Pause, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum ContinueKind { - Continue, - StepOver, - // StepInto, - // StepOut, -} +// TODO: These will be shared properly + +use serde::{Deserialize, Serialize}; + +pub const DEFAULT_PORT: u16 = 2448; + +// Message from client -> server +#[derive(Serialize, Deserialize, Debug)] +pub enum Request { + BreakpointSet { + instruction: InstructionRef, + }, + BreakpointUnset { + instruction: InstructionRef, + }, + LineNumber { + proc: ProcRef, + offset: u32, + }, + Offset { + proc: ProcRef, + line: u32, + }, + StackFrames { + thread_id: u32, + start_frame: Option, + count: Option, + }, + Continue { + kind: ContinueKind, + }, + Pause, +} + +// Message from server -> client +#[derive(Serialize, Deserialize, Debug)] +pub enum Response { + BreakpointSet { + success: bool, + }, + BreakpointUnset { + success: bool, + }, + LineNumber { + line: Option, + }, + Offset { + offset: Option, + }, + StackFrames { + frames: Vec, + total_count: u32, + }, + + // Notifications (no `Request` counter-part) + // The server should send back a `Continue` request after getting this + // These should only be sent between Server/ServerThread on the notifications channel + BreakpointHit { + reason: BreakpointReason, + }, +} + +#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)] +pub struct ProcRef { + pub path: String, + // TODO: this is 0 in some places + pub override_id: u32, +} + +#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)] +pub struct InstructionRef { + pub proc: ProcRef, + pub offset: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum BreakpointReason { + Breakpoint, + Step, + Pause, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum ContinueKind { + Continue, + StepOver, + // StepInto, + // StepOut, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct StackFrame { + pub instruction: InstructionRef, + pub line: Option, +} diff --git a/src/langserver/debugger/dap_types.rs b/src/langserver/debugger/dap_types.rs index 9eba3a606..284b62cdc 100644 --- a/src/langserver/debugger/dap_types.rs +++ b/src/langserver/debugger/dap_types.rs @@ -3,8 +3,8 @@ //! * https://microsoft.github.io/debug-adapter-protocol/specification #![allow(non_snake_case)] -use std::collections::HashMap; use serde_json::Value; +use std::collections::HashMap; pub trait Request { type Params; @@ -173,8 +173,8 @@ pub struct ThreadEvent { pub reason: String, /** - * The identifier of the thread. - */ + * The identifier of the thread. + */ pub threadId: i64, } @@ -575,7 +575,7 @@ pub struct EvaluateResponse { /** * The optional type of the evaluate result. */ - #[serde(rename="type")] + #[serde(rename = "type")] pub type_: Option, /** @@ -608,13 +608,19 @@ pub struct EvaluateResponse { impl From for EvaluateResponse { fn from(result: String) -> EvaluateResponse { - EvaluateResponse { result, .. Default::default() } + EvaluateResponse { + result, + ..Default::default() + } } } impl From<&str> for EvaluateResponse { fn from(result: &str) -> EvaluateResponse { - EvaluateResponse { result: result.to_owned(), .. Default::default() } + EvaluateResponse { + result: result.to_owned(), + ..Default::default() + } } } @@ -926,7 +932,10 @@ pub struct SourceResponse { impl From for SourceResponse { fn from(content: String) -> SourceResponse { - SourceResponse { content, mimeType: None } + SourceResponse { + content, + mimeType: None, + } } } @@ -1075,9 +1084,9 @@ pub struct VariablesArguments { #[derive(Serialize, Deserialize, Debug)] pub enum VariablesFilter { - #[serde(rename="indexed")] + #[serde(rename = "indexed")] Indexed, - #[serde(rename="named")] + #[serde(rename = "named")] Named, } @@ -1356,16 +1365,16 @@ pub struct DisassembledInstruction { #[derive(Serialize, Deserialize, Debug)] pub enum ExceptionBreakMode { /// never breaks - #[serde(rename="never")] + #[serde(rename = "never")] Never, /// always breaks - #[serde(rename="always")] + #[serde(rename = "always")] Always, /// breaks when exception unhandled - #[serde(rename="unhandled")] + #[serde(rename = "unhandled")] Unhandled, /// breaks if the exception is not handled by user code - #[serde(rename="userUnhandled")] + #[serde(rename = "userUnhandled")] UserUnhandled, } @@ -1658,7 +1667,6 @@ pub struct Source { * Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ pub adapterData: Option, - /*/** * The checksums associated with this file. */ @@ -1667,11 +1675,11 @@ pub struct Source { #[derive(Serialize, Deserialize, Debug)] pub enum SourcePresentationHint { - #[serde(rename="normal")] + #[serde(rename = "normal")] Normal, - #[serde(rename="emphasize")] + #[serde(rename = "emphasize")] Emphasize, - #[serde(rename="deemphasize")] + #[serde(rename = "deemphasize")] Deemphasize, } @@ -1751,7 +1759,6 @@ pub struct StackFrame { * The module associated with this frame, if any. */ moduleId?: number | string;*/ - /** * An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ @@ -1760,11 +1767,11 @@ pub struct StackFrame { #[derive(Serialize, Deserialize, Debug)] pub enum StackFramePresentationHint { - #[serde(rename="normal")] + #[serde(rename = "normal")] Normal, - #[serde(rename="label")] + #[serde(rename = "label")] Label, - #[serde(rename="subtle")] + #[serde(rename = "subtle")] Subtle, } @@ -1868,7 +1875,7 @@ pub struct Variable { /** * The type of the variable's value. Typically shown in the UI when hovering over the value. */ - #[serde(rename="type")] + #[serde(rename = "type")] pub type_: Option, /** diff --git a/src/langserver/debugger/evaluate.rs b/src/langserver/debugger/evaluate.rs index f57f352b3..e813f1eea 100644 --- a/src/langserver/debugger/evaluate.rs +++ b/src/langserver/debugger/evaluate.rs @@ -1,45 +1,68 @@ -use super::*; use super::dap_types::*; +use super::*; const EVALUATE_HELP: &str = " #dis, #disassemble: show disassembly for current stack frame"; impl Debugger { - pub fn evaluate(&mut self, params: EvaluateArguments) -> Result> { + pub fn evaluate( + &mut self, + params: EvaluateArguments, + ) -> Result> { let input = params.expression.trim_start(); if input.starts_with("#help") { return Ok(EvaluateResponse::from(EVALUATE_HELP.trim())); } - let extools = self.extools.get()?; + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; - guard!(let Some(frame_id) = params.frameId else { - return Err(Box::new(GenericError("Must select a stack frame to evaluate in"))); - }); + guard!(let Some(frame_id) = params.frameId else { + return Err(Box::new(GenericError("Must select a stack frame to evaluate in"))); + }); - let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; + let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; - if input.starts_with('#') { - if input == "#dis" || input == "#disassemble" { - guard!(let Some(frame) = thread.call_stack.get(frame_no) else { - return Err(Box::new(GenericError("Stack frame out of range"))); - }); + if input.starts_with('#') { + if input == "#dis" || input == "#disassemble" { + guard!(let Some(frame) = thread.call_stack.get(frame_no) else { + return Err(Box::new(GenericError("Stack frame out of range"))); + }); - let bytecode = extools.bytecode(&frame.proc, frame.override_id); - return Ok(EvaluateResponse::from(Self::format_disassembly(bytecode))); + let bytecode = extools.bytecode(&frame.proc, frame.override_id); + return Ok(EvaluateResponse::from(Self::format_disassembly(bytecode))); + } + } } + + DebugClient::Auxtools(_) => {} } Err(Box::new(GenericError("Not yet implemented"))) } - pub fn format_disassembly(bytecode: &[super::extools_types::DisassembledInstruction]) -> String { + pub fn format_disassembly( + bytecode: &[super::extools_types::DisassembledInstruction], + ) -> String { let mut buf = String::new(); - let bytes_max_len = bytecode.iter().map(|elem| elem.bytes.len()).max().unwrap_or(0); + let bytes_max_len = bytecode + .iter() + .map(|elem| elem.bytes.len()) + .max() + .unwrap_or(0); for instr in bytecode { use std::fmt::Write; - let _ = writeln!(buf, "{:#8X} {:width$} {} {}", instr.offset, instr.bytes, instr.mnemonic, instr.comment, width = bytes_max_len); + let _ = writeln!( + buf, + "{:#8X} {:width$} {} {}", + instr.offset, + instr.bytes, + instr.mnemonic, + instr.comment, + width = bytes_max_len + ); } buf diff --git a/src/langserver/debugger/extools.rs b/src/langserver/debugger/extools.rs index 41bf2a26b..018b9a509 100644 --- a/src/langserver/debugger/extools.rs +++ b/src/langserver/debugger/extools.rs @@ -1,15 +1,15 @@ //! Client for the Extools debugger protocol. -use std::time::Duration; -use std::sync::{mpsc, Arc, Mutex}; -use std::net::{SocketAddr, Ipv4Addr, TcpStream, TcpListener}; use std::collections::HashMap; -use std::io::{Read, Write}; use std::error::Error; +use std::io::{Read, Write}; +use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream}; +use std::sync::{mpsc, Arc, Mutex}; +use std::time::Duration; -use super::SequenceNumber; use super::dap_types; use super::extools_types::*; +use super::SequenceNumber; pub const DEFAULT_PORT: u16 = 2448; @@ -71,10 +71,10 @@ impl ExtoolsHolder { } })?; - Ok((port, ExtoolsHolder(ExtoolsHolderInner::Listening { + Ok(( port, - conn_rx, - }))) + ExtoolsHolder(ExtoolsHolderInner::Listening { port, conn_rx }), + )) } pub fn attach(seq: Arc, port: u16) -> std::io::Result { @@ -88,7 +88,9 @@ impl ExtoolsHolder { .name(format!("extools attaching on port {}", port)) .spawn(move || { while let Err(mpsc::TryRecvError::Empty) = cancel_rx.try_recv() { - if let Ok(stream) = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5)) { + if let Ok(stream) = + TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5)) + { let (conn, mut thread) = Extools::from_stream(seq, stream); if conn_tx.send(conn).is_ok() { thread.read_loop(); @@ -105,13 +107,14 @@ impl ExtoolsHolder { } pub fn get(&mut self) -> Result<&mut Extools, Box> { - self.as_ref().ok_or_else(|| Box::new(super::GenericError("No extools connection")) as Box) + self.as_ref() + .ok_or_else(|| Box::new(super::GenericError("No extools connection")) as Box) } pub fn as_ref(&mut self) -> Option<&mut Extools> { match &mut self.0 { - ExtoolsHolderInner::Listening { conn_rx, .. } | - ExtoolsHolderInner::Attaching { conn_rx, .. } => { + ExtoolsHolderInner::Listening { conn_rx, .. } + | ExtoolsHolderInner::Attaching { conn_rx, .. } => { if let Ok(conn) = conn_rx.try_recv() { self.0 = ExtoolsHolderInner::Active(conn); } @@ -120,13 +123,15 @@ impl ExtoolsHolder { } match &mut self.0 { ExtoolsHolderInner::Active(conn) => Some(conn), - _ => None + _ => None, } } pub fn disconnect(&mut self) { match std::mem::replace(&mut self.0, ExtoolsHolderInner::None) { - ExtoolsHolderInner::Attaching { cancel_tx, .. } => { let _ = cancel_tx.send(()); }, + ExtoolsHolderInner::Attaching { cancel_tx, .. } => { + let _ = cancel_tx.send(()); + } // TODO: ExtoolsHolderInner::Listening _ => {} } @@ -201,22 +206,38 @@ impl Extools { } pub fn get_thread(&self, thread_id: i64) -> Result> { - self.threads.lock().unwrap().get(&thread_id).cloned() - .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box) - } - - pub fn get_thread_by_frame_id(&self, frame_id: i64) -> Result<(ThreadInfo, usize), Box> { + self.threads + .lock() + .unwrap() + .get(&thread_id) + .cloned() + .ok_or_else(|| { + Box::new(super::GenericError("Getting call stack failed")) as Box + }) + } + + pub fn get_thread_by_frame_id( + &self, + frame_id: i64, + ) -> Result<(ThreadInfo, usize), Box> { let frame_id = frame_id as usize; let threads = self.threads.lock().unwrap(); let thread_id = (frame_id % threads.len()) as i64; let frame_no = frame_id / threads.len(); - let thread = threads.get(&thread_id).cloned() - .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box)?; + let thread = threads.get(&thread_id).cloned().ok_or_else(|| { + Box::new(super::GenericError("Getting call stack failed")) as Box + })?; Ok((thread, frame_no)) } pub fn bytecode(&mut self, proc_ref: &str, override_id: usize) -> &[DisassembledInstruction] { - let Extools { bytecode, sender, seq: _seq, bytecode_rx, .. } = self; + let Extools { + bytecode, + sender, + seq: _seq, + bytecode_rx, + .. + } = self; bytecode.entry((proc_ref.to_owned(), override_id)).or_insert_with(|| { debug_output!(in _seq, "[extools] Fetching bytecode for {}#{}", proc_ref, override_id); sender.send(ProcDisassemblyRequest(ProcId { @@ -227,7 +248,12 @@ impl Extools { }) } - pub fn offset_to_line(&mut self, proc_ref: &str, override_id: usize, offset: i64) -> Option { + pub fn offset_to_line( + &mut self, + proc_ref: &str, + override_id: usize, + offset: i64, + ) -> Option { let bc = self.bytecode(proc_ref, override_id); let mut comment = ""; for instr in bc.iter() { @@ -260,11 +286,19 @@ impl Extools { } pub fn set_breakpoint(&self, proc: &str, override_id: usize, offset: i64) { - self.sender.send(BreakpointSet(ProcOffset { proc: proc.to_owned(), override_id, offset })); + self.sender.send(BreakpointSet(ProcOffset { + proc: proc.to_owned(), + override_id, + offset, + })); } pub fn unset_breakpoint(&self, proc: &str, override_id: usize, offset: i64) { - self.sender.send(BreakpointUnset(ProcOffset { proc: proc.to_owned(), override_id, offset })); + self.sender.send(BreakpointUnset(ProcOffset { + proc: proc.to_owned(), + override_id, + offset, + })); } pub fn continue_execution(&self) { @@ -297,7 +331,10 @@ impl Extools { Ok(self.get_type_rx.recv_timeout(RECV_TIMEOUT)?.0) } - pub fn get_all_fields(&self, reference: Ref) -> Result, Box> { + pub fn get_all_fields( + &self, + reference: Ref, + ) -> Result, Box> { self.sender.send(GetAllFields(reference)); Ok(self.get_field_rx.recv_timeout(RECV_TIMEOUT)?.0) } @@ -420,13 +457,13 @@ impl ExtoolsThread { reason: "sleep".to_owned(), threadId: Some(k), preserveFocusHint: Some(true), - .. Default::default() + ..Default::default() }); } } self.seq.issue_event(dap_types::StoppedEvent { threadId: Some(0), - .. base + ..base }); } } @@ -522,7 +559,10 @@ impl Clone for ExtoolsSender { fn clone(&self) -> ExtoolsSender { ExtoolsSender { seq: self.seq.clone(), - stream: self.stream.try_clone().expect("TcpStream::try_clone failed in ExtoolsSender::clone") + stream: self + .stream + .try_clone() + .expect("TcpStream::try_clone failed in ExtoolsSender::clone"), } } } @@ -533,9 +573,12 @@ impl ExtoolsSender { let mut buffer = serde_json::to_vec(&ProtocolMessage { type_: M::TYPE.to_owned(), content: Some(content), - }).expect("extools encode error"); + }) + .expect("extools encode error"); buffer.push(0); // TODO: needs more synchronization - (&self.stream).write_all(&buffer[..]).expect("extools write error"); + (&self.stream) + .write_all(&buffer[..]) + .expect("extools write error"); } } diff --git a/src/langserver/debugger/extools_bundle.rs b/src/langserver/debugger/extools_bundle.rs index 02396526e..04c631c8a 100644 --- a/src/langserver/debugger/extools_bundle.rs +++ b/src/langserver/debugger/extools_bundle.rs @@ -1,7 +1,7 @@ #![cfg(extools_bundle)] +use std::fs::File; use std::io::{Result, Write}; use std::path::{Path, PathBuf}; -use std::fs::File; const BYTES: &[u8] = include_bytes!(env!("EXTOOLS_BUNDLE_DLL")); diff --git a/src/langserver/debugger/extools_types.rs b/src/langserver/debugger/extools_types.rs index 4b1e59465..1c1638648 100644 --- a/src/langserver/debugger/extools_types.rs +++ b/src/langserver/debugger/extools_types.rs @@ -1,12 +1,11 @@ //! Serde types for the Extools debugger protocol. //! //! * https://github.com/MCHSL/extools/blob/master/byond-extools/src/debug_server/protocol.h +use serde_json::Value as Json; /// /// > All communication happens over a TCP socket using a JSON-based protocol. /// > A null byte signifies the end of a message. - use std::collections::HashMap; -use serde_json::Value as Json; // ---------------------------------------------------------------------------- // Extools data structures @@ -132,11 +131,14 @@ impl ValueText { let ref_ = Ref(raw); let is_list = raw >> 24 == 0x0F; - (ValueText { - literal: Literal::Ref(ref_), - has_vars: !is_list, - is_list, - }, ref_) + ( + ValueText { + literal: Literal::Ref(ref_), + has_vars: !is_list, + is_list, + }, + ref_, + ) } pub fn to_variables_reference(&self) -> i64 { @@ -165,12 +167,10 @@ impl std::fmt::Display for Literal { Literal::String(s) => write!(fmt, "{:?}", s), Literal::Typepath(t) => write!(fmt, "{}", t), Literal::Resource(f) => write!(fmt, "'{}'", f), - Literal::Proc(p) => { - match p.rfind('/') { - Some(idx) => write!(fmt, "{}/proc/{}", &p[..idx], &p[idx + 1..]), - None => write!(fmt, "{}", p), - } - } + Literal::Proc(p) => match p.rfind('/') { + Some(idx) => write!(fmt, "{}/proc/{}", &p[..idx], &p[idx + 1..]), + None => write!(fmt, "{}", p), + }, } } } diff --git a/src/langserver/debugger/launched.rs b/src/langserver/debugger/launched.rs index d3dc9d70e..cc9a12ac9 100644 --- a/src/langserver/debugger/launched.rs +++ b/src/langserver/debugger/launched.rs @@ -1,9 +1,9 @@ //! Child process lifecycle management. #![allow(unsafe_code)] -use std::sync::{Arc, Mutex}; -use std::process::{Command, Stdio}; use super::{dap_types, SequenceNumber}; +use std::process::{Command, Stdio}; +use std::sync::{Arc, Mutex}; // active --kill--> killed: emit Terminated, send SIGKILL // active --detach--> detached: emit Terminated @@ -28,7 +28,13 @@ pub struct Launched { } impl Launched { - pub fn new(seq: Arc, dreamseeker_exe: &str, dmb: &str, port: Option, extools_dll: Option<&std::path::Path>) -> std::io::Result { + pub fn new( + seq: Arc, + dreamseeker_exe: &str, + dmb: &str, + port: Option, + extools_dll: Option<&std::path::Path>, + ) -> std::io::Result { let mut command = Command::new(dreamseeker_exe); command .arg(dmb) @@ -83,11 +89,7 @@ impl Launched { }); })?; - Ok(Launched { - handle, - seq, - mutex, - }) + Ok(Launched { handle, seq, mutex }) } pub fn kill(self) -> std::io::Result<()> { diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 71cd6f7ba..a705c3b1a 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -24,31 +24,36 @@ macro_rules! debug_output { #[cfg(not(debug_assertions))] macro_rules! debug_output { - ($($rest:tt)*) => { {} } + ($($rest:tt)*) => {{}}; } -mod dap_types; -mod launched; -mod auxtools_types; mod auxtools; -mod extools_types; +mod auxtools_types; +mod dap_types; +mod evaluate; mod extools; mod extools_bundle; -mod evaluate; +mod extools_types; +mod launched; +use std::collections::{HashMap, HashSet}; use std::error::Error; use std::sync::{atomic, Arc, Mutex}; -use std::collections::{HashMap, HashSet}; -use dm::FileId; use dm::objtree::ObjectTree; +use dm::FileId; + +use auxtools::Auxtools; -use crate::jrpc_io; use self::dap_types::*; -use self::launched::Launched; use self::extools::ExtoolsHolder; +use self::launched::Launched; +use crate::jrpc_io; -pub fn start_server(dreamseeker_exe: String, db: DebugDatabaseBuilder) -> std::io::Result<(u16, std::thread::JoinHandle<()>)> { +pub fn start_server( + dreamseeker_exe: String, + db: DebugDatabaseBuilder, +) -> std::io::Result<(u16, std::thread::JoinHandle<()>)> { use std::net::*; let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))?; @@ -68,28 +73,35 @@ pub fn start_server(dreamseeker_exe: String, db: DebugDatabaseBuilder) -> std::i Ok((port, handle)) } -pub fn debugger_main>(mut args: I) { +pub fn debugger_main>(mut args: I) { eprintln!("acting as debug adapter"); let mut dreamseeker_exe = None; while let Some(arg) = args.next() { if arg == "--dreamseeker-exe" { - dreamseeker_exe = Some(args.next().expect("must specify a value for --dreamseeker-exe")); + dreamseeker_exe = Some( + args.next() + .expect("must specify a value for --dreamseeker-exe"), + ); } else { panic!("unknown argument {:?}", arg); } } - let dreamseeker_exe = dreamseeker_exe.expect("must provide argument `--dreamseeker-exe path/to/dreamseeker.exe`"); + let dreamseeker_exe = + dreamseeker_exe.expect("must provide argument `--dreamseeker-exe path/to/dreamseeker.exe`"); eprintln!("dreamseeker: {}", dreamseeker_exe); // This isn't the preferred way to run the DAP server so it's okay for it // to be kind of sloppy. - let environment = dm::detect_environment_default().expect("detect .dme error").expect("did not detect a .dme"); + let environment = dm::detect_environment_default() + .expect("detect .dme error") + .expect("did not detect a .dme"); let ctx = dm::Context::default(); let mut pp = dm::preprocessor::Preprocessor::new(&ctx, environment).unwrap(); let objtree = { - let mut parser = dm::parser::Parser::new(&ctx, dm::indents::IndentProcessor::new(&ctx, &mut pp)); + let mut parser = + dm::parser::Parser::new(&ctx, dm::indents::IndentProcessor::new(&ctx, &mut pp)); parser.enable_procs(); Arc::new(parser.parse_object_tree()) }; @@ -113,23 +125,32 @@ pub struct DebugDatabaseBuilder { impl DebugDatabaseBuilder { fn build(self) -> DebugDatabase { - let DebugDatabaseBuilder { root_dir, files, objtree, extools_dll: _ } = self; - let mut line_numbers: HashMap> = HashMap::new(); + let DebugDatabaseBuilder { + root_dir, + files, + objtree, + extools_dll: _, + } = self; + let mut line_numbers: HashMap> = + HashMap::new(); objtree.root().recurse(&mut |ty| { for (name, proc) in ty.procs.iter() { - for (override_id, pv) in proc.value.iter() - .skip_while(|pv| pv.location.is_builtins() && !STDDEF_PROCS.contains(&format!("{}/{}", ty.path, name).as_str())) + for (override_id, pv) in proc + .value + .iter() + .skip_while(|pv| { + pv.location.is_builtins() + && !STDDEF_PROCS.contains(&format!("{}/{}", ty.path, name).as_str()) + }) .enumerate() { - line_numbers.entry(pv.location.file) - .or_default() - .push(( - pv.location.line.into(), - ty.path.to_owned(), - name.to_owned(), - override_id, - )); + line_numbers.entry(pv.location.file).or_default().push(( + pv.location.line.into(), + ty.path.to_owned(), + name.to_owned(), + override_id, + )); } } }); @@ -154,11 +175,17 @@ pub struct DebugDatabase { line_numbers: HashMap>, } -fn get_proc<'o>(objtree: &'o ObjectTree, proc_ref: &str, override_id: usize) -> Option<&'o dm::objtree::ProcValue> { +fn get_proc<'o>( + objtree: &'o ObjectTree, + proc_ref: &str, + override_id: usize, +) -> Option<&'o dm::objtree::ProcValue> { let mut bits: Vec<&str> = proc_ref.split('/').collect(); let procname = bits.pop().unwrap(); match bits.last() { - Some(&"proc") | Some(&"verb") => { bits.pop(); } + Some(&"proc") | Some(&"verb") => { + bits.pop(); + } _ => {} } let typename = bits.join("/"); @@ -166,7 +193,9 @@ fn get_proc<'o>(objtree: &'o ObjectTree, proc_ref: &str, override_id: usize) -> if let Some(ty) = objtree.find(&typename) { if let Some(ty_proc) = ty.get().procs.get(procname) { // Don't consider (most) builtins against the override_id count. - return ty_proc.value.iter() + return ty_proc + .value + .iter() .skip_while(|pv| pv.location.is_builtins() && !STDDEF_PROCS.contains(&proc_ref)) .nth(override_id); } @@ -181,7 +210,8 @@ impl DebugDatabase { fn file_id(&self, file_path: &str) -> Option { let path = std::path::Path::new(file_path); - self.files.get_id(path.strip_prefix(&self.root_dir).unwrap_or(path)) + self.files + .get_id(path.strip_prefix(&self.root_dir).unwrap_or(path)) } fn location_to_proc_ref(&self, file_id: FileId, line: i64) -> Option<(&str, &str, usize)> { @@ -196,12 +226,17 @@ impl DebugDatabase { } } +enum DebugClient { + Extools(ExtoolsHolder), + Auxtools(Auxtools), +} + struct Debugger { dreamseeker_exe: String, extools_dll: Option, db: DebugDatabase, launched: Option, - extools: ExtoolsHolder, + client: DebugClient, seq: Arc, client_caps: ClientCaps, @@ -217,7 +252,7 @@ impl Debugger { extools_dll: db.extools_dll.take(), db: db.build(), launched: None, - extools: ExtoolsHolder::default(), + client: DebugClient::Extools(ExtoolsHolder::default()), seq: Arc::new(SequenceNumber::new(stream)), client_caps: Default::default(), @@ -229,7 +264,8 @@ impl Debugger { fn handle_input(&mut self, message: &str) { // TODO: error handling - self.handle_input_inner(message).expect("error in handle_input"); + self.handle_input_inner(message) + .expect("error in handle_input"); } fn handle_input_inner(&mut self, message: &str) -> Result<(), Box> { @@ -241,7 +277,9 @@ impl Debugger { let command = request.command.clone(); let handled = match Self::handle_request_table(&request.command) { - Some(handler) => handler(self, request.arguments.unwrap_or(serde_json::Value::Null)), + Some(handler) => { + handler(self, request.arguments.unwrap_or(serde_json::Value::Null)) + } None => Err(format!("Request NYI: {}", request.command).into()), }; @@ -261,13 +299,14 @@ impl Debugger { } debug_output!(in self.seq, " - {}", message); None - }, + } }, command, }; - self.seq.send_raw(&serde_json::to_string(&response).expect("response encode error")) + self.seq + .send_raw(&serde_json::to_string(&response).expect("response encode error")) } - other => return Err(format!("unknown `type` field {:?}", other).into()) + other => return Err(format!("unknown `type` field {:?}", other).into()), } Ok(()) } @@ -294,16 +333,26 @@ impl Debugger { // An alternative would be to send these in real-time when sleeping // threads enter or exit existence. + match &mut self.client { + DebugClient::Extools(extools) => { + let keys: Vec<_> = { + guard!(let Ok(extools) = extools.get() else { return }); + extools + .get_all_threads() + .keys() + .cloned() + .filter(|&k| k != 0) + .collect() + }; + for k in keys { + self.issue_event(dap_types::ThreadEvent { + reason: dap_types::ThreadEvent::REASON_EXITED.to_owned(), + threadId: k, + }); + } + } - let keys: Vec<_> = { - guard!(let Ok(extools) = self.extools.get() else { return }); - extools.get_all_threads().keys().cloned().filter(|&k| k != 0).collect() - }; - for k in keys { - self.issue_event(dap_types::ThreadEvent { - reason: dap_types::ThreadEvent::REASON_EXITED.to_owned(), - threadId: k, - }); + DebugClient::Auxtools(_) => {} } } } @@ -350,7 +399,7 @@ handle_request! { let debug = !params.base.noDebug.unwrap_or(false); let port = if debug { let (port, extools) = ExtoolsHolder::listen(self.seq.clone())?; - self.extools = extools; + self.client = DebugClient::Extools(extools); Some(port) } else { None @@ -377,14 +426,32 @@ handle_request! { } on AttachVsc(&mut self, params) { - self.extools = ExtoolsHolder::attach(self.seq.clone(), params.port.unwrap_or(extools::DEFAULT_PORT))?; + std::thread::sleep(std::time::Duration::from_secs(10)); + self.client = DebugClient::Auxtools(Auxtools::new(self.seq.clone(), params.port)?); + /*self.client = match params.client { + DebugClientParam::Extools => { + DebugClient::Extools(ExtoolsHolder::attach(self.seq.clone(), params.port.unwrap_or(extools::DEFAULT_PORT))?) + } + + DebugClientParam::Auxtools => { + DebugClient::Auxtools(Auxtools::new(self.seq.clone(), params.port)?) + } + };*/ } on Disconnect(&mut self, params) { let default_terminate = self.launched.is_some(); let terminate = params.terminateDebuggee.unwrap_or(default_terminate); - self.extools.disconnect(); + match &mut self.client { + DebugClient::Extools(extools) => { + extools.disconnect(); + } + + DebugClient::Auxtools(auxtools) => { + //auxtools.disconnect(); + } + } if let Some(launched) = self.launched.take() { if terminate { @@ -396,34 +463,54 @@ handle_request! { } on ConfigurationDone(&mut self, ()) { - let extools = self.extools.get()?; + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + + let text = extools.get_source("stddef.dm".to_owned())?; + self.stddef_dm_info = Some(StddefDmInfo::new(text)); - let text = extools.get_source("stddef.dm".to_owned())?; - self.stddef_dm_info = Some(StddefDmInfo::new(text)); + extools.configuration_done(); + } + + DebugClient::Auxtools(auxtools) => { + + } + } - extools.configuration_done(); } on Threads(&mut self, ()) { - let mut threads = Vec::new(); - - let extools = self.extools.get()?; - for (&k, v) in extools.get_all_threads().iter() { - threads.push(Thread { - id: k, - name: v.call_stack.last().unwrap().proc.clone(), - }); - } + match &mut self.client { + DebugClient::Extools(extools) => { - if threads.is_empty() { - threads.push(Thread { - id: 0, - name: "Main".to_owned(), - }); - } + let mut threads = Vec::new(); + + let extools = extools.get()?; + for (&k, v) in extools.get_all_threads().iter() { + threads.push(Thread { + id: k, + name: v.call_stack.last().unwrap().proc.clone(), + }); + } - ThreadsResponse { - threads, + if threads.is_empty() { + threads.push(Thread { + id: 0, + name: "Main".to_owned(), + }); + } + + ThreadsResponse { + threads, + } + }, + + DebugClient::Auxtools(auxtools) => { + ThreadsResponse { + threads: vec![Thread { id: 0, name: "Main".to_owned() }], + } + } } } @@ -442,70 +529,78 @@ handle_request! { let inputs = params.breakpoints.unwrap_or_default(); let mut breakpoints = Vec::new(); - guard!(let Some(extools) = self.extools.as_ref() else { - for sbp in inputs { - breakpoints.push(Breakpoint { - message: Some("Debugging hooks not available".to_owned()), - line: Some(sbp.line), - verified: false, - .. Default::default() + match &mut self.client { + DebugClient::Extools(extools) => { + guard!(let Some(extools) = extools.as_ref() else { + for sbp in inputs { + breakpoints.push(Breakpoint { + message: Some("Debugging hooks not available".to_owned()), + line: Some(sbp.line), + verified: false, + .. Default::default() + }); + } + return Ok(SetBreakpointsResponse { breakpoints }); }); - } - return Ok(SetBreakpointsResponse { breakpoints }); - }); - let saved = self.saved_breakpoints.entry(file_id).or_default(); - let mut keep = HashSet::new(); - - for sbp in inputs { - if let Some((typepath, name, override_id)) = self.db.location_to_proc_ref(file_id, sbp.line) { - // TODO: better discipline around format!("{}/{}") and so on - let proc = format!("{}/{}", typepath, name); - if let Some(offset) = extools.line_to_offset(&proc, override_id, sbp.line) { - let tup = (proc, override_id, offset); - if saved.insert(tup.clone()) { - extools.set_breakpoint(&tup.0, tup.1, tup.2); + let saved = self.saved_breakpoints.entry(file_id).or_default(); + let mut keep = HashSet::new(); + + for sbp in inputs { + if let Some((typepath, name, override_id)) = self.db.location_to_proc_ref(file_id, sbp.line) { + // TODO: better discipline around format!("{}/{}") and so on + let proc = format!("{}/{}", typepath, name); + if let Some(offset) = extools.line_to_offset(&proc, override_id, sbp.line) { + let tup = (proc, override_id, offset); + if saved.insert(tup.clone()) { + extools.set_breakpoint(&tup.0, tup.1, tup.2); + } + keep.insert(tup); + breakpoints.push(Breakpoint { + line: Some(sbp.line), + verified: true, + column: Some(0), + .. Default::default() + }); + } else { + debug_output!(in self.seq, + "Couldn't find line {} in the following disassembly:\n{}", + sbp.line, + Self::format_disassembly(extools.bytecode(&proc, override_id))); + + breakpoints.push(Breakpoint { + message: Some("Unable to determine offset in proc".to_owned()), + line: Some(sbp.line), + verified: false, + .. Default::default() + }); + } + } else { + breakpoints.push(Breakpoint { + message: Some("Unable to determine proc ref".to_owned()), + line: Some(sbp.line), + verified: false, + .. Default::default() + }); } - keep.insert(tup); - breakpoints.push(Breakpoint { - line: Some(sbp.line), - verified: true, - column: Some(0), - .. Default::default() - }); - } else { - debug_output!(in self.seq, - "Couldn't find line {} in the following disassembly:\n{}", - sbp.line, - Self::format_disassembly(extools.bytecode(&proc, override_id))); - - breakpoints.push(Breakpoint { - message: Some("Unable to determine offset in proc".to_owned()), - line: Some(sbp.line), - verified: false, - .. Default::default() - }); } - } else { - breakpoints.push(Breakpoint { - message: Some("Unable to determine proc ref".to_owned()), - line: Some(sbp.line), - verified: false, - .. Default::default() + + saved.retain(|k| { + if !keep.contains(&k) { + extools.unset_breakpoint(&k.0, k.1, k.2); + false + } else { + true + } }); - } - } - saved.retain(|k| { - if !keep.contains(&k) { - extools.unset_breakpoint(&k.0, k.1, k.2); - false - } else { - true + SetBreakpointsResponse { breakpoints } } - }); - SetBreakpointsResponse { breakpoints } + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't set breakpoints yet"))); + } + } } on SetFunctionBreakpoints(&mut self, params) { @@ -514,301 +609,405 @@ handle_request! { let inputs = params.breakpoints; let mut breakpoints = Vec::new(); - guard!(let Some(extools) = self.extools.as_ref() else { - for _ in inputs { - breakpoints.push(Breakpoint { - message: Some("Debugging hooks not available".to_owned()), - verified: false, - .. Default::default() + match &mut self.client { + DebugClient::Extools(extools) => { + guard!(let Some(extools) = extools.as_ref() else { + for _ in inputs { + breakpoints.push(Breakpoint { + message: Some("Debugging hooks not available".to_owned()), + verified: false, + .. Default::default() + }); + } + return Ok(SetFunctionBreakpointsResponse { breakpoints }); }); - } - return Ok(SetFunctionBreakpointsResponse { breakpoints }); - }); - let saved = self.saved_breakpoints.entry(file_id).or_default(); - let mut keep = HashSet::new(); + let saved = self.saved_breakpoints.entry(file_id).or_default(); + let mut keep = HashSet::new(); - for sbp in inputs { - // parse function reference - let mut proc = &sbp.name[..]; - let mut override_id = 0; - if let Some(idx) = sbp.name.find('#') { - proc = &sbp.name[..idx]; - override_id = sbp.name[idx+1..].parse()?; - } + for sbp in inputs { + // parse function reference + let mut proc = &sbp.name[..]; + let mut override_id = 0; + if let Some(idx) = sbp.name.find('#') { + proc = &sbp.name[..idx]; + override_id = sbp.name[idx+1..].parse()?; + } - if let Some(proc_ref) = self.db.get_proc(proc, override_id) { - let offset = 0; - let tup = (proc.to_owned(), override_id, offset); - if saved.insert(tup.clone()) { - extools.set_breakpoint(&tup.0, tup.1, tup.2); + if let Some(proc_ref) = self.db.get_proc(proc, override_id) { + let offset = 0; + let tup = (proc.to_owned(), override_id, offset); + if saved.insert(tup.clone()) { + extools.set_breakpoint(&tup.0, tup.1, tup.2); + } + keep.insert(tup); + breakpoints.push(Breakpoint { + line: Some(proc_ref.location.line as i64), + verified: true, + column: Some(0), + .. Default::default() + }); + } else { + breakpoints.push(Breakpoint { + message: Some(format!("Unknown proc {}#{}", proc, override_id)), + verified: false, + .. Default::default() + }); + } } - keep.insert(tup); - breakpoints.push(Breakpoint { - line: Some(proc_ref.location.line as i64), - verified: true, - column: Some(0), - .. Default::default() - }); - } else { - breakpoints.push(Breakpoint { - message: Some(format!("Unknown proc {}#{}", proc, override_id)), - verified: false, - .. Default::default() + + saved.retain(|k| { + if !keep.contains(&k) { + extools.unset_breakpoint(&k.0, k.1, k.2); + false + } else { + true + } }); - } - } - saved.retain(|k| { - if !keep.contains(&k) { - extools.unset_breakpoint(&k.0, k.1, k.2); - false - } else { - true + SetFunctionBreakpointsResponse { breakpoints } } - }); - SetFunctionBreakpointsResponse { breakpoints } + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't set breakpoints yet"))); + } + } } on StackTrace(&mut self, params) { - let extools = self.extools.get()?; - let thread = extools.get_thread(params.threadId)?; - - let len = thread.call_stack.len(); - let mut frames = Vec::with_capacity(len); - for (i, ex_frame) in thread.call_stack.into_iter().enumerate() { - let mut dap_frame = StackFrame { - name: ex_frame.proc.clone(), - id: (i * extools.get_all_threads().len()) as i64 + params.threadId, - instructionPointerReference: Some(format!("{}@{}#{}", ex_frame.proc, ex_frame.override_id, ex_frame.offset)), - .. Default::default() - }; - - if i == 0 { - // Column must be nonzero for VSC to show the exception widget, - // but we don't usually have meaningful column information. - dap_frame.column = 1; - } + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + let thread = extools.get_thread(params.threadId)?; + + let len = thread.call_stack.len(); + let mut frames = Vec::with_capacity(len); + for (i, ex_frame) in thread.call_stack.into_iter().enumerate() { + let mut dap_frame = StackFrame { + name: ex_frame.proc.clone(), + id: (i * extools.get_all_threads().len()) as i64 + params.threadId, + instructionPointerReference: Some(format!("{}@{}#{}", ex_frame.proc, ex_frame.override_id, ex_frame.offset)), + .. Default::default() + }; + + if i == 0 { + // Column must be nonzero for VSC to show the exception widget, + // but we don't usually have meaningful column information. + dap_frame.column = 1; + } + + if let Some(proc) = self.db.get_proc(&ex_frame.proc, ex_frame.override_id) { + if proc.location.is_builtins() { + // `stddef.dm` proc. + if let Some(stddef_dm_info) = self.stddef_dm_info.as_ref() { + if let Some(proc) = get_proc(&stddef_dm_info.objtree, &ex_frame.proc, ex_frame.override_id) { + dap_frame.source = Some(Source { + name: Some("stddef.dm".to_owned()), + sourceReference: Some(STDDEF_SOURCE_REFERENCE), + .. Default::default() + }); + dap_frame.line = i64::from(proc.location.line); + //dap_frame.column = i64::from(proc.location.column); + } + } + } else { + // Normal proc. + let path = self.db.files.get_path(proc.location.file); - if let Some(proc) = self.db.get_proc(&ex_frame.proc, ex_frame.override_id) { - if proc.location.is_builtins() { - // `stddef.dm` proc. - if let Some(stddef_dm_info) = self.stddef_dm_info.as_ref() { - if let Some(proc) = get_proc(&stddef_dm_info.objtree, &ex_frame.proc, ex_frame.override_id) { dap_frame.source = Some(Source { - name: Some("stddef.dm".to_owned()), - sourceReference: Some(STDDEF_SOURCE_REFERENCE), + name: Some(path.file_name() + .unwrap_or_default() + .to_string_lossy() + .into_owned()), + path: Some(self.db.root_dir.join(path).to_string_lossy().into_owned()), .. Default::default() }); dap_frame.line = i64::from(proc.location.line); //dap_frame.column = i64::from(proc.location.column); } } - } else { - // Normal proc. - let path = self.db.files.get_path(proc.location.file); - - dap_frame.source = Some(Source { - name: Some(path.file_name() - .unwrap_or_default() - .to_string_lossy() - .into_owned()), - path: Some(self.db.root_dir.join(path).to_string_lossy().into_owned()), - .. Default::default() - }); - dap_frame.line = i64::from(proc.location.line); - //dap_frame.column = i64::from(proc.location.column); + + if let Some(line) = extools.offset_to_line(&ex_frame.proc, ex_frame.override_id, ex_frame.offset) { + dap_frame.line = line; + } + + frames.push(dap_frame); } - } - if let Some(line) = extools.offset_to_line(&ex_frame.proc, ex_frame.override_id, ex_frame.offset) { - dap_frame.line = line; + StackTraceResponse { + totalFrames: Some(len as i64), + stackFrames: frames, + } } - frames.push(dap_frame); - } + DebugClient::Auxtools(auxtools) => { + let (aux_frames, aux_frames_total) = auxtools.get_stack_frames( + params.threadId as u32, + params.startFrame.map(|x| x as u32), + params.levels.map(|x| x as u32)); + + let mut frames = Vec::with_capacity(aux_frames.len()); + for (i, aux_frame) in aux_frames.iter().enumerate() { + let aux_proc = &aux_frame.instruction.proc; + let mut dap_frame = StackFrame { + name: aux_proc.path.to_owned(), + id: params.threadId, // TODO: multiple threads + instructionPointerReference: Some(format!("{}@{}#{}", aux_proc.path, aux_proc.override_id, aux_frame.instruction.offset)), + .. Default::default() + }; + + if i == 0 { + // Column must be nonzero for VSC to show the exception widget, + // but we don't usually have meaningful column information. + dap_frame.column = 1; + } - StackTraceResponse { - totalFrames: Some(len as i64), - stackFrames: frames, + if let Some(proc) = self.db.get_proc(&aux_proc.path, aux_proc.override_id as usize) { + if proc.location.is_builtins() { + // `stddef.dm` proc. + if let Some(stddef_dm_info) = self.stddef_dm_info.as_ref() { + if let Some(proc) = get_proc(&stddef_dm_info.objtree, &aux_proc.path, aux_proc.override_id as usize) { + dap_frame.source = Some(Source { + name: Some("stddef.dm".to_owned()), + sourceReference: Some(STDDEF_SOURCE_REFERENCE), + .. Default::default() + }); + dap_frame.line = i64::from(proc.location.line); + //dap_frame.column = i64::from(proc.location.column); + } + } + } else { + // Normal proc. + let path = self.db.files.get_path(proc.location.file); + + dap_frame.source = Some(Source { + name: Some(path.file_name() + .unwrap_or_default() + .to_string_lossy() + .into_owned()), + path: Some(self.db.root_dir.join(path).to_string_lossy().into_owned()), + .. Default::default() + }); + dap_frame.line = i64::from(proc.location.line); + //dap_frame.column = i64::from(proc.location.column); + } + } + + if let Some(line) = aux_frame.line { + dap_frame.line = line as i64; + } + + frames.push(dap_frame); + } + + StackTraceResponse { + totalFrames: Some(aux_frames_total as i64), + stackFrames: frames, + } + } } } on Scopes(&mut self, ScopesArguments { frameId }) { - let extools = self.extools.get()?; - let frame_id = frameId as usize; + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + let frame_id = frameId as usize; - let threads = extools.get_all_threads(); - let thread_id = (frame_id % threads.len()) as i64; - let frame_no = frame_id / threads.len(); + let threads = extools.get_all_threads(); + let thread_id = (frame_id % threads.len()) as i64; + let frame_no = frame_id / threads.len(); - guard!(let Some(frame) = threads[&thread_id].call_stack.get(frame_no) else { - return Err(Box::new(GenericError2(format!("Stack frame out of range: {} (thread {}, depth {})", frameId, thread_id, frame_no)))); - }); + guard!(let Some(frame) = threads[&thread_id].call_stack.get(frame_no) else { + return Err(Box::new(GenericError2(format!("Stack frame out of range: {} (thread {}, depth {})", frameId, thread_id, frame_no)))); + }); - ScopesResponse { - scopes: vec![ - Scope { - name: "Locals".to_owned(), - presentationHint: Some("locals".to_owned()), - variablesReference: frameId * 2 + 2, - indexedVariables: Some(frame.locals.len() as i64), - .. Default::default() - }, - Scope { - name: "Arguments".to_owned(), - presentationHint: Some("arguments".to_owned()), - variablesReference: frameId * 2 + 1, - namedVariables: Some(2 + frame.args.len() as i64), - .. Default::default() - }, - Scope { - name: "Globals".to_owned(), - variablesReference: 0x0e_000001, - .. Default::default() - }, - ] + ScopesResponse { + scopes: vec![ + Scope { + name: "Locals".to_owned(), + presentationHint: Some("locals".to_owned()), + variablesReference: frameId * 2 + 2, + indexedVariables: Some(frame.locals.len() as i64), + .. Default::default() + }, + Scope { + name: "Arguments".to_owned(), + presentationHint: Some("arguments".to_owned()), + variablesReference: frameId * 2 + 1, + namedVariables: Some(2 + frame.args.len() as i64), + .. Default::default() + }, + Scope { + name: "Globals".to_owned(), + variablesReference: 0x0e_000001, + .. Default::default() + }, + ] + } + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't scopes yet"))); + } } } on Variables(&mut self, params) { - let extools = self.extools.get()?; - - if params.variablesReference >= 0x01_000000 { - let (var, ref_) = extools_types::ValueText::from_variables_reference(params.variablesReference); - let mut variables = Vec::new(); - - if var.is_list { - // List reference - match extools.get_list_contents(ref_)? { - extools_types::ListContents::Linear(entries) => { - for (i, entry) in entries.iter().enumerate() { - variables.push(Variable { - name: format!("[{}]", 1 + i), - value: entry.to_string(), - variablesReference: entry.to_variables_reference(), - .. Default::default() - }); + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + + if params.variablesReference >= 0x01_000000 { + let (var, ref_) = extools_types::ValueText::from_variables_reference(params.variablesReference); + let mut variables = Vec::new(); + + if var.is_list { + // List reference + match extools.get_list_contents(ref_)? { + extools_types::ListContents::Linear(entries) => { + for (i, entry) in entries.iter().enumerate() { + variables.push(Variable { + name: format!("[{}]", 1 + i), + value: entry.to_string(), + variablesReference: entry.to_variables_reference(), + .. Default::default() + }); + } + } + extools_types::ListContents::Associative(entries) => { + for (i, (key, val)) in entries.iter().enumerate() { + variables.push(Variable { + name: format!("keys[{}]", 1 + i), + value: key.to_string(), + variablesReference: key.to_variables_reference(), + .. Default::default() + }); + variables.push(Variable { + name: format!("vals[{}]", 1 + i), + value: val.to_string(), + variablesReference: val.to_variables_reference(), + .. Default::default() + }); + } + } } - } - extools_types::ListContents::Associative(entries) => { - for (i, (key, val)) in entries.iter().enumerate() { + } else if var.has_vars { + // Datum reference + let hashmap = extools.get_all_fields(ref_)?; + let mut entries: Vec<_> = hashmap.iter().collect(); + entries.sort_unstable_by_key(|tup| tup.0); + for (name, vt) in entries { variables.push(Variable { - name: format!("keys[{}]", 1 + i), - value: key.to_string(), - variablesReference: key.to_variables_reference(), + name: name.to_owned(), + value: vt.to_string(), + variablesReference: vt.to_variables_reference(), .. Default::default() - }); - variables.push(Variable { - name: format!("vals[{}]", 1 + i), - value: val.to_string(), - variablesReference: val.to_variables_reference(), - .. Default::default() - }); + }) } } + + return Ok(VariablesResponse { variables }); } - } else if var.has_vars { - // Datum reference - let hashmap = extools.get_all_fields(ref_)?; - let mut entries: Vec<_> = hashmap.iter().collect(); - entries.sort_unstable_by_key(|tup| tup.0); - for (name, vt) in entries { + + // Stack frame, arguments or locals + let frame_id = (params.variablesReference - 1) / 2; + let mod2 = params.variablesReference % 2; + + let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; + guard!(let Some(frame) = thread.call_stack.get(frame_no) else { + return Err(Box::new(GenericError("Stack frame out of range"))); + }); + + if mod2 == 1 { + // arguments + let mut variables = Vec::with_capacity(2 + frame.args.len()); + let mut seen = std::collections::HashMap::new(); + + seen.insert("src", 0); + variables.push(Variable { + name: "src".to_owned(), + value: frame.src.to_string(), + variablesReference: frame.src.to_variables_reference(), + .. Default::default() + }); + seen.insert("usr", 0); + variables.push(Variable { + name: "usr".to_owned(), + value: frame.usr.to_string(), + variablesReference: frame.usr.to_variables_reference(), + .. Default::default() + }); + + variables.extend(frame.args.iter().enumerate().map(|(i, vt)| Variable { + name: match frame.arg_names.get(i) { + Some(param) => { + match seen.entry(param).and_modify(|e| *e += 1).or_default() { + 0 => param.clone(), + n => format!("{} #{}", param, n), + } + } + None => format!("args[{}]", i + 1), + }, + value: vt.to_string(), + variablesReference: vt.to_variables_reference(), + .. Default::default() + })); + VariablesResponse { variables } + } else if mod2 == 0 { + // locals + let mut variables = Vec::with_capacity(1 + frame.locals.len()); + variables.push(Variable { - name: name.to_owned(), + name: ".".to_owned(), + value: frame.dot.to_string(), + variablesReference: frame.dot.to_variables_reference(), + .. Default::default() + }); + + // If VSC receives two Variables with the same name, it only + // displays the first one. Avert this by adding suffixes. + let mut seen = std::collections::HashMap::new(); + variables.extend(frame.locals.iter().enumerate().map(|(i, vt)| Variable { + name: match frame.local_names.get(i) { + Some(local) => { + match seen.entry(local).and_modify(|e| *e += 1).or_default() { + 0 => local.clone(), + n => format!("{} #{}", local, n), + } + } + None => i.to_string(), + }, value: vt.to_string(), variablesReference: vt.to_variables_reference(), .. Default::default() - }) + })); + VariablesResponse { variables } + } else { + return Err(Box::new(GenericError("Bad variables reference"))); } } - return Ok(VariablesResponse { variables }); + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't variables yet"))); + } } + } - // Stack frame, arguments or locals - let frame_id = (params.variablesReference - 1) / 2; - let mod2 = params.variablesReference % 2; + on Continue(&mut self, _params) { + self.cull_thread_list(); - let (thread, frame_no) = extools.get_thread_by_frame_id(frame_id)?; - guard!(let Some(frame) = thread.call_stack.get(frame_no) else { - return Err(Box::new(GenericError("Stack frame out of range"))); - }); + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.continue_execution(); + } - if mod2 == 1 { - // arguments - let mut variables = Vec::with_capacity(2 + frame.args.len()); - let mut seen = std::collections::HashMap::new(); - - seen.insert("src", 0); - variables.push(Variable { - name: "src".to_owned(), - value: frame.src.to_string(), - variablesReference: frame.src.to_variables_reference(), - .. Default::default() - }); - seen.insert("usr", 0); - variables.push(Variable { - name: "usr".to_owned(), - value: frame.usr.to_string(), - variablesReference: frame.usr.to_variables_reference(), - .. Default::default() - }); - - variables.extend(frame.args.iter().enumerate().map(|(i, vt)| Variable { - name: match frame.arg_names.get(i) { - Some(param) => { - match seen.entry(param).and_modify(|e| *e += 1).or_default() { - 0 => param.clone(), - n => format!("{} #{}", param, n), - } - } - None => format!("args[{}]", i + 1), - }, - value: vt.to_string(), - variablesReference: vt.to_variables_reference(), - .. Default::default() - })); - VariablesResponse { variables } - } else if mod2 == 0 { - // locals - let mut variables = Vec::with_capacity(1 + frame.locals.len()); - - variables.push(Variable { - name: ".".to_owned(), - value: frame.dot.to_string(), - variablesReference: frame.dot.to_variables_reference(), - .. Default::default() - }); - - // If VSC receives two Variables with the same name, it only - // displays the first one. Avert this by adding suffixes. - let mut seen = std::collections::HashMap::new(); - variables.extend(frame.locals.iter().enumerate().map(|(i, vt)| Variable { - name: match frame.local_names.get(i) { - Some(local) => { - match seen.entry(local).and_modify(|e| *e += 1).or_default() { - 0 => local.clone(), - n => format!("{} #{}", local, n), - } - } - None => i.to_string(), - }, - value: vt.to_string(), - variablesReference: vt.to_variables_reference(), - .. Default::default() - })); - VariablesResponse { variables } - } else { - return Err(Box::new(GenericError("Bad variables reference"))); + DebugClient::Auxtools(auxtools) => { + auxtools.continue_execution(); + } } - } - on Continue(&mut self, _params) { - self.cull_thread_list(); - let extools = self.extools.get()?; - extools.continue_execution(); ContinueResponse { allThreadsContinued: Some(true), } @@ -816,41 +1015,92 @@ handle_request! { on StepIn(&mut self, params) { self.notify_continue(); - let extools = self.extools.get()?; - extools.step_in(params.threadId); + + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.step_in(params.threadId); + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't step-in yet"))); + } + } } on Next(&mut self, params) { self.notify_continue(); - let extools = self.extools.get()?; - extools.step_over(params.threadId); + + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.step_over(params.threadId); + } + + DebugClient::Auxtools(auxtools) => { + auxtools.next(); + } + } } on StepOut(&mut self, params) { self.notify_continue(); - let extools = self.extools.get()?; - extools.step_out(params.threadId); + + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.step_out(params.threadId); + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't step-out yet"))); + } + } } on Pause(&mut self, _params) { - let extools = self.extools.get()?; - extools.pause(); + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.pause(); + } + + DebugClient::Auxtools(auxtools) => { + auxtools.pause(); + } + } } on SetExceptionBreakpoints(&mut self, params) { - let extools = self.extools.get()?; - extools.set_break_on_runtime(params.filters.iter().any(|x| x == EXCEPTION_FILTER_RUNTIMES)); + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + extools.set_break_on_runtime(params.filters.iter().any(|x| x == EXCEPTION_FILTER_RUNTIMES)); + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't set-exception-breakpoints yet"))); + } + } } on ExceptionInfo(&mut self, _params) { - let extools = self.extools.get()?; - // VSC shows exceptionId, description, stackTrace in that order. - let message = extools.last_error_message(); - ExceptionInfoResponse { - exceptionId: message.unwrap_or_default().to_owned(), - description: None, - breakMode: ExceptionBreakMode::Always, - details: None, + match &mut self.client { + DebugClient::Extools(extools) => { + let extools = extools.get()?; + // VSC shows exceptionId, description, stackTrace in that order. + let message = extools.last_error_message(); + ExceptionInfoResponse { + exceptionId: message.unwrap_or_default().to_owned(), + description: None, + breakMode: ExceptionBreakMode::Always, + details: None, + } + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't exception-info yet"))); + } } } @@ -878,26 +1128,34 @@ handle_request! { } on Disassemble(&mut self, params) { - guard!(let Some(captures) = MEMORY_REFERENCE_REGEX.captures(¶ms.memoryReference) else { - return Err(Box::new(GenericError("Invalid memory reference"))); - }); - let proc = &captures[1]; - let override_id: usize = captures[2].parse()?; - //let offset: i64 = captures[3].parse()?; - - let extools = self.extools.get()?; - let mut result = Vec::new(); - for instr in extools.bytecode(proc, override_id) { - result.push(DisassembledInstruction { - address: format!("{}#{}@{}", proc, override_id, instr.offset), - instructionBytes: Some(instr.bytes.clone()), - instruction: format!("{} {}", instr.mnemonic, instr.comment), - .. Default::default() - }); - } + match &mut self.client { + DebugClient::Extools(extools) => { + guard!(let Some(captures) = MEMORY_REFERENCE_REGEX.captures(¶ms.memoryReference) else { + return Err(Box::new(GenericError("Invalid memory reference"))); + }); + let proc = &captures[1]; + let override_id: usize = captures[2].parse()?; + //let offset: i64 = captures[3].parse()?; + + let extools = extools.get()?; + let mut result = Vec::new(); + for instr in extools.bytecode(proc, override_id) { + result.push(DisassembledInstruction { + address: format!("{}#{}@{}", proc, override_id, instr.offset), + instructionBytes: Some(instr.bytes.clone()), + instruction: format!("{} {}", instr.mnemonic, instr.comment), + .. Default::default() + }); + } - DisassembleResponse { - instructions: result + DisassembleResponse { + instructions: result + } + } + + DebugClient::Auxtools(auxtools) => { + return Err(Box::new(GenericError("auxtools can't disassemble yet"))); + } } } } @@ -968,7 +1226,7 @@ impl SequenceNumber { output.push('\n'); self.issue_event(OutputEvent { output, - .. Default::default() + ..Default::default() }) } @@ -978,7 +1236,7 @@ impl SequenceNumber { self.issue_event(OutputEvent { output, category: Some("console".to_owned()), - .. Default::default() + ..Default::default() }) } @@ -992,7 +1250,9 @@ impl SequenceNumber { pub struct GenericError(&'static str); impl Error for GenericError { - fn description(&self) -> &str { self.0 } + fn description(&self) -> &str { + self.0 + } } impl std::fmt::Display for GenericError { @@ -1005,7 +1265,9 @@ impl std::fmt::Display for GenericError { pub struct GenericError2(String); impl Error for GenericError2 { - fn description(&self) -> &str { &self.0 } + fn description(&self) -> &str { + &self.0 + } } impl std::fmt::Display for GenericError2 { @@ -1025,6 +1287,12 @@ impl Request for LaunchVsc { const COMMAND: &'static str = Launch::COMMAND; } +#[derive(Deserialize)] +pub enum DebugClientParam { + Extools, + Auxtools, +} + #[derive(Deserialize)] pub struct LaunchRequestArgumentsVsc { #[serde(flatten)] @@ -1032,7 +1300,6 @@ pub struct LaunchRequestArgumentsVsc { // provided by vscode dmb: String, - // other keys: __sessionId, name, preLaunchTask, request, type } @@ -1048,7 +1315,7 @@ impl Request for AttachVsc { pub struct AttachRequestArgumentsVsc { #[serde(flatten)] base: AttachRequestArguments, - + //client: DebugClientParam, port: Option, } @@ -1109,7 +1376,7 @@ const STDDEF_PROCS: &[&str] = &[ "/exception/New", "/regex/New", "/regex/Find", - "/regex/Replace" + "/regex/Replace", ]; const STDDEF_SOURCE_REFERENCE: i64 = 1; @@ -1123,11 +1390,9 @@ impl StddefDmInfo { fn new(text: String) -> StddefDmInfo { let context = dm::Context::default(); let pp = dm::preprocessor::Preprocessor::from_buffer(&context, "stddef.dm".into(), &text); - let parser = dm::parser::Parser::new(&context, dm::indents::IndentProcessor::new(&context, pp)); + let parser = + dm::parser::Parser::new(&context, dm::indents::IndentProcessor::new(&context, pp)); let objtree = parser.parse_object_tree_without_builtins(); - StddefDmInfo { - text, - objtree, - } + StddefDmInfo { text, objtree } } } From 953cde5c65d5e45a7e846af94be6f469f97659b2 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Fri, 6 Nov 2020 02:32:17 +0000 Subject: [PATCH 03/30] setting breakpoints --- src/langserver/debugger/auxtools.rs | 40 +++-------- src/langserver/debugger/auxtools_types.rs | 8 ++- src/langserver/debugger/mod.rs | 83 +++++++++++++++++++++-- 3 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index e881cb07d..c69d3c690 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -1,8 +1,8 @@ use super::auxtools_types::*; -use std::sync::mpsc; +use std::{collections::HashSet, sync::mpsc}; use std::thread; use std::{ - collections::HashSet, + collections::HashMap, io::{Read, Write}, net::Ipv4Addr, net::SocketAddr, @@ -13,11 +13,12 @@ use std::{ use super::dap_types; use super::SequenceNumber; +use dm::FileId; pub struct Auxtools { requests: mpsc::Sender, responses: mpsc::Receiver, - breakpoints: HashSet, + breakpoints: HashMap>, _thread: JoinHandle<()>, } @@ -50,7 +51,7 @@ impl Auxtools { Ok(Auxtools { requests: requests_sender, responses: responses_receiver, - breakpoints: HashSet::new(), + breakpoints: HashMap::new(), _thread: thread.start_thread(), }) } @@ -112,24 +113,7 @@ impl Auxtools { } } - // Kinda lame: This function waits for a confirmation between set/unset - pub fn set_breakpoints(&mut self, new_breakpoints: &HashSet) { - let current_breakpoints = self.breakpoints.clone(); - - for ins in new_breakpoints { - if !current_breakpoints.contains(&ins) { - self.set_breakpoint(&ins); - } - } - - for ins in current_breakpoints { - if !new_breakpoints.contains(&ins) { - self.unset_breakpoint(&ins); - } - } - } - - fn set_breakpoint(&mut self, instruction: &InstructionRef) { + pub fn set_breakpoint(&mut self, instruction: &InstructionRef) -> BreakpointSetResult { self.requests .send(Request::BreakpointSet { instruction: instruction.clone(), @@ -140,10 +124,8 @@ impl Auxtools { match self.read_response() { Ok(response) => { match response { - Response::BreakpointSet { success } => { - if success { - self.breakpoints.insert(instruction.clone()); - } + Response::BreakpointSet { result } => { + return result; } // TODO: disconnect @@ -156,7 +138,7 @@ impl Auxtools { } } - fn unset_breakpoint(&mut self, instruction: &InstructionRef) { + pub fn unset_breakpoint(&mut self, instruction: &InstructionRef) { self.requests .send(Request::BreakpointUnset { instruction: instruction.clone(), @@ -169,9 +151,7 @@ impl Auxtools { Ok(response) => { match response { Response::BreakpointUnset { success } => { - if success { - self.breakpoints.remove(instruction); - } + } // TODO: disconnect diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index c7e3b42a9..79c0dc5a5 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -36,7 +36,7 @@ pub enum Request { #[derive(Serialize, Deserialize, Debug)] pub enum Response { BreakpointSet { - success: bool, + result: BreakpointSetResult, }, BreakpointUnset { success: bool, @@ -93,3 +93,9 @@ pub struct StackFrame { pub instruction: InstructionRef, pub line: Option, } + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BreakpointSetResult { + Success { line: Option }, + Failed, +} \ No newline at end of file diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index a705c3b1a..e21f3d6b8 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -527,10 +527,13 @@ handle_request! { } let inputs = params.breakpoints.unwrap_or_default(); - let mut breakpoints = Vec::new(); + let saved = self.saved_breakpoints.entry(file_id).or_default(); + let mut keep = HashSet::new(); match &mut self.client { DebugClient::Extools(extools) => { + let mut breakpoints = Vec::new(); + guard!(let Some(extools) = extools.as_ref() else { for sbp in inputs { breakpoints.push(Breakpoint { @@ -543,9 +546,6 @@ handle_request! { return Ok(SetBreakpointsResponse { breakpoints }); }); - let saved = self.saved_breakpoints.entry(file_id).or_default(); - let mut keep = HashSet::new(); - for sbp in inputs { if let Some((typepath, name, override_id)) = self.db.location_to_proc_ref(file_id, sbp.line) { // TODO: better discipline around format!("{}/{}") and so on @@ -598,7 +598,80 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't set breakpoints yet"))); + let mut breakpoints = vec![]; + + for sbp in inputs { + if let Some((typepath, name, override_id)) = self.db.location_to_proc_ref(file_id, sbp.line) { + // TODO: better discipline around format!("{}/{}") and so on + let proc = format!("{}/{}", typepath, name); + + if let Some(offset) = auxtools.get_offset(proc.as_str(), override_id as u32, sbp.line as u32) { + saved.insert((proc.clone(), override_id, sbp.line)); + keep.insert((proc.clone(), override_id, sbp.line)); + + let result = auxtools.set_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: proc, + override_id: override_id as u32 + }, + offset + }); + + breakpoints.push(match result { + auxtools_types::BreakpointSetResult::Success { line } => { + Breakpoint { + verified: true, + line: line.map(|x| x as i64), + .. Default::default() + } + }, + + auxtools_types::BreakpointSetResult::Failed => { + Breakpoint { + verified: false, + .. Default::default() + } + } + }); + } else { + // debug_output!(in self.seq, + // "Couldn't find line {} in the following disassembly:\n{}", + // sbp.line, + // Self::format_disassembly(extools.bytecode(&proc, override_id))); + + breakpoints.push(Breakpoint { + message: Some("Unable to determine offset in proc".to_owned()), + line: Some(sbp.line), + verified: false, + .. Default::default() + }); + } + } else { + breakpoints.push(Breakpoint { + message: Some("Unable to determine proc ref".to_owned()), + line: Some(sbp.line), + verified: false, + .. Default::default() + }); + } + } + + saved.retain(|k| { + if !keep.contains(&k) { + auxtools.unset_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: k.0.clone(), + override_id: k.1 as u32 + }, + offset: k.2 as u32 + }); + false + } else { + true + } + }); + + SetBreakpointsResponse { breakpoints } } } } From 10513e5466836a63645d33ea843ec8acaee98029 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Fri, 6 Nov 2020 19:21:53 +0000 Subject: [PATCH 04/30] breakpoint unsetting --- src/langserver/debugger/auxtools.rs | 5 +---- src/langserver/debugger/mod.rs | 16 +++++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index c69d3c690..20078f6df 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -145,14 +145,11 @@ impl Auxtools { }) .unwrap(); - // We ignore the result // TODO: disconnect match self.read_response() { Ok(response) => { match response { - Response::BreakpointUnset { success } => { - - } + Response::BreakpointUnset { success: _ } => {} // TODO: disconnect _ => panic!("received wrong response"), diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index e21f3d6b8..8c2bbed35 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -658,13 +658,15 @@ handle_request! { saved.retain(|k| { if !keep.contains(&k) { - auxtools.unset_breakpoint(&auxtools_types::InstructionRef { - proc: auxtools_types::ProcRef { - path: k.0.clone(), - override_id: k.1 as u32 - }, - offset: k.2 as u32 - }); + if let Some(offset) = auxtools.get_offset(k.0.as_str(), k.1 as u32, k.2 as u32) { + auxtools.unset_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: k.0.clone(), + override_id: k.1 as u32 + }, + offset: offset, + }); + } false } else { true From c2eaa56106c3be88daa52f3d1994b9e3b070d855 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sat, 7 Nov 2020 05:06:34 +0000 Subject: [PATCH 05/30] step-in --- src/langserver/debugger/auxtools.rs | 9 +++++++++ src/langserver/debugger/auxtools_types.rs | 4 ++-- src/langserver/debugger/mod.rs | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 20078f6df..83fb575f0 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -179,6 +179,15 @@ impl Auxtools { .unwrap(); } + pub fn step_into(&mut self) { + // TODO: disconnect + self.requests + .send(Request::Continue { + kind: ContinueKind::StepInto, + }) + .unwrap(); + } + pub fn pause(&mut self) { // TODO: disconnect self.requests.send(Request::Pause).unwrap(); diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 79c0dc5a5..59bb518df 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -84,7 +84,7 @@ pub enum BreakpointReason { pub enum ContinueKind { Continue, StepOver, - // StepInto, + StepInto, // StepOut, } @@ -98,4 +98,4 @@ pub struct StackFrame { pub enum BreakpointSetResult { Success { line: Option }, Failed, -} \ No newline at end of file +} diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 8c2bbed35..1a806a9f3 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -1098,7 +1098,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't step-in yet"))); + auxtools.step_into(); } } } From b3148f2c01e7deda3e3d9261655a2601e75ae1d2 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sat, 7 Nov 2020 05:39:56 +0000 Subject: [PATCH 06/30] . --- Cargo.toml | 2 +- src/langserver/debugger/mod.rs | 1 - src/langserver/main.rs | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd7a932c6..02b7b1edc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [profile.dev] -opt-level = 0 +opt-level = 2 [profile.release] lto = true diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 1a806a9f3..06411ac14 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -426,7 +426,6 @@ handle_request! { } on AttachVsc(&mut self, params) { - std::thread::sleep(std::time::Duration::from_secs(10)); self.client = DebugClient::Auxtools(Auxtools::new(self.seq.clone(), params.port)?); /*self.client = match params.client { DebugClientParam::Extools => { diff --git a/src/langserver/main.rs b/src/langserver/main.rs index 15b139e0d..493a9d338 100644 --- a/src/langserver/main.rs +++ b/src/langserver/main.rs @@ -47,6 +47,8 @@ use dm::annotation::{Annotation, AnnotationTree}; use dm::objtree::TypeRef; fn main() { + std::thread::sleep(std::time::Duration::from_secs(10)); + std::env::set_var("RUST_BACKTRACE", "1"); eprintln!( From 21f8786807a8ac6a272871310a46d5de29ddb4b6 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sat, 7 Nov 2020 05:48:52 +0000 Subject: [PATCH 07/30] step in --- src/langserver/debugger/auxtools.rs | 9 +++++++++ src/langserver/debugger/auxtools_types.rs | 2 +- src/langserver/debugger/mod.rs | 2 +- src/langserver/main.rs | 2 -- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 83fb575f0..0330feb09 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -188,6 +188,15 @@ impl Auxtools { .unwrap(); } + pub fn step_out(&mut self) { + // TODO: disconnect + self.requests + .send(Request::Continue { + kind: ContinueKind::StepOut, + }) + .unwrap(); + } + pub fn pause(&mut self) { // TODO: disconnect self.requests.send(Request::Pause).unwrap(); diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 59bb518df..571d48927 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -85,7 +85,7 @@ pub enum ContinueKind { Continue, StepOver, StepInto, - // StepOut, + StepOut, } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 06411ac14..a1937fa8e 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -1127,7 +1127,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't step-out yet"))); + auxtools.step_out(); } } } diff --git a/src/langserver/main.rs b/src/langserver/main.rs index 493a9d338..15b139e0d 100644 --- a/src/langserver/main.rs +++ b/src/langserver/main.rs @@ -47,8 +47,6 @@ use dm::annotation::{Annotation, AnnotationTree}; use dm::objtree::TypeRef; fn main() { - std::thread::sleep(std::time::Duration::from_secs(10)); - std::env::set_var("RUST_BACKTRACE", "1"); eprintln!( From 87a48117b9b0d05dd735d3bae786cd49fd6ca3ba Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sun, 8 Nov 2020 02:40:20 +0000 Subject: [PATCH 08/30] hacky globals --- Cargo.toml | 2 +- src/langserver/debugger/auxtools.rs | 90 ++++++++++++++++++++--- src/langserver/debugger/auxtools_types.rs | 34 ++++++++- src/langserver/debugger/mod.rs | 41 ++++++++++- 4 files changed, 150 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02b7b1edc..dd7a932c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [profile.dev] -opt-level = 2 +opt-level = 0 [profile.release] lto = true diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 0330feb09..d5f9b68f5 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -1,5 +1,5 @@ use super::auxtools_types::*; -use std::{collections::HashSet, sync::mpsc}; +use std::{sync::mpsc}; use std::thread; use std::{ collections::HashMap, @@ -15,6 +15,36 @@ use super::dap_types; use super::SequenceNumber; use dm::FileId; +impl VariablesRef { + pub fn encode(&self) -> i64 { + match self { + VariablesRef::Arguments { frame: _ } => { + 0 + } + + VariablesRef::Locals { frame: _ } => { + 0 + } + + VariablesRef::Internal { tag, data } => { + let tag = *tag as i64; + let data = *data as i64; + (tag << 24) + data + } + } + } + + pub fn decode(value: i64) -> Self { + let tag = (value & 0xFF000000) >> 24; + let data = value & 0x00FFFFFF; + + VariablesRef::Internal { + tag: tag as u8, + data: data as u32, + } + } +} + pub struct Auxtools { requests: mpsc::Sender, responses: mpsc::Receiver, @@ -212,7 +242,7 @@ impl Auxtools { thread_id, start_frame, count, - }); + }).unwrap(); match self.read_response() { Ok(response) => { @@ -230,8 +260,51 @@ impl Auxtools { // TODO: disconnect _ => panic!("timed out"), } + } + + // TODO: return all the scopes + pub fn get_scopes(&mut self, frame_id: u32) -> Option { + self.requests.send(Request::Scopes { + frame_id + }).unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::Scopes { + arguments: _, + locals: _, + globals, + } => return globals, - (vec![], 0) + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + + pub fn get_variables(&mut self, vars: VariablesRef) -> Vec { + self.requests.send(Request::Variables { + vars + }).unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::Variables { vars } => return vars, + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } } } @@ -287,13 +360,10 @@ impl AuxtoolsThread { } if got_data { - for message in queued_data.split(|x| *x == 0) { - // split can give us empty slices - if message.is_empty() { - continue; - } - - self.handle_message(message).unwrap(); + while let Some(pos) = queued_data.iter().position(|x| *x == 0) { + let mut message: Vec = queued_data.drain(0..=pos).collect(); + message.pop(); // remove null-terminator + self.handle_message(&message).unwrap(); } } diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 571d48927..471e15285 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; +#[allow(dead_code)] pub const DEFAULT_PORT: u16 = 2448; // Message from client -> server @@ -26,6 +27,12 @@ pub enum Request { start_frame: Option, count: Option, }, + Scopes { + frame_id: u32, + }, + Variables { + vars: VariablesRef, + }, Continue { kind: ContinueKind, }, @@ -51,10 +58,14 @@ pub enum Response { frames: Vec, total_count: u32, }, - - // Notifications (no `Request` counter-part) - // The server should send back a `Continue` request after getting this - // These should only be sent between Server/ServerThread on the notifications channel + Scopes { + arguments: Option, + locals: Option, + globals: Option, + }, + Variables { + vars: Vec, + }, BreakpointHit { reason: BreakpointReason, }, @@ -99,3 +110,18 @@ pub enum BreakpointSetResult { Success { line: Option }, Failed, } + +#[derive(Serialize, Deserialize, Debug)] +pub enum VariablesRef { + Arguments { frame: u16 }, + Locals { frame: u16 }, + Internal { tag: u8, data: u32 }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Variable { + pub name: String, + pub kind: String, + pub value: String, + pub variables: Option, +} diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index a1937fa8e..d1a9d60c9 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -922,7 +922,26 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't scopes yet"))); + match auxtools.get_scopes( frameId as u32 ) { + Some(globals) => { + ScopesResponse { + scopes: vec![ + Scope { + name: "Globals".to_owned(), + variablesReference: globals.encode(), + .. Default::default() + }, + ], + } + } + + None => { + ScopesResponse { + scopes: vec![], + } + } + } + } } } @@ -1063,7 +1082,25 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't variables yet"))); + let aux_variables = auxtools.get_variables(auxtools_types::VariablesRef::decode(params.variablesReference)); + let mut variables = vec![]; + + // TODO + // If VSC receives two Variables with the same name, it only + // displays the first one. Avert this by adding suffixes. + + for aux_var in aux_variables { + variables.push(Variable { + name: aux_var.name, + value: aux_var.value, + variablesReference: 0, + .. Default::default() + }); + } + + VariablesResponse { + variables + } } } } From f96e433860b013069c8e6a842ce22a5776897789 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sun, 8 Nov 2020 21:53:47 +0000 Subject: [PATCH 09/30] var reading --- src/langserver/debugger/auxtools.rs | 52 +++++++++++++++++------ src/langserver/debugger/auxtools_types.rs | 4 +- src/langserver/debugger/mod.rs | 48 ++++++++++++--------- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index d5f9b68f5..713750399 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -15,15 +15,23 @@ use super::dap_types; use super::SequenceNumber; use dm::FileId; +// We need to be able to encode/decode VariablesRef into an i64 for DAP to use +// but valid values are between 1 and 2^32-1. +// We make a few assumptions here: +// 1) BYOND doesn't use 0x7F or 0x7E as a tag for any data-types +// 2) The data portion of any BYOND value only needs 24-bits to be stored +// 3) Frame IDs only need 24-bits to be stored +// +// If this doesn't work out we'll just keep a map on the server of identifiers -> refs impl VariablesRef { pub fn encode(&self) -> i64 { match self { - VariablesRef::Arguments { frame: _ } => { - 0 + VariablesRef::Arguments { frame } => { + (0x7F << 24) + *frame as i64 } - VariablesRef::Locals { frame: _ } => { - 0 + VariablesRef::Locals { frame } => { + (0x7E << 24) + *frame as i64 } VariablesRef::Internal { tag, data } => { @@ -35,12 +43,28 @@ impl VariablesRef { } pub fn decode(value: i64) -> Self { - let tag = (value & 0xFF000000) >> 24; - let data = value & 0x00FFFFFF; + let tag = (((value as u32) & 0xFF000000) >> 24) as u8; + let data = (value as u32) & 0x00FFFFFF; - VariablesRef::Internal { - tag: tag as u8, - data: data as u32, + match tag { + 0x7F => { + VariablesRef::Arguments { + frame: data, + } + } + + 0x7E => { + VariablesRef::Locals { + frame: data, + } + } + + tag => { + VariablesRef::Internal { + tag, + data: data as u32, + } + } } } } @@ -263,7 +287,7 @@ impl Auxtools { } // TODO: return all the scopes - pub fn get_scopes(&mut self, frame_id: u32) -> Option { + pub fn get_scopes(&mut self, frame_id: u32) -> (Option, Option, Option) { self.requests.send(Request::Scopes { frame_id }).unwrap(); @@ -272,10 +296,12 @@ impl Auxtools { Ok(response) => { match response { Response::Scopes { - arguments: _, - locals: _, + arguments, + locals, globals, - } => return globals, + } => { + (arguments, locals, globals) + } // TODO: disconnect _ => panic!("received wrong response"), diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 471e15285..0589134b0 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -113,8 +113,8 @@ pub enum BreakpointSetResult { #[derive(Serialize, Deserialize, Debug)] pub enum VariablesRef { - Arguments { frame: u16 }, - Locals { frame: u16 }, + Arguments { frame: u32 }, + Locals { frame: u32 }, Internal { tag: u8, data: u32 }, } diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index d1a9d60c9..0611526f5 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -922,26 +922,36 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - match auxtools.get_scopes( frameId as u32 ) { - Some(globals) => { - ScopesResponse { - scopes: vec![ - Scope { - name: "Globals".to_owned(), - variablesReference: globals.encode(), - .. Default::default() - }, - ], - } - } + let (arguments, locals, globals) = auxtools.get_scopes( frameId as u32 ); + let mut scopes = vec![]; - None => { - ScopesResponse { - scopes: vec![], - } - } + if let Some(arguments) = arguments { + scopes.push(Scope { + name: "Arguments".to_owned(), + variablesReference: arguments.encode(), + .. Default::default() + }); + } + + if let Some(locals) = locals { + scopes.push(Scope { + name: "Locals".to_owned(), + variablesReference: locals.encode(), + .. Default::default() + }); + } + + if let Some(globals) = globals { + scopes.push(Scope { + name: "Globals".to_owned(), + variablesReference: globals.encode(), + .. Default::default() + }); + } + + ScopesResponse { + scopes } - } } } @@ -1093,7 +1103,7 @@ handle_request! { variables.push(Variable { name: aux_var.name, value: aux_var.value, - variablesReference: 0, + variablesReference: aux_var.variables.map(|x| x.encode()).unwrap_or(0), .. Default::default() }); } From b22276e609dc06d815f438d26039a1338e6176ef Mon Sep 17 00:00:00 2001 From: William Wallace Date: Tue, 10 Nov 2020 18:02:57 +0000 Subject: [PATCH 10/30] use stack frame id --- Cargo.toml | 2 +- src/langserver/debugger/auxtools_types.rs | 1 + src/langserver/debugger/mod.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd7a932c6..02b7b1edc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [profile.dev] -opt-level = 0 +opt-level = 2 [profile.release] lto = true diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 0589134b0..5f6fb08ea 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -101,6 +101,7 @@ pub enum ContinueKind { #[derive(Serialize, Deserialize, Debug)] pub struct StackFrame { + pub id: u32, pub instruction: InstructionRef, pub line: Option, } diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 0611526f5..8a1de5f36 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -825,7 +825,7 @@ handle_request! { let aux_proc = &aux_frame.instruction.proc; let mut dap_frame = StackFrame { name: aux_proc.path.to_owned(), - id: params.threadId, // TODO: multiple threads + id: aux_frame.id as i64, // TODO: multiple threads instructionPointerReference: Some(format!("{}@{}#{}", aux_proc.path, aux_proc.override_id, aux_frame.instruction.offset)), .. Default::default() }; From d2a0edf3c6dc7621669f7639f978b52661012df5 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 12 Nov 2020 20:17:40 +0000 Subject: [PATCH 11/30] auxtools exception catching --- src/langserver/debugger/auxtools.rs | 24 ++++++++++++++++++----- src/langserver/debugger/auxtools_types.rs | 1 + src/langserver/debugger/mod.rs | 7 ++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 713750399..05f039d4f 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -2,18 +2,16 @@ use super::auxtools_types::*; use std::{sync::mpsc}; use std::thread; use std::{ - collections::HashMap, io::{Read, Write}, net::Ipv4Addr, net::SocketAddr, net::TcpStream, - sync::Arc, + sync::{Arc, RwLock}, thread::JoinHandle, }; use super::dap_types; use super::SequenceNumber; -use dm::FileId; // We need to be able to encode/decode VariablesRef into an i64 for DAP to use // but valid values are between 1 and 2^32-1. @@ -72,8 +70,8 @@ impl VariablesRef { pub struct Auxtools { requests: mpsc::Sender, responses: mpsc::Receiver, - breakpoints: HashMap>, _thread: JoinHandle<()>, + last_error: Arc>, } pub struct AuxtoolsThread { @@ -81,6 +79,7 @@ pub struct AuxtoolsThread { requests: mpsc::Receiver, responses: mpsc::Sender, stream: TcpStream, + last_error: Arc>, } impl Auxtools { @@ -95,18 +94,21 @@ impl Auxtools { // Look at this little trouble-maker right here seq.issue_event(dap_types::InitializedEvent); + let last_error = Arc::new(RwLock::new("".to_owned())); + let thread = AuxtoolsThread { seq, requests: requests_receiver, responses: responses_sender, stream, + last_error: last_error.clone(), }; Ok(Auxtools { requests: requests_sender, responses: responses_receiver, - breakpoints: HashMap::new(), _thread: thread.start_thread(), + last_error, }) } @@ -332,6 +334,10 @@ impl Auxtools { _ => panic!("timed out"), } } + + pub fn get_last_error_message(&self) -> String { + self.last_error.read().unwrap().clone() + } } impl AuxtoolsThread { @@ -346,15 +352,23 @@ impl AuxtoolsThread { match response { Response::BreakpointHit { reason } => { + let mut description = None; + let reason = match reason { BreakpointReason::Step => dap_types::StoppedEvent::REASON_STEP, BreakpointReason::Pause => dap_types::StoppedEvent::REASON_PAUSE, BreakpointReason::Breakpoint => dap_types::StoppedEvent::REASON_BREAKPOINT, + BreakpointReason::Runtime(error) => { + *(self.last_error.write().unwrap()) = error.clone(); + description = Some(error); + dap_types::StoppedEvent::REASON_EXCEPTION + } }; self.seq.issue_event(dap_types::StoppedEvent { threadId: Some(0), reason: reason.to_owned(), + description, ..Default::default() }); } diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 5f6fb08ea..947e570cf 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -89,6 +89,7 @@ pub enum BreakpointReason { Breakpoint, Step, Pause, + Runtime(String), } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 8a1de5f36..491442cab 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -1220,7 +1220,12 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't exception-info yet"))); + ExceptionInfoResponse { + exceptionId: auxtools.get_last_error_message(), + description: None, + breakMode: ExceptionBreakMode::Always, + details: None, + } } } } From e2b5c8c06e601d491e13c122552f45bf076eac4e Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sun, 22 Nov 2020 02:00:32 +0000 Subject: [PATCH 12/30] enabling/disabling runtime breakpoint works --- src/langserver/debugger/auxtools.rs | 4 ++++ src/langserver/debugger/auxtools_types.rs | 1 + src/langserver/debugger/mod.rs | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 05f039d4f..67a505350 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -338,6 +338,10 @@ impl Auxtools { pub fn get_last_error_message(&self) -> String { self.last_error.read().unwrap().clone() } + + pub fn set_catch_runtimes(&self, should_catch: bool) { + self.requests.send(Request::SetCatchRuntimes(should_catch)).unwrap(); + } } impl AuxtoolsThread { diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 947e570cf..57e0a50a6 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -14,6 +14,7 @@ pub enum Request { BreakpointUnset { instruction: InstructionRef, }, + SetCatchRuntimes(bool), LineNumber { proc: ProcRef, offset: u32, diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 491442cab..25aea40a1 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -1200,7 +1200,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't set-exception-breakpoints yet"))); + auxtools.set_catch_runtimes(params.filters.iter().any(|x| x == EXCEPTION_FILTER_RUNTIMES)); } } } From 7fbd044b68b89329f7b8e1fc2ad81a28dd57d21d Mon Sep 17 00:00:00 2001 From: William Wallace Date: Sun, 22 Nov 2020 19:28:49 +0000 Subject: [PATCH 13/30] disconnect messages --- src/langserver/debugger/auxtools.rs | 21 +++++++++++++++++---- src/langserver/debugger/auxtools_types.rs | 2 ++ src/langserver/debugger/mod.rs | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 67a505350..817707fc2 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -117,6 +117,10 @@ impl Auxtools { .recv_timeout(std::time::Duration::from_secs(5)) } + pub fn disconnect(&mut self) { + self.requests.send(Request::Disconnect).unwrap(); + } + pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { self.requests .send(Request::LineNumber { @@ -351,10 +355,13 @@ impl AuxtoolsThread { self.stream.write_all(&message[..]).unwrap(); } - fn handle_message(&mut self, data: &[u8]) -> Result<(), Box> { + // returns true if we should disconnect + fn handle_message(&mut self, data: &[u8]) -> Result> { let response = serde_json::from_slice::(data)?; match response { + Response::Disconnect => return Ok(true), + Response::BreakpointHit { reason } => { let mut description = None; @@ -376,20 +383,22 @@ impl AuxtoolsThread { ..Default::default() }); } + x => { self.responses.send(x)?; } } - Ok(()) + Ok(false) } + // TODO: rewrite to not be a non-blocking socket pub fn start_thread(mut self) -> JoinHandle<()> { thread::spawn(move || { let mut buf = [0u8; 4096]; let mut queued_data = vec![]; - loop { + 'outer: loop { let mut got_data = false; match self.stream.read(&mut buf) { Ok(0) => (), @@ -407,7 +416,9 @@ impl AuxtoolsThread { while let Some(pos) = queued_data.iter().position(|x| *x == 0) { let mut message: Vec = queued_data.drain(0..=pos).collect(); message.pop(); // remove null-terminator - self.handle_message(&message).unwrap(); + if self.handle_message(&message).unwrap() { + break 'outer; + } } } @@ -421,6 +432,8 @@ impl AuxtoolsThread { self.send(request); } } + + self.seq.issue_event(dap_types::TerminatedEvent::default()); }) } } diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 57e0a50a6..3903b5dd4 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -8,6 +8,7 @@ pub const DEFAULT_PORT: u16 = 2448; // Message from client -> server #[derive(Serialize, Deserialize, Debug)] pub enum Request { + Disconnect, BreakpointSet { instruction: InstructionRef, }, @@ -43,6 +44,7 @@ pub enum Request { // Message from server -> client #[derive(Serialize, Deserialize, Debug)] pub enum Response { + Disconnect, BreakpointSet { result: BreakpointSetResult, }, diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 25aea40a1..37b1d1a4c 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -448,7 +448,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - //auxtools.disconnect(); + auxtools.disconnect(); } } From 8d5526d5bd7f4801927645d2bb64188b26e1c4d8 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Mon, 23 Nov 2020 03:17:46 +0000 Subject: [PATCH 14/30] threads --- src/langserver/debugger/auxtools.rs | 40 ++++++++++++++++++----- src/langserver/debugger/auxtools_types.rs | 18 +++++++--- src/langserver/debugger/mod.rs | 39 ++++++++++++++++++---- 3 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 817707fc2..2cbeff4db 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -230,29 +230,29 @@ impl Auxtools { .unwrap(); } - pub fn next(&mut self) { + pub fn next(&mut self, stack_id: u32) { // TODO: disconnect self.requests .send(Request::Continue { - kind: ContinueKind::StepOver, + kind: ContinueKind::StepOver { stack_id }, }) .unwrap(); } - pub fn step_into(&mut self) { + pub fn step_into(&mut self, stack_id: u32) { // TODO: disconnect self.requests .send(Request::Continue { - kind: ContinueKind::StepInto, + kind: ContinueKind::StepInto { stack_id }, }) .unwrap(); } - pub fn step_out(&mut self) { + pub fn step_out(&mut self, stack_id: u32) { // TODO: disconnect self.requests .send(Request::Continue { - kind: ContinueKind::StepOut, + kind: ContinueKind::StepOut { stack_id }, }) .unwrap(); } @@ -262,14 +262,32 @@ impl Auxtools { self.requests.send(Request::Pause).unwrap(); } + pub fn get_stacks(&mut self) -> Vec { + self.requests.send(Request::Stacks).unwrap(); + + match self.read_response() { + Ok(response) => { + match response { + Response::Stacks { stacks } => return stacks, + + // TODO: disconnect + _ => panic!("received wrong response"), + } + } + + // TODO: disconnect + _ => panic!("timed out"), + } + } + pub fn get_stack_frames( &mut self, - thread_id: u32, + stack_id: u32, start_frame: Option, count: Option, ) -> (Vec, u32) { self.requests.send(Request::StackFrames { - thread_id, + stack_id, start_frame, count, }).unwrap(); @@ -380,6 +398,7 @@ impl AuxtoolsThread { threadId: Some(0), reason: reason.to_owned(), description, + allThreadsStopped: Some(true), ..Default::default() }); } @@ -409,7 +428,10 @@ impl AuxtoolsThread { // This is a crutch Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {} - Err(_) => panic!("Handle me!"), + Err(e) => { + println!("{:?}", e); + panic!("Handle me!"); + } } if got_data { diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 3903b5dd4..e363179b3 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -24,8 +24,9 @@ pub enum Request { proc: ProcRef, line: u32, }, + Stacks, StackFrames { - thread_id: u32, + stack_id: u32, start_frame: Option, count: Option, }, @@ -57,6 +58,9 @@ pub enum Response { Offset { offset: Option, }, + Stacks { + stacks: Vec, + }, StackFrames { frames: Vec, total_count: u32, @@ -98,9 +102,15 @@ pub enum BreakpointReason { #[derive(Serialize, Deserialize, Debug)] pub enum ContinueKind { Continue, - StepOver, - StepInto, - StepOut, + StepOver { stack_id: u32 }, + StepInto { stack_id: u32 }, + StepOut { stack_id: u32 }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Stack { + pub id: u32, + pub name: String, } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 37b1d1a4c..1dce14316 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -352,7 +352,15 @@ impl Debugger { } } - DebugClient::Auxtools(_) => {} + DebugClient::Auxtools(auxtools) => for stack in auxtools.get_stacks() { + if stack.id == 0 { + continue; + } + self.issue_event(dap_types::ThreadEvent { + reason: dap_types::ThreadEvent::REASON_EXITED.to_owned(), + threadId: stack.id as i64, + }); + }, } } } @@ -506,8 +514,25 @@ handle_request! { }, DebugClient::Auxtools(auxtools) => { + let mut threads : Vec = auxtools.get_stacks().into_iter().map(|x| { + Thread { + id: x.id as i64, + name: x.name, + } + }).collect(); + + // If we tell DAP that there are no threads, Pause requests never get passed through! + if threads.is_empty() { + threads.push( + Thread { + id: 0, + name: "Main".to_owned(), + } + ); + } + ThreadsResponse { - threads: vec![Thread { id: 0, name: "Main".to_owned() }], + threads, } } } @@ -825,7 +850,7 @@ handle_request! { let aux_proc = &aux_frame.instruction.proc; let mut dap_frame = StackFrame { name: aux_proc.path.to_owned(), - id: aux_frame.id as i64, // TODO: multiple threads + id: aux_frame.id as i64, instructionPointerReference: Some(format!("{}@{}#{}", aux_proc.path, aux_proc.override_id, aux_frame.instruction.offset)), .. Default::default() }; @@ -1116,7 +1141,7 @@ handle_request! { } on Continue(&mut self, _params) { - self.cull_thread_list(); + self.notify_continue(); match &mut self.client { DebugClient::Extools(extools) => { @@ -1144,7 +1169,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.step_into(); + auxtools.step_into(params.threadId as u32); } } } @@ -1159,7 +1184,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.next(); + auxtools.next(params.threadId as u32); } } } @@ -1174,7 +1199,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.step_out(); + auxtools.step_out(params.threadId as u32); } } } From 9d46ee261f1912ec68ef3f8f80533169b1dc52ad Mon Sep 17 00:00:00 2001 From: William Wallace Date: Mon, 23 Nov 2020 04:38:57 +0000 Subject: [PATCH 15/30] debug console notifications --- src/langserver/debugger/auxtools.rs | 4 ++++ src/langserver/debugger/auxtools_types.rs | 8 +++++++- src/langserver/debugger/mod.rs | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 2cbeff4db..0821c33bc 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -380,6 +380,10 @@ impl AuxtoolsThread { match response { Response::Disconnect => return Ok(true), + Response::Notification { message } => { + debug_output!(in self.seq, "[auxtools] {}", message); + } + Response::BreakpointHit { reason } => { let mut description = None; diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index e363179b3..1e91cb233 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -45,7 +45,6 @@ pub enum Request { // Message from server -> client #[derive(Serialize, Deserialize, Debug)] pub enum Response { - Disconnect, BreakpointSet { result: BreakpointSetResult, }, @@ -73,6 +72,13 @@ pub enum Response { Variables { vars: Vec, }, + + // These responses can occur at any moment, even between a request and its response + // I guess they aren't really responses... + Disconnect, + Notification { + message: String, + }, BreakpointHit { reason: BreakpointReason, }, diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 1dce14316..482dff001 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -768,7 +768,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - return Err(Box::new(GenericError("auxtools can't set breakpoints yet"))); + return Err(Box::new(GenericError("auxtools can't set function breakpoints yet"))); } } } From bca1e282645a61352754247fe653131b9f916fc4 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Mon, 23 Nov 2020 05:01:40 +0000 Subject: [PATCH 16/30] warnings --- src/langserver/debugger/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 482dff001..923bb7500 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -480,9 +480,7 @@ handle_request! { extools.configuration_done(); } - DebugClient::Auxtools(auxtools) => { - - } + DebugClient::Auxtools(_) => {} } } @@ -767,7 +765,7 @@ handle_request! { SetFunctionBreakpointsResponse { breakpoints } } - DebugClient::Auxtools(auxtools) => { + DebugClient::Auxtools(_) => { return Err(Box::new(GenericError("auxtools can't set function breakpoints yet"))); } } @@ -1304,7 +1302,7 @@ handle_request! { } } - DebugClient::Auxtools(auxtools) => { + DebugClient::Auxtools(_) => { return Err(Box::new(GenericError("auxtools can't disassemble yet"))); } } From f32291f068836e4942e156cfd2ddfad60e1a4b20 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Mon, 23 Nov 2020 19:57:21 +0000 Subject: [PATCH 17/30] move variables refs handling to auxtools --- src/langserver/debugger/auxtools.rs | 54 ----------------------- src/langserver/debugger/auxtools_types.rs | 8 +--- src/langserver/debugger/mod.rs | 10 ++--- 3 files changed, 7 insertions(+), 65 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 0821c33bc..a9afdf335 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -13,60 +13,6 @@ use std::{ use super::dap_types; use super::SequenceNumber; -// We need to be able to encode/decode VariablesRef into an i64 for DAP to use -// but valid values are between 1 and 2^32-1. -// We make a few assumptions here: -// 1) BYOND doesn't use 0x7F or 0x7E as a tag for any data-types -// 2) The data portion of any BYOND value only needs 24-bits to be stored -// 3) Frame IDs only need 24-bits to be stored -// -// If this doesn't work out we'll just keep a map on the server of identifiers -> refs -impl VariablesRef { - pub fn encode(&self) -> i64 { - match self { - VariablesRef::Arguments { frame } => { - (0x7F << 24) + *frame as i64 - } - - VariablesRef::Locals { frame } => { - (0x7E << 24) + *frame as i64 - } - - VariablesRef::Internal { tag, data } => { - let tag = *tag as i64; - let data = *data as i64; - (tag << 24) + data - } - } - } - - pub fn decode(value: i64) -> Self { - let tag = (((value as u32) & 0xFF000000) >> 24) as u8; - let data = (value as u32) & 0x00FFFFFF; - - match tag { - 0x7F => { - VariablesRef::Arguments { - frame: data, - } - } - - 0x7E => { - VariablesRef::Locals { - frame: data, - } - } - - tag => { - VariablesRef::Internal { - tag, - data: data as u32, - } - } - } - } -} - pub struct Auxtools { requests: mpsc::Sender, responses: mpsc::Receiver, diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 1e91cb233..1b672abfb 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -132,12 +132,8 @@ pub enum BreakpointSetResult { Failed, } -#[derive(Serialize, Deserialize, Debug)] -pub enum VariablesRef { - Arguments { frame: u32 }, - Locals { frame: u32 }, - Internal { tag: u8, data: u32 }, -} +#[derive(Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Debug)] +pub struct VariablesRef(pub i32); #[derive(Serialize, Deserialize, Debug)] pub struct Variable { diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 923bb7500..0f853ee6b 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -951,7 +951,7 @@ handle_request! { if let Some(arguments) = arguments { scopes.push(Scope { name: "Arguments".to_owned(), - variablesReference: arguments.encode(), + variablesReference: arguments.0 as i64, .. Default::default() }); } @@ -959,7 +959,7 @@ handle_request! { if let Some(locals) = locals { scopes.push(Scope { name: "Locals".to_owned(), - variablesReference: locals.encode(), + variablesReference: locals.0 as i64, .. Default::default() }); } @@ -967,7 +967,7 @@ handle_request! { if let Some(globals) = globals { scopes.push(Scope { name: "Globals".to_owned(), - variablesReference: globals.encode(), + variablesReference: globals.0 as i64, .. Default::default() }); } @@ -1115,7 +1115,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - let aux_variables = auxtools.get_variables(auxtools_types::VariablesRef::decode(params.variablesReference)); + let aux_variables = auxtools.get_variables(auxtools_types::VariablesRef(params.variablesReference as i32)); let mut variables = vec![]; // TODO @@ -1126,7 +1126,7 @@ handle_request! { variables.push(Variable { name: aux_var.name, value: aux_var.value, - variablesReference: aux_var.variables.map(|x| x.encode()).unwrap_or(0), + variablesReference: aux_var.variables.map(|x| x.0 as i64).unwrap_or(0), .. Default::default() }); } From fdf0d33c60725e31916fd6301c4a004837716c9b Mon Sep 17 00:00:00 2001 From: William Wallace Date: Wed, 25 Nov 2020 19:36:45 +0000 Subject: [PATCH 18/30] auxtools update --- Cargo.lock | 11 ++ src/langserver/Cargo.toml | 1 + src/langserver/debugger/auxtools.rs | 128 ++++++++++------------ src/langserver/debugger/auxtools_types.rs | 6 +- 4 files changed, 73 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f81541964..b3493287c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "bincode" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +dependencies = [ + "byteorder", + "serde", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -406,6 +416,7 @@ dependencies = [ name = "dm-langserver" version = "1.4.0" dependencies = [ + "bincode", "chrono", "dreamchecker", "dreammaker", diff --git a/src/langserver/Cargo.toml b/src/langserver/Cargo.toml index 10858b752..ccc19f2a8 100644 --- a/src/langserver/Cargo.toml +++ b/src/langserver/Cargo.toml @@ -13,6 +13,7 @@ url = "2.1.0" serde = "1.0.27" serde_json = "1.0.10" serde_derive = "1.0.27" +bincode = "1.3.1" jsonrpc-core = "14.0.3" lsp-types = "0.80.0" dreammaker = { path = "../dreammaker" } diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index a9afdf335..3f200011b 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -14,15 +14,14 @@ use super::dap_types; use super::SequenceNumber; pub struct Auxtools { - requests: mpsc::Sender, responses: mpsc::Receiver, _thread: JoinHandle<()>, + stream: TcpStream, last_error: Arc>, } pub struct AuxtoolsThread { seq: Arc, - requests: mpsc::Receiver, responses: mpsc::Sender, stream: TcpStream, last_error: Arc>, @@ -31,11 +30,9 @@ pub struct AuxtoolsThread { impl Auxtools { pub fn new(seq: Arc, port: Option) -> std::io::Result { let addr: SocketAddr = (Ipv4Addr::LOCALHOST, port.unwrap_or(DEFAULT_PORT)).into(); - let (requests_sender, requests_receiver) = mpsc::channel(); let (responses_sender, responses_receiver) = mpsc::channel(); let stream = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))?; - stream.set_nonblocking(true)?; // Look at this little trouble-maker right here seq.issue_event(dap_types::InitializedEvent); @@ -44,16 +41,15 @@ impl Auxtools { let thread = AuxtoolsThread { seq, - requests: requests_receiver, responses: responses_sender, - stream, + stream: stream.try_clone().unwrap(), last_error: last_error.clone(), }; Ok(Auxtools { - requests: requests_sender, responses: responses_receiver, _thread: thread.start_thread(), + stream, last_error, }) } @@ -63,13 +59,20 @@ impl Auxtools { .recv_timeout(std::time::Duration::from_secs(5)) } + fn send(&mut self, request: Request) -> Result<(), ()> { + let data = bincode::serialize(&request).unwrap(); + self.stream.write_all(&(data.len() as u32).to_le_bytes()).unwrap(); + self.stream.write_all(&data[..]).unwrap(); + self.stream.flush().unwrap(); + Ok(()) + } + pub fn disconnect(&mut self) { - self.requests.send(Request::Disconnect).unwrap(); + self.send(Request::Disconnect).unwrap(); } pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { - self.requests - .send(Request::LineNumber { + self.send(Request::LineNumber { proc: ProcRef { path: path.to_owned(), override_id, @@ -94,8 +97,7 @@ impl Auxtools { } pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Option { - self.requests - .send(Request::Offset { + self.send(Request::Offset { proc: ProcRef { path: path.to_owned(), override_id, @@ -120,8 +122,7 @@ impl Auxtools { } pub fn set_breakpoint(&mut self, instruction: &InstructionRef) -> BreakpointSetResult { - self.requests - .send(Request::BreakpointSet { + self.send(Request::BreakpointSet { instruction: instruction.clone(), }) .unwrap(); @@ -145,8 +146,7 @@ impl Auxtools { } pub fn unset_breakpoint(&mut self, instruction: &InstructionRef) { - self.requests - .send(Request::BreakpointUnset { + self.send(Request::BreakpointUnset { instruction: instruction.clone(), }) .unwrap(); @@ -169,8 +169,7 @@ impl Auxtools { pub fn continue_execution(&mut self) { // TODO: disconnect - self.requests - .send(Request::Continue { + self.send(Request::Continue { kind: ContinueKind::Continue, }) .unwrap(); @@ -178,8 +177,7 @@ impl Auxtools { pub fn next(&mut self, stack_id: u32) { // TODO: disconnect - self.requests - .send(Request::Continue { + self.send(Request::Continue { kind: ContinueKind::StepOver { stack_id }, }) .unwrap(); @@ -187,8 +185,7 @@ impl Auxtools { pub fn step_into(&mut self, stack_id: u32) { // TODO: disconnect - self.requests - .send(Request::Continue { + self.send(Request::Continue { kind: ContinueKind::StepInto { stack_id }, }) .unwrap(); @@ -196,8 +193,7 @@ impl Auxtools { pub fn step_out(&mut self, stack_id: u32) { // TODO: disconnect - self.requests - .send(Request::Continue { + self.send(Request::Continue { kind: ContinueKind::StepOut { stack_id }, }) .unwrap(); @@ -205,11 +201,11 @@ impl Auxtools { pub fn pause(&mut self) { // TODO: disconnect - self.requests.send(Request::Pause).unwrap(); + self.send(Request::Pause).unwrap(); } pub fn get_stacks(&mut self) -> Vec { - self.requests.send(Request::Stacks).unwrap(); + self.send(Request::Stacks).unwrap(); match self.read_response() { Ok(response) => { @@ -232,7 +228,7 @@ impl Auxtools { start_frame: Option, count: Option, ) -> (Vec, u32) { - self.requests.send(Request::StackFrames { + self.send(Request::StackFrames { stack_id, start_frame, count, @@ -258,7 +254,7 @@ impl Auxtools { // TODO: return all the scopes pub fn get_scopes(&mut self, frame_id: u32) -> (Option, Option, Option) { - self.requests.send(Request::Scopes { + self.send(Request::Scopes { frame_id }).unwrap(); @@ -284,7 +280,7 @@ impl Auxtools { } pub fn get_variables(&mut self, vars: VariablesRef) -> Vec { - self.requests.send(Request::Variables { + self.send(Request::Variables { vars }).unwrap(); @@ -307,21 +303,15 @@ impl Auxtools { self.last_error.read().unwrap().clone() } - pub fn set_catch_runtimes(&self, should_catch: bool) { - self.requests.send(Request::SetCatchRuntimes(should_catch)).unwrap(); + pub fn set_catch_runtimes(&mut self, should_catch: bool) { + self.send(Request::CatchRuntimes { should_catch }).unwrap(); } } impl AuxtoolsThread { - fn send(&mut self, request: Request) { - let mut message = serde_json::to_vec(&request).unwrap(); - message.push(0); // null-terminator - self.stream.write_all(&message[..]).unwrap(); - } - // returns true if we should disconnect - fn handle_message(&mut self, data: &[u8]) -> Result> { - let response = serde_json::from_slice::(data)?; + fn handle_response(&mut self, data: &[u8]) -> Result> { + let response = bincode::deserialize::(data)?; match response { Response::Disconnect => return Ok(true), @@ -364,44 +354,42 @@ impl AuxtoolsThread { // TODO: rewrite to not be a non-blocking socket pub fn start_thread(mut self) -> JoinHandle<()> { thread::spawn(move || { - let mut buf = [0u8; 4096]; - let mut queued_data = vec![]; - - 'outer: loop { - let mut got_data = false; - match self.stream.read(&mut buf) { - Ok(0) => (), - Ok(n) => { - queued_data.extend_from_slice(&buf[..n]); - got_data = true; + let mut buf = vec![]; + + // The incoming stream is a u32 followed by a bincode-encoded Request. + loop { + let mut len_bytes = [0u8; 4]; + let len = match self.stream.read_exact(&mut len_bytes) { + Ok(_) => u32::from_le_bytes(len_bytes), + + Err(e) => { + eprintln!("Debug server thread read error: {}", e); + break; } + }; - // This is a crutch - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {} + buf.resize(len as usize, 0); + match self.stream.read_exact(&mut buf) { + Ok(_) => (), + Err(e) => { - println!("{:?}", e); - panic!("Handle me!"); + eprintln!("Debug server thread read error: {}", e); + break; } - } + }; - if got_data { - while let Some(pos) = queued_data.iter().position(|x| *x == 0) { - let mut message: Vec = queued_data.drain(0..=pos).collect(); - message.pop(); // remove null-terminator - if self.handle_message(&message).unwrap() { - break 'outer; + match self.handle_response(&buf[..]) { + Ok(requested_disconnect) => { + if requested_disconnect { + eprintln!("Debug server disconnected"); + break; } } - } - - // Clear any finished messages from the buffer - if let Some(idx) = queued_data.iter().rposition(|x| *x == 0) { - queued_data.drain(..idx); - } - - // Send any requests to the server - while let Ok(request) = self.requests.try_recv() { - self.send(request); + + Err(e) => { + eprintln!("Debug server thread failed to handle request: {}", e); + break; + } } } diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index 1b672abfb..d3e6e16e8 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -15,7 +15,9 @@ pub enum Request { BreakpointUnset { instruction: InstructionRef, }, - SetCatchRuntimes(bool), + CatchRuntimes { + should_catch: bool, + }, LineNumber { proc: ProcRef, offset: u32, @@ -87,7 +89,6 @@ pub enum Response { #[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)] pub struct ProcRef { pub path: String, - // TODO: this is 0 in some places pub override_id: u32, } @@ -138,7 +139,6 @@ pub struct VariablesRef(pub i32); #[derive(Serialize, Deserialize, Debug)] pub struct Variable { pub name: String, - pub kind: String, pub value: String, pub variables: Option, } From 914006e0882e9ece7034c36c0dbe03ddb6de1e4b Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 00:10:15 +0000 Subject: [PATCH 19/30] error handling --- src/langserver/debugger/auxtools.rs | 454 +++++++++++----------- src/langserver/debugger/auxtools_types.rs | 1 + src/langserver/debugger/mod.rs | 32 +- 3 files changed, 235 insertions(+), 252 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 3f200011b..45fb298dd 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -13,212 +13,226 @@ use std::{ use super::dap_types; use super::SequenceNumber; +enum StreamState { + // The client is waiting for a Stream to be sent from the thread + Waiting(mpsc::Receiver), + + Connected(TcpStream), + + // The server has finished being used + Disconnected, +} + pub struct Auxtools { + seq: Arc, responses: mpsc::Receiver, _thread: JoinHandle<()>, - stream: TcpStream, + stream: StreamState, last_error: Arc>, } pub struct AuxtoolsThread { seq: Arc, responses: mpsc::Sender, - stream: TcpStream, last_error: Arc>, } impl Auxtools { - pub fn new(seq: Arc, port: Option) -> std::io::Result { + pub fn connect(seq: Arc, port: Option) -> std::io::Result { let addr: SocketAddr = (Ipv4Addr::LOCALHOST, port.unwrap_or(DEFAULT_PORT)).into(); let (responses_sender, responses_receiver) = mpsc::channel(); - + let last_error = Arc::new(RwLock::new("".to_owned())); let stream = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))?; // Look at this little trouble-maker right here seq.issue_event(dap_types::InitializedEvent); - let last_error = Arc::new(RwLock::new("".to_owned())); - - let thread = AuxtoolsThread { - seq, - responses: responses_sender, - stream: stream.try_clone().unwrap(), - last_error: last_error.clone(), + let thread = { + let seq = seq.clone(); + let last_error = last_error.clone(); + let stream = stream.try_clone().unwrap(); + thread::spawn(move || { + AuxtoolsThread { + seq, + responses: responses_sender, + last_error: last_error, + }.run(stream); + }) }; Ok(Auxtools { + seq, responses: responses_receiver, - _thread: thread.start_thread(), - stream, + _thread: thread, + stream: StreamState::Connected(stream), last_error, }) } - fn read_response(&mut self) -> Result { - self.responses - .recv_timeout(std::time::Duration::from_secs(5)) - } - - fn send(&mut self, request: Request) -> Result<(), ()> { - let data = bincode::serialize(&request).unwrap(); - self.stream.write_all(&(data.len() as u32).to_le_bytes()).unwrap(); - self.stream.write_all(&data[..]).unwrap(); - self.stream.flush().unwrap(); - Ok(()) - } - - pub fn disconnect(&mut self) { - self.send(Request::Disconnect).unwrap(); + fn read_response_or_disconnect(&mut self) -> Result> { + match self.responses.recv_timeout(std::time::Duration::from_secs(5)) { + Ok(response) => Ok(response), + Err(_) => { + self.disconnect(); + Err(Box::new(super::GenericError("timed out waiting for response"))) + } + } } - pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Option { - self.send(Request::LineNumber { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - offset, - }) - .unwrap(); + fn send(&mut self, request: Request) -> Result<(), Box> { + if let StreamState::Waiting(recv) = &self.stream { + if let Ok(stream) = recv.try_recv() { + self.stream = StreamState::Connected(stream); + } + } - match self.read_response() { - Ok(response) => { - match response { - Response::LineNumber { line } => line, + match &mut self.stream { + StreamState::Connected(stream) => { + let data = bincode::serialize(&request)?; + stream.write_all(&(data.len() as u32).to_le_bytes())?; + stream.write_all(&data[..])?; + stream.flush()?; + Ok(()) + } - // TODO: disconnect - _ => panic!("received wrong response"), - } + _ => { + // Success if not connected (kinda dumb) + Ok(()) } + } + } - // TODO: disconnect - _ => panic!("timed out"), + fn send_or_disconnect(&mut self, request: Request) -> Result<(), Box> { + if let Err(e) = self.send(request) { + self.disconnect(); + return Err(e); } + + Ok(()) } - pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Option { - self.send(Request::Offset { - proc: ProcRef { - path: path.to_owned(), - override_id, - }, - line, - }) - .unwrap(); + pub fn disconnect(&mut self) { + debug_output!(in self.seq, "[auxtools] disconnecting"); + let _ = self.send(Request::Disconnect); - match self.read_response() { - Ok(response) => { - match response { - Response::Offset { offset } => offset, + if let StreamState::Connected(stream) = &self.stream { + let _ = stream.shutdown(std::net::Shutdown::Both); + } - // TODO: disconnect - _ => panic!("received wrong response"), - } - } + self.stream = StreamState::Disconnected; + } - // TODO: disconnect - _ => panic!("timed out"), + pub fn get_line_number(&mut self, path: &str, override_id: u32, offset: u32) -> Result, Box> { + self.send_or_disconnect(Request::LineNumber { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + offset, + })?; + + match self.read_response_or_disconnect()? { + Response::LineNumber { line } => Ok(line), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } - pub fn set_breakpoint(&mut self, instruction: &InstructionRef) -> BreakpointSetResult { - self.send(Request::BreakpointSet { - instruction: instruction.clone(), - }) - .unwrap(); - - // TODO: disconnect on fail - match self.read_response() { - Ok(response) => { - match response { - Response::BreakpointSet { result } => { - return result; - } + pub fn get_offset(&mut self, path: &str, override_id: u32, line: u32) -> Result, Box> { + self.send_or_disconnect(Request::Offset { + proc: ProcRef { + path: path.to_owned(), + override_id, + }, + line, + })?; + + match self.read_response_or_disconnect()? { + Response::Offset { offset } => Ok(offset), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } + } - // TODO: disconnect - _ => panic!("received wrong response"), - } - } + pub fn set_breakpoint(&mut self, instruction: &InstructionRef) -> Result> { + self.send_or_disconnect(Request::BreakpointSet { + instruction: instruction.clone(), + })?; - // TODO: disconnect - _ => panic!("timed out"), + match self.read_response_or_disconnect()? { + Response::BreakpointSet { result } => Ok(result), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } - pub fn unset_breakpoint(&mut self, instruction: &InstructionRef) { - self.send(Request::BreakpointUnset { - instruction: instruction.clone(), - }) - .unwrap(); + pub fn unset_breakpoint(&mut self, instruction: &InstructionRef) -> Result<(), Box> { + self.send_or_disconnect(Request::BreakpointUnset { + instruction: instruction.clone(), + })?; - // TODO: disconnect - match self.read_response() { - Ok(response) => { - match response { - Response::BreakpointUnset { success: _ } => {} + match self.read_response_or_disconnect()? { + Response::BreakpointUnset { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } + } - // TODO: disconnect - _ => panic!("received wrong response"), - } - } + pub fn continue_execution(&mut self) -> Result<(), Box> { + self.send_or_disconnect(Request::Continue { + kind: ContinueKind::Continue, + })?; - // TODO: disconnect - _ => panic!("timed out"), + match self.read_response_or_disconnect()? { + Response::Ack { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } - pub fn continue_execution(&mut self) { - // TODO: disconnect - self.send(Request::Continue { - kind: ContinueKind::Continue, - }) - .unwrap(); - } + pub fn next(&mut self, stack_id: u32) -> Result<(), Box> { + self.send_or_disconnect(Request::Continue { + kind: ContinueKind::StepOver { stack_id }, + })?; - pub fn next(&mut self, stack_id: u32) { - // TODO: disconnect - self.send(Request::Continue { - kind: ContinueKind::StepOver { stack_id }, - }) - .unwrap(); + match self.read_response_or_disconnect()? { + Response::Ack { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } } - pub fn step_into(&mut self, stack_id: u32) { - // TODO: disconnect - self.send(Request::Continue { - kind: ContinueKind::StepInto { stack_id }, - }) - .unwrap(); - } + pub fn step_into(&mut self, stack_id: u32) -> Result<(), Box> { + self.send_or_disconnect(Request::Continue { + kind: ContinueKind::StepInto { stack_id }, + })?; - pub fn step_out(&mut self, stack_id: u32) { - // TODO: disconnect - self.send(Request::Continue { - kind: ContinueKind::StepOut { stack_id }, - }) - .unwrap(); + match self.read_response_or_disconnect()? { + Response::Ack { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } } - pub fn pause(&mut self) { - // TODO: disconnect - self.send(Request::Pause).unwrap(); + pub fn step_out(&mut self, stack_id: u32) -> Result<(), Box> { + self.send_or_disconnect(Request::Continue { + kind: ContinueKind::StepOut { stack_id }, + })?; + + match self.read_response_or_disconnect()? { + Response::Ack { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } } - pub fn get_stacks(&mut self) -> Vec { - self.send(Request::Stacks).unwrap(); + pub fn pause(&mut self) -> Result<(), Box> { + self.send_or_disconnect(Request::Pause)?; - match self.read_response() { - Ok(response) => { - match response { - Response::Stacks { stacks } => return stacks, + match self.read_response_or_disconnect()? { + Response::Ack { .. } => Ok(()), + _ => Err(Box::new(super::GenericError("received incorrect response"))), + } + } - // TODO: disconnect - _ => panic!("received wrong response"), - } - } + pub fn get_stacks(&mut self) -> Result, Box> { + self.send_or_disconnect(Request::Stacks)?; - // TODO: disconnect - _ => panic!("timed out"), + match self.read_response_or_disconnect()? { + Response::Stacks { stacks } => Ok(stacks), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } @@ -227,75 +241,46 @@ impl Auxtools { stack_id: u32, start_frame: Option, count: Option, - ) -> (Vec, u32) { - self.send(Request::StackFrames { + ) -> Result<(Vec, u32), Box> { + self.send_or_disconnect(Request::StackFrames { stack_id, start_frame, count, - }).unwrap(); - - match self.read_response() { - Ok(response) => { - match response { - Response::StackFrames { - frames, - total_count, - } => return (frames, total_count), - - // TODO: disconnect - _ => panic!("received wrong response"), - } - } - - // TODO: disconnect - _ => panic!("timed out"), + })?; + + match self.read_response_or_disconnect()? { + Response::StackFrames { + frames, + total_count, + } => Ok((frames, total_count)), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } // TODO: return all the scopes - pub fn get_scopes(&mut self, frame_id: u32) -> (Option, Option, Option) { - self.send(Request::Scopes { + pub fn get_scopes(&mut self, frame_id: u32) -> Result<(Option, Option, Option), Box> { + self.send_or_disconnect(Request::Scopes { frame_id - }).unwrap(); - - match self.read_response() { - Ok(response) => { - match response { - Response::Scopes { - arguments, - locals, - globals, - } => { - (arguments, locals, globals) - } - - // TODO: disconnect - _ => panic!("received wrong response"), - } - } - - // TODO: disconnect - _ => panic!("timed out"), + })?; + + match self.read_response_or_disconnect()? { + Response::Scopes { + arguments, + locals, + globals, + } => Ok((arguments, locals, globals)), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } - pub fn get_variables(&mut self, vars: VariablesRef) -> Vec { - self.send(Request::Variables { + pub fn get_variables(&mut self, vars: VariablesRef) -> Result, Box> { + self.send_or_disconnect(Request::Variables { vars - }).unwrap(); - - match self.read_response() { - Ok(response) => { - match response { - Response::Variables { vars } => return vars, - - // TODO: disconnect - _ => panic!("received wrong response"), - } - } + })?; - // TODO: disconnect - _ => panic!("timed out"), + match self.read_response_or_disconnect()? { + Response::Variables { vars } => Ok(vars), + _ => Err(Box::new(super::GenericError("received incorrect response"))), } } @@ -303,8 +288,8 @@ impl Auxtools { self.last_error.read().unwrap().clone() } - pub fn set_catch_runtimes(&mut self, should_catch: bool) { - self.send(Request::CatchRuntimes { should_catch }).unwrap(); + pub fn set_catch_runtimes(&mut self, should_catch: bool) -> Result<(), Box> { + self.send_or_disconnect(Request::CatchRuntimes { should_catch }) } } @@ -351,49 +336,46 @@ impl AuxtoolsThread { Ok(false) } - // TODO: rewrite to not be a non-blocking socket - pub fn start_thread(mut self) -> JoinHandle<()> { - thread::spawn(move || { - let mut buf = vec![]; - - // The incoming stream is a u32 followed by a bincode-encoded Request. - loop { - let mut len_bytes = [0u8; 4]; - let len = match self.stream.read_exact(&mut len_bytes) { - Ok(_) => u32::from_le_bytes(len_bytes), - - Err(e) => { - eprintln!("Debug server thread read error: {}", e); - break; - } - }; + pub fn run(mut self, mut stream: TcpStream) { + let mut buf = vec![]; - buf.resize(len as usize, 0); - match self.stream.read_exact(&mut buf) { - Ok(_) => (), - - Err(e) => { - eprintln!("Debug server thread read error: {}", e); - break; - } - }; + // The incoming stream is a u32 followed by a bincode-encoded Request. + loop { + let mut len_bytes = [0u8; 4]; + let len = match stream.read_exact(&mut len_bytes) { + Ok(_) => u32::from_le_bytes(len_bytes), - match self.handle_response(&buf[..]) { - Ok(requested_disconnect) => { - if requested_disconnect { - eprintln!("Debug server disconnected"); - break; - } - } - - Err(e) => { - eprintln!("Debug server thread failed to handle request: {}", e); + Err(e) => { + eprintln!("Debug server thread read error: {}", e); + break; + } + }; + + buf.resize(len as usize, 0); + match stream.read_exact(&mut buf) { + Ok(_) => (), + + Err(e) => { + eprintln!("Debug server thread read error: {}", e); + break; + } + }; + + match self.handle_response(&buf[..]) { + Ok(requested_disconnect) => { + if requested_disconnect { + eprintln!("Debug server disconnected"); break; } } + + Err(e) => { + eprintln!("Debug server thread failed to handle request: {}", e); + break; + } } + } - self.seq.issue_event(dap_types::TerminatedEvent::default()); - }) + self.seq.issue_event(dap_types::TerminatedEvent::default()); } } diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index d3e6e16e8..cd1b231d0 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -47,6 +47,7 @@ pub enum Request { // Message from server -> client #[derive(Serialize, Deserialize, Debug)] pub enum Response { + Ack, BreakpointSet { result: BreakpointSetResult, }, diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 0f853ee6b..e630e71dc 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -352,7 +352,7 @@ impl Debugger { } } - DebugClient::Auxtools(auxtools) => for stack in auxtools.get_stacks() { + DebugClient::Auxtools(auxtools) => for stack in auxtools.get_stacks().unwrap_or(vec![]) { if stack.id == 0 { continue; } @@ -434,7 +434,7 @@ handle_request! { } on AttachVsc(&mut self, params) { - self.client = DebugClient::Auxtools(Auxtools::new(self.seq.clone(), params.port)?); + self.client = DebugClient::Auxtools(Auxtools::connect(self.seq.clone(), params.port)?); /*self.client = match params.client { DebugClientParam::Extools => { DebugClient::Extools(ExtoolsHolder::attach(self.seq.clone(), params.port.unwrap_or(extools::DEFAULT_PORT))?) @@ -512,7 +512,7 @@ handle_request! { }, DebugClient::Auxtools(auxtools) => { - let mut threads : Vec = auxtools.get_stacks().into_iter().map(|x| { + let mut threads : Vec = auxtools.get_stacks()?.into_iter().map(|x| { Thread { id: x.id as i64, name: x.name, @@ -627,7 +627,7 @@ handle_request! { // TODO: better discipline around format!("{}/{}") and so on let proc = format!("{}/{}", typepath, name); - if let Some(offset) = auxtools.get_offset(proc.as_str(), override_id as u32, sbp.line as u32) { + if let Some(offset) = auxtools.get_offset(proc.as_str(), override_id as u32, sbp.line as u32)? { saved.insert((proc.clone(), override_id, sbp.line)); keep.insert((proc.clone(), override_id, sbp.line)); @@ -637,7 +637,7 @@ handle_request! { override_id: override_id as u32 }, offset - }); + })?; breakpoints.push(match result { auxtools_types::BreakpointSetResult::Success { line } => { @@ -680,8 +680,8 @@ handle_request! { saved.retain(|k| { if !keep.contains(&k) { - if let Some(offset) = auxtools.get_offset(k.0.as_str(), k.1 as u32, k.2 as u32) { - auxtools.unset_breakpoint(&auxtools_types::InstructionRef { + if let Ok(Some(offset)) = auxtools.get_offset(k.0.as_str(), k.1 as u32, k.2 as u32) { + let _ = auxtools.unset_breakpoint(&auxtools_types::InstructionRef { proc: auxtools_types::ProcRef { path: k.0.clone(), override_id: k.1 as u32 @@ -841,7 +841,7 @@ handle_request! { let (aux_frames, aux_frames_total) = auxtools.get_stack_frames( params.threadId as u32, params.startFrame.map(|x| x as u32), - params.levels.map(|x| x as u32)); + params.levels.map(|x| x as u32))?; let mut frames = Vec::with_capacity(aux_frames.len()); for (i, aux_frame) in aux_frames.iter().enumerate() { @@ -945,7 +945,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - let (arguments, locals, globals) = auxtools.get_scopes( frameId as u32 ); + let (arguments, locals, globals) = auxtools.get_scopes( frameId as u32 )?; let mut scopes = vec![]; if let Some(arguments) = arguments { @@ -1115,7 +1115,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - let aux_variables = auxtools.get_variables(auxtools_types::VariablesRef(params.variablesReference as i32)); + let aux_variables = auxtools.get_variables(auxtools_types::VariablesRef(params.variablesReference as i32))?; let mut variables = vec![]; // TODO @@ -1148,7 +1148,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.continue_execution(); + auxtools.continue_execution()?; } } @@ -1167,7 +1167,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.step_into(params.threadId as u32); + auxtools.step_into(params.threadId as u32)?; } } } @@ -1182,7 +1182,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.next(params.threadId as u32); + auxtools.next(params.threadId as u32)?; } } } @@ -1197,7 +1197,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.step_out(params.threadId as u32); + auxtools.step_out(params.threadId as u32)?; } } } @@ -1210,7 +1210,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.pause(); + auxtools.pause()?; } } } @@ -1223,7 +1223,7 @@ handle_request! { } DebugClient::Auxtools(auxtools) => { - auxtools.set_catch_runtimes(params.filters.iter().any(|x| x == EXCEPTION_FILTER_RUNTIMES)); + auxtools.set_catch_runtimes(params.filters.iter().any(|x| x == EXCEPTION_FILTER_RUNTIMES))?; } } } From 5eed99453e2c71ee14da1e0f09b4c114d4da3d8d Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 02:37:55 +0000 Subject: [PATCH 20/30] env vars and stuff --- src/dreammaker/config.rs | 22 +++++ src/langserver/build.rs | 6 ++ src/langserver/debugger/auxtools.rs | 56 ++++++++++++- src/langserver/debugger/auxtools_bundle.rs | 24 ++++++ src/langserver/debugger/launched.rs | 42 +++++++--- src/langserver/debugger/mod.rs | 97 +++++++++++++--------- 6 files changed, 198 insertions(+), 49 deletions(-) create mode 100644 src/langserver/debugger/auxtools_bundle.rs diff --git a/src/dreammaker/config.rs b/src/dreammaker/config.rs index bb8785c84..3b6ff81a4 100644 --- a/src/dreammaker/config.rs +++ b/src/dreammaker/config.rs @@ -24,6 +24,7 @@ pub struct Config { // tool-specific configuration pub langserver: Langserver, pub dmdoc: DMDoc, + pub debugger: Debugger, } /// General error display options @@ -53,6 +54,13 @@ pub struct DMDoc { pub use_typepath_names: bool, } +// Debugger config options +#[derive(Deserialize, Default, Debug, Clone)] +pub struct Debugger { + #[serde(default)] + pub engine: DebugEngine, +} + /// Severity overrides from configuration #[derive(Debug, Deserialize, Clone, Copy, PartialEq)] #[serde(rename_all(deserialize = "lowercase"))] @@ -70,6 +78,14 @@ pub enum WarningLevel { Unset = 6, } +#[derive(Debug, Deserialize, Clone, Copy, PartialEq)] +pub enum DebugEngine { + #[serde(alias = "extools")] + Extools, + #[serde(alias = "auxtools")] + Auxtools, +} + impl Config { /// Read a config TOML and generate a [`Config`] struct /// @@ -152,6 +168,12 @@ impl PartialEq for WarningLevel { } } +impl Default for DebugEngine { + fn default() -> Self { + Self::Extools + } +} + /// Config parse error #[derive(Debug)] pub enum Error { diff --git a/src/langserver/build.rs b/src/langserver/build.rs index 2e673d14e..fbc67b762 100644 --- a/src/langserver/build.rs +++ b/src/langserver/build.rs @@ -21,6 +21,12 @@ fn main() { if env::var_os("EXTOOLS_BUNDLE_DLL").is_some() { println!("cargo:rustc-cfg=extools_bundle"); } + + // auxtools bundling + println!("cargo:rerun-if-env-changed=AUXTOOLS_BUNDLE_DLL"); + if env::var_os("AUXTOOLS_BUNDLE_DLL").is_some() { + println!("cargo:rustc-cfg=auxtools_bundle"); + } } fn read_commit() -> Result { diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 45fb298dd..5c08a5e6b 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -1,5 +1,5 @@ use super::auxtools_types::*; -use std::{sync::mpsc}; +use std::{net::TcpListener, sync::mpsc}; use std::thread; use std::{ io::{Read, Write}, @@ -69,6 +69,33 @@ impl Auxtools { }) } + pub fn listen(seq: Arc) -> std::io::Result<(u16, Self)> { + let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))?; + let port = listener.local_addr()?.port(); + + let (connection_sender, connection_receiver) = mpsc::channel(); + let (responses_sender, responses_receiver) = mpsc::channel(); + let last_error = Arc::new(RwLock::new("".to_owned())); + + let thread = { + let seq = seq.clone(); + let last_error = last_error.clone(); + AuxtoolsThread { + seq, + responses: responses_sender, + last_error: last_error, + }.spawn_listener(listener, connection_sender) + }; + + Ok((port, Auxtools { + seq, + responses: responses_receiver, + _thread: thread, + stream: StreamState::Waiting(connection_receiver), + last_error, + })) + } + fn read_response_or_disconnect(&mut self) -> Result> { match self.responses.recv_timeout(std::time::Duration::from_secs(5)) { Ok(response) => Ok(response), @@ -294,6 +321,33 @@ impl Auxtools { } impl AuxtoolsThread { + fn spawn_listener( + self, + listener: TcpListener, + connection_sender: mpsc::Sender, + ) -> JoinHandle<()> { + thread::spawn(move || match listener.accept() { + Ok((stream, _)) => { + match connection_sender.send(stream.try_clone().unwrap()) { + Ok(_) => {} + Err(e) => { + eprintln!("Debug client thread failed to pass cloned TcpStream: {}", e); + return; + } + } + + // Look at this little trouble-maker right here (he got me again) + self.seq.issue_event(dap_types::InitializedEvent); + + self.run(stream); + } + + Err(e) => { + eprintln!("Debug client failed to accept connection: {}", e); + } + }) + } + // returns true if we should disconnect fn handle_response(&mut self, data: &[u8]) -> Result> { let response = bincode::deserialize::(data)?; diff --git a/src/langserver/debugger/auxtools_bundle.rs b/src/langserver/debugger/auxtools_bundle.rs new file mode 100644 index 000000000..240ba15dd --- /dev/null +++ b/src/langserver/debugger/auxtools_bundle.rs @@ -0,0 +1,24 @@ +#![cfg(auxtools_bundle)] +use std::fs::File; +use std::io::{Result, Write}; +use std::path::{Path, PathBuf}; + +const BYTES: &[u8] = include_bytes!(env!("AUXTOOLS_BUNDLE_DLL")); + +fn write(path: &Path) -> Result<()> { + File::create(path)?.write_all(BYTES) +} + +pub fn extract() -> Result { + let exe = std::env::current_exe()?; + let directory = exe.parent().unwrap(); + for i in 0..9 { + let dll = directory.join(format!("auxtools_debug_server{}.dll", i)); + if let Ok(()) = write(&dll) { + return Ok(dll); + } + } + let dll = directory.join("auxtools_debug_server9.dll"); + write(&dll)?; + Ok(dll) +} diff --git a/src/langserver/debugger/launched.rs b/src/langserver/debugger/launched.rs index cc9a12ac9..67a2b58e9 100644 --- a/src/langserver/debugger/launched.rs +++ b/src/langserver/debugger/launched.rs @@ -27,13 +27,23 @@ pub struct Launched { mutex: Arc>, } +pub enum EngineParams { + Extools { + port: u16, + dll: Option + }, + Auxtools { + port: u16, + dll: Option + } +} + impl Launched { pub fn new( seq: Arc, dreamseeker_exe: &str, dmb: &str, - port: Option, - extools_dll: Option<&std::path::Path>, + params: Option, ) -> std::io::Result { let mut command = Command::new(dreamseeker_exe); command @@ -42,15 +52,27 @@ impl Launched { .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()); - if let Some(extools_dll) = extools_dll { - command.env("EXTOOLS_DLL", extools_dll); - } - if let Some(port) = port { - command.env("EXTOOLS_MODE", "LAUNCHED"); - command.env("EXTOOLS_PORT", port.to_string()); - } else { - command.env("EXTOOLS_MODE", "NONE"); + + match params { + Some(EngineParams::Extools { port, dll }) => { + command.env("EXTOOLS_MODE", "LAUNCHED"); + command.env("EXTOOLS_PORT", port.to_string()); + if let Some(dll) = dll { + command.env("EXTOOLS_DLL", dll); + } + } + + Some(EngineParams::Auxtools { port, dll }) => { + command.env("AUXTOOLS_DEBUG_MODE", "LAUNCHED"); + command.env("AUXTOOLS_DEBUG_PORT", port.to_string()); + if let Some(dll) = dll { + command.env("AUXTOOLS_DEBUG_DLL", dll); + } + } + + None => (), } + let mut child = command.spawn()?; output!(in seq, "[launched] Started: {:?}", command); let mutex = Arc::new(Mutex::new(State::Active)); diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index e630e71dc..eaeefa47e 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -28,6 +28,7 @@ macro_rules! debug_output { } mod auxtools; +mod auxtools_bundle; mod auxtools_types; mod dap_types; mod evaluate; @@ -42,12 +43,13 @@ use std::sync::{atomic, Arc, Mutex}; use dm::objtree::ObjectTree; use dm::FileId; +use dreammaker::config::DebugEngine; use auxtools::Auxtools; use self::dap_types::*; use self::extools::ExtoolsHolder; -use self::launched::Launched; +use self::launched::{Launched, EngineParams}; use crate::jrpc_io; pub fn start_server( @@ -64,9 +66,13 @@ pub fn start_server( .spawn(move || { let (stream, _) = listener.accept().unwrap(); drop(listener); - + let env = dm::detect_environment_default() + .expect("error detecting .dme") + .expect("no .dme found"); + let ctx = dm::Context::default(); + ctx.autodetect_config(&env); let mut input = std::io::BufReader::new(stream.try_clone().unwrap()); - let mut debugger = Debugger::new(dreamseeker_exe, db, Box::new(stream)); + let mut debugger = Debugger::new(ctx, dreamseeker_exe, db, Box::new(stream)); jrpc_io::run_with_read(&mut input, |message| debugger.handle_input(message)); })?; @@ -98,6 +104,7 @@ pub fn debugger_main>(mut args: I) { .expect("detect .dme error") .expect("did not detect a .dme"); let ctx = dm::Context::default(); + ctx.autodetect_config(&environment); let mut pp = dm::preprocessor::Preprocessor::new(&ctx, environment).unwrap(); let objtree = { let mut parser = @@ -112,7 +119,7 @@ pub fn debugger_main>(mut args: I) { objtree, extools_dll: None, }; - let mut debugger = Debugger::new(dreamseeker_exe, db, Box::new(std::io::stdout())); + let mut debugger = Debugger::new(ctx, dreamseeker_exe, db, Box::new(std::io::stdout())); jrpc_io::run_until_stdin_eof(|message| debugger.handle_input(message)); } @@ -232,6 +239,7 @@ enum DebugClient { } struct Debugger { + context: dm::Context, dreamseeker_exe: String, extools_dll: Option, db: DebugDatabase, @@ -246,8 +254,9 @@ struct Debugger { } impl Debugger { - fn new(dreamseeker_exe: String, mut db: DebugDatabaseBuilder, stream: OutStream) -> Self { + fn new(context: dm::Context, dreamseeker_exe: String, mut db: DebugDatabaseBuilder, stream: OutStream) -> Self { Debugger { + context, dreamseeker_exe, extools_dll: db.extools_dll.take(), db: db.build(), @@ -405,45 +414,64 @@ handle_request! { on LaunchVsc(&mut self, params) { // Determine port number to pass if debugging is enabled. let debug = !params.base.noDebug.unwrap_or(false); - let port = if debug { - let (port, extools) = ExtoolsHolder::listen(self.seq.clone())?; - self.client = DebugClient::Extools(extools); - Some(port) - } else { - None - }; - // Set EXTOOLS_DLL based on configuration or on bundle if available. - #[cfg(extools_bundle)] - let pathbuf; + let engine_params = if debug { + Some(match self.context.config().debugger.engine { + DebugEngine::Extools => { + let (port, extools) = ExtoolsHolder::listen(self.seq.clone())?; + self.client = DebugClient::Extools(extools); - #[allow(unused_mut)] - let mut extools_dll = self.extools_dll.as_ref().map(std::path::Path::new); + // Set EXTOOLS_DLL based on configuration or on bundle if available. + #[allow(unused_mut)] + let mut extools_dll = self.extools_dll.as_ref().map(|x| std::path::Path::new(x).to_path_buf()); - debug_output!(in self.seq, "[main] configured override: {:?}", extools_dll); + debug_output!(in self.seq, "[main] configured override: {:?}", extools_dll); - #[cfg(extools_bundle)] { - if extools_dll.is_none() { - pathbuf = self::extools_bundle::extract()?; - extools_dll = Some(&pathbuf); - } - } + #[cfg(extools_bundle)] { + if extools_dll.is_none() { + extools_dll = Some(self::extools_bundle::extract()?); + } + } + + EngineParams::Extools { + port, + dll: extools_dll, + } + } + + DebugEngine::Auxtools => { + let (port, auxtools) = Auxtools::listen(self.seq.clone())?; + self.client = DebugClient::Auxtools(auxtools); + let auxtools_dll = None; + + #[cfg(extools_bundle)] { + auxtools_dll = Some(self::auxtools_bundle::extract()?); + } + + EngineParams::Auxtools { + port, + dll: auxtools_dll, + } + } + }) + } else { + None + }; // Launch the subprocess. - self.launched = Some(Launched::new(self.seq.clone(), &self.dreamseeker_exe, ¶ms.dmb, port, extools_dll)?); + self.launched = Some(Launched::new(self.seq.clone(), &self.dreamseeker_exe, ¶ms.dmb, engine_params)?); } on AttachVsc(&mut self, params) { - self.client = DebugClient::Auxtools(Auxtools::connect(self.seq.clone(), params.port)?); - /*self.client = match params.client { - DebugClientParam::Extools => { + self.client = match self.context.config().debugger.engine { + DebugEngine::Extools => { DebugClient::Extools(ExtoolsHolder::attach(self.seq.clone(), params.port.unwrap_or(extools::DEFAULT_PORT))?) } - DebugClientParam::Auxtools => { - DebugClient::Auxtools(Auxtools::new(self.seq.clone(), params.port)?) + DebugEngine::Auxtools => { + DebugClient::Auxtools(Auxtools::connect(self.seq.clone(), params.port)?) } - };*/ + }; } on Disconnect(&mut self, params) { @@ -1436,12 +1464,6 @@ impl Request for LaunchVsc { const COMMAND: &'static str = Launch::COMMAND; } -#[derive(Deserialize)] -pub enum DebugClientParam { - Extools, - Auxtools, -} - #[derive(Deserialize)] pub struct LaunchRequestArgumentsVsc { #[serde(flatten)] @@ -1464,7 +1486,6 @@ impl Request for AttachVsc { pub struct AttachRequestArgumentsVsc { #[serde(flatten)] base: AttachRequestArguments, - //client: DebugClientParam, port: Option, } From f2f59c8a221371e694d056701af092ad8846a56c Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 02:44:16 +0000 Subject: [PATCH 21/30] move dap initialize event --- src/langserver/debugger/auxtools.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 5c08a5e6b..b553b3f48 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -44,9 +44,6 @@ impl Auxtools { let last_error = Arc::new(RwLock::new("".to_owned())); let stream = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5))?; - // Look at this little trouble-maker right here - seq.issue_event(dap_types::InitializedEvent); - let thread = { let seq = seq.clone(); let last_error = last_error.clone(); @@ -336,9 +333,6 @@ impl AuxtoolsThread { } } - // Look at this little trouble-maker right here (he got me again) - self.seq.issue_event(dap_types::InitializedEvent); - self.run(stream); } @@ -393,6 +387,9 @@ impl AuxtoolsThread { pub fn run(mut self, mut stream: TcpStream) { let mut buf = vec![]; + // Look at this little trouble-maker right here + self.seq.issue_event(dap_types::InitializedEvent); + // The incoming stream is a u32 followed by a bincode-encoded Request. loop { let mut len_bytes = [0u8; 4]; From 1c77f8f995cf3f06d886bd08ea7cdf797905717c Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 18:03:48 +0000 Subject: [PATCH 22/30] restore dap_types.rs to old contents --- src/langserver/debugger/dap_types.rs | 51 ++++++++++++---------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/langserver/debugger/dap_types.rs b/src/langserver/debugger/dap_types.rs index 284b62cdc..9eba3a606 100644 --- a/src/langserver/debugger/dap_types.rs +++ b/src/langserver/debugger/dap_types.rs @@ -3,8 +3,8 @@ //! * https://microsoft.github.io/debug-adapter-protocol/specification #![allow(non_snake_case)] -use serde_json::Value; use std::collections::HashMap; +use serde_json::Value; pub trait Request { type Params; @@ -173,8 +173,8 @@ pub struct ThreadEvent { pub reason: String, /** - * The identifier of the thread. - */ + * The identifier of the thread. + */ pub threadId: i64, } @@ -575,7 +575,7 @@ pub struct EvaluateResponse { /** * The optional type of the evaluate result. */ - #[serde(rename = "type")] + #[serde(rename="type")] pub type_: Option, /** @@ -608,19 +608,13 @@ pub struct EvaluateResponse { impl From for EvaluateResponse { fn from(result: String) -> EvaluateResponse { - EvaluateResponse { - result, - ..Default::default() - } + EvaluateResponse { result, .. Default::default() } } } impl From<&str> for EvaluateResponse { fn from(result: &str) -> EvaluateResponse { - EvaluateResponse { - result: result.to_owned(), - ..Default::default() - } + EvaluateResponse { result: result.to_owned(), .. Default::default() } } } @@ -932,10 +926,7 @@ pub struct SourceResponse { impl From for SourceResponse { fn from(content: String) -> SourceResponse { - SourceResponse { - content, - mimeType: None, - } + SourceResponse { content, mimeType: None } } } @@ -1084,9 +1075,9 @@ pub struct VariablesArguments { #[derive(Serialize, Deserialize, Debug)] pub enum VariablesFilter { - #[serde(rename = "indexed")] + #[serde(rename="indexed")] Indexed, - #[serde(rename = "named")] + #[serde(rename="named")] Named, } @@ -1365,16 +1356,16 @@ pub struct DisassembledInstruction { #[derive(Serialize, Deserialize, Debug)] pub enum ExceptionBreakMode { /// never breaks - #[serde(rename = "never")] + #[serde(rename="never")] Never, /// always breaks - #[serde(rename = "always")] + #[serde(rename="always")] Always, /// breaks when exception unhandled - #[serde(rename = "unhandled")] + #[serde(rename="unhandled")] Unhandled, /// breaks if the exception is not handled by user code - #[serde(rename = "userUnhandled")] + #[serde(rename="userUnhandled")] UserUnhandled, } @@ -1667,6 +1658,7 @@ pub struct Source { * Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ pub adapterData: Option, + /*/** * The checksums associated with this file. */ @@ -1675,11 +1667,11 @@ pub struct Source { #[derive(Serialize, Deserialize, Debug)] pub enum SourcePresentationHint { - #[serde(rename = "normal")] + #[serde(rename="normal")] Normal, - #[serde(rename = "emphasize")] + #[serde(rename="emphasize")] Emphasize, - #[serde(rename = "deemphasize")] + #[serde(rename="deemphasize")] Deemphasize, } @@ -1759,6 +1751,7 @@ pub struct StackFrame { * The module associated with this frame, if any. */ moduleId?: number | string;*/ + /** * An optional hint for how to present this frame in the UI. A value of 'label' can be used to indicate that the frame is an artificial frame that is used as a visual label or separator. A value of 'subtle' can be used to change the appearance of a frame in a 'subtle' way. */ @@ -1767,11 +1760,11 @@ pub struct StackFrame { #[derive(Serialize, Deserialize, Debug)] pub enum StackFramePresentationHint { - #[serde(rename = "normal")] + #[serde(rename="normal")] Normal, - #[serde(rename = "label")] + #[serde(rename="label")] Label, - #[serde(rename = "subtle")] + #[serde(rename="subtle")] Subtle, } @@ -1875,7 +1868,7 @@ pub struct Variable { /** * The type of the variable's value. Typically shown in the UI when hovering over the value. */ - #[serde(rename = "type")] + #[serde(rename="type")] pub type_: Option, /** From bcf731a9e57854239287d23094dbd8528496aee3 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 18:07:20 +0000 Subject: [PATCH 23/30] restore extools to pre-fmt contents --- src/langserver/debugger/extools.rs | 105 +++++++++-------------------- 1 file changed, 31 insertions(+), 74 deletions(-) diff --git a/src/langserver/debugger/extools.rs b/src/langserver/debugger/extools.rs index 018b9a509..41bf2a26b 100644 --- a/src/langserver/debugger/extools.rs +++ b/src/langserver/debugger/extools.rs @@ -1,15 +1,15 @@ //! Client for the Extools debugger protocol. +use std::time::Duration; +use std::sync::{mpsc, Arc, Mutex}; +use std::net::{SocketAddr, Ipv4Addr, TcpStream, TcpListener}; use std::collections::HashMap; -use std::error::Error; use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream}; -use std::sync::{mpsc, Arc, Mutex}; -use std::time::Duration; +use std::error::Error; +use super::SequenceNumber; use super::dap_types; use super::extools_types::*; -use super::SequenceNumber; pub const DEFAULT_PORT: u16 = 2448; @@ -71,10 +71,10 @@ impl ExtoolsHolder { } })?; - Ok(( + Ok((port, ExtoolsHolder(ExtoolsHolderInner::Listening { port, - ExtoolsHolder(ExtoolsHolderInner::Listening { port, conn_rx }), - )) + conn_rx, + }))) } pub fn attach(seq: Arc, port: u16) -> std::io::Result { @@ -88,9 +88,7 @@ impl ExtoolsHolder { .name(format!("extools attaching on port {}", port)) .spawn(move || { while let Err(mpsc::TryRecvError::Empty) = cancel_rx.try_recv() { - if let Ok(stream) = - TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5)) - { + if let Ok(stream) = TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(5)) { let (conn, mut thread) = Extools::from_stream(seq, stream); if conn_tx.send(conn).is_ok() { thread.read_loop(); @@ -107,14 +105,13 @@ impl ExtoolsHolder { } pub fn get(&mut self) -> Result<&mut Extools, Box> { - self.as_ref() - .ok_or_else(|| Box::new(super::GenericError("No extools connection")) as Box) + self.as_ref().ok_or_else(|| Box::new(super::GenericError("No extools connection")) as Box) } pub fn as_ref(&mut self) -> Option<&mut Extools> { match &mut self.0 { - ExtoolsHolderInner::Listening { conn_rx, .. } - | ExtoolsHolderInner::Attaching { conn_rx, .. } => { + ExtoolsHolderInner::Listening { conn_rx, .. } | + ExtoolsHolderInner::Attaching { conn_rx, .. } => { if let Ok(conn) = conn_rx.try_recv() { self.0 = ExtoolsHolderInner::Active(conn); } @@ -123,15 +120,13 @@ impl ExtoolsHolder { } match &mut self.0 { ExtoolsHolderInner::Active(conn) => Some(conn), - _ => None, + _ => None } } pub fn disconnect(&mut self) { match std::mem::replace(&mut self.0, ExtoolsHolderInner::None) { - ExtoolsHolderInner::Attaching { cancel_tx, .. } => { - let _ = cancel_tx.send(()); - } + ExtoolsHolderInner::Attaching { cancel_tx, .. } => { let _ = cancel_tx.send(()); }, // TODO: ExtoolsHolderInner::Listening _ => {} } @@ -206,38 +201,22 @@ impl Extools { } pub fn get_thread(&self, thread_id: i64) -> Result> { - self.threads - .lock() - .unwrap() - .get(&thread_id) - .cloned() - .ok_or_else(|| { - Box::new(super::GenericError("Getting call stack failed")) as Box - }) - } - - pub fn get_thread_by_frame_id( - &self, - frame_id: i64, - ) -> Result<(ThreadInfo, usize), Box> { + self.threads.lock().unwrap().get(&thread_id).cloned() + .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box) + } + + pub fn get_thread_by_frame_id(&self, frame_id: i64) -> Result<(ThreadInfo, usize), Box> { let frame_id = frame_id as usize; let threads = self.threads.lock().unwrap(); let thread_id = (frame_id % threads.len()) as i64; let frame_no = frame_id / threads.len(); - let thread = threads.get(&thread_id).cloned().ok_or_else(|| { - Box::new(super::GenericError("Getting call stack failed")) as Box - })?; + let thread = threads.get(&thread_id).cloned() + .ok_or_else(|| Box::new(super::GenericError("Getting call stack failed")) as Box)?; Ok((thread, frame_no)) } pub fn bytecode(&mut self, proc_ref: &str, override_id: usize) -> &[DisassembledInstruction] { - let Extools { - bytecode, - sender, - seq: _seq, - bytecode_rx, - .. - } = self; + let Extools { bytecode, sender, seq: _seq, bytecode_rx, .. } = self; bytecode.entry((proc_ref.to_owned(), override_id)).or_insert_with(|| { debug_output!(in _seq, "[extools] Fetching bytecode for {}#{}", proc_ref, override_id); sender.send(ProcDisassemblyRequest(ProcId { @@ -248,12 +227,7 @@ impl Extools { }) } - pub fn offset_to_line( - &mut self, - proc_ref: &str, - override_id: usize, - offset: i64, - ) -> Option { + pub fn offset_to_line(&mut self, proc_ref: &str, override_id: usize, offset: i64) -> Option { let bc = self.bytecode(proc_ref, override_id); let mut comment = ""; for instr in bc.iter() { @@ -286,19 +260,11 @@ impl Extools { } pub fn set_breakpoint(&self, proc: &str, override_id: usize, offset: i64) { - self.sender.send(BreakpointSet(ProcOffset { - proc: proc.to_owned(), - override_id, - offset, - })); + self.sender.send(BreakpointSet(ProcOffset { proc: proc.to_owned(), override_id, offset })); } pub fn unset_breakpoint(&self, proc: &str, override_id: usize, offset: i64) { - self.sender.send(BreakpointUnset(ProcOffset { - proc: proc.to_owned(), - override_id, - offset, - })); + self.sender.send(BreakpointUnset(ProcOffset { proc: proc.to_owned(), override_id, offset })); } pub fn continue_execution(&self) { @@ -331,10 +297,7 @@ impl Extools { Ok(self.get_type_rx.recv_timeout(RECV_TIMEOUT)?.0) } - pub fn get_all_fields( - &self, - reference: Ref, - ) -> Result, Box> { + pub fn get_all_fields(&self, reference: Ref) -> Result, Box> { self.sender.send(GetAllFields(reference)); Ok(self.get_field_rx.recv_timeout(RECV_TIMEOUT)?.0) } @@ -457,13 +420,13 @@ impl ExtoolsThread { reason: "sleep".to_owned(), threadId: Some(k), preserveFocusHint: Some(true), - ..Default::default() + .. Default::default() }); } } self.seq.issue_event(dap_types::StoppedEvent { threadId: Some(0), - ..base + .. base }); } } @@ -559,10 +522,7 @@ impl Clone for ExtoolsSender { fn clone(&self) -> ExtoolsSender { ExtoolsSender { seq: self.seq.clone(), - stream: self - .stream - .try_clone() - .expect("TcpStream::try_clone failed in ExtoolsSender::clone"), + stream: self.stream.try_clone().expect("TcpStream::try_clone failed in ExtoolsSender::clone") } } } @@ -573,12 +533,9 @@ impl ExtoolsSender { let mut buffer = serde_json::to_vec(&ProtocolMessage { type_: M::TYPE.to_owned(), content: Some(content), - }) - .expect("extools encode error"); + }).expect("extools encode error"); buffer.push(0); // TODO: needs more synchronization - (&self.stream) - .write_all(&buffer[..]) - .expect("extools write error"); + (&self.stream).write_all(&buffer[..]).expect("extools write error"); } } From dd7c6ed36ba7071b3f0674cc56884322a212907b Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 18:08:14 +0000 Subject: [PATCH 24/30] restore extools_bundle to pre-fmt contents --- src/langserver/debugger/extools_bundle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/langserver/debugger/extools_bundle.rs b/src/langserver/debugger/extools_bundle.rs index 04c631c8a..02396526e 100644 --- a/src/langserver/debugger/extools_bundle.rs +++ b/src/langserver/debugger/extools_bundle.rs @@ -1,7 +1,7 @@ #![cfg(extools_bundle)] -use std::fs::File; use std::io::{Result, Write}; use std::path::{Path, PathBuf}; +use std::fs::File; const BYTES: &[u8] = include_bytes!(env!("EXTOOLS_BUNDLE_DLL")); From 4b4d0a0b280cf268fe107d892ffc92244fe31528 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 18:09:02 +0000 Subject: [PATCH 25/30] restore extools_types to pre-fmt contents --- src/langserver/debugger/extools_types.rs | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/langserver/debugger/extools_types.rs b/src/langserver/debugger/extools_types.rs index 1c1638648..4b1e59465 100644 --- a/src/langserver/debugger/extools_types.rs +++ b/src/langserver/debugger/extools_types.rs @@ -1,11 +1,12 @@ //! Serde types for the Extools debugger protocol. //! //! * https://github.com/MCHSL/extools/blob/master/byond-extools/src/debug_server/protocol.h -use serde_json::Value as Json; /// /// > All communication happens over a TCP socket using a JSON-based protocol. /// > A null byte signifies the end of a message. + use std::collections::HashMap; +use serde_json::Value as Json; // ---------------------------------------------------------------------------- // Extools data structures @@ -131,14 +132,11 @@ impl ValueText { let ref_ = Ref(raw); let is_list = raw >> 24 == 0x0F; - ( - ValueText { - literal: Literal::Ref(ref_), - has_vars: !is_list, - is_list, - }, - ref_, - ) + (ValueText { + literal: Literal::Ref(ref_), + has_vars: !is_list, + is_list, + }, ref_) } pub fn to_variables_reference(&self) -> i64 { @@ -167,10 +165,12 @@ impl std::fmt::Display for Literal { Literal::String(s) => write!(fmt, "{:?}", s), Literal::Typepath(t) => write!(fmt, "{}", t), Literal::Resource(f) => write!(fmt, "'{}'", f), - Literal::Proc(p) => match p.rfind('/') { - Some(idx) => write!(fmt, "{}/proc/{}", &p[..idx], &p[idx + 1..]), - None => write!(fmt, "{}", p), - }, + Literal::Proc(p) => { + match p.rfind('/') { + Some(idx) => write!(fmt, "{}/proc/{}", &p[..idx], &p[idx + 1..]), + None => write!(fmt, "{}", p), + } + } } } } From a5313dc93c67a1878d40dbbe2051969cd5feede6 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 18:32:23 +0000 Subject: [PATCH 26/30] function breakpoints --- src/langserver/debugger/mod.rs | 88 +++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index eaeefa47e..30b08b44d 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -656,8 +656,8 @@ handle_request! { let proc = format!("{}/{}", typepath, name); if let Some(offset) = auxtools.get_offset(proc.as_str(), override_id as u32, sbp.line as u32)? { - saved.insert((proc.clone(), override_id, sbp.line)); - keep.insert((proc.clone(), override_id, sbp.line)); + saved.insert((proc.clone(), override_id, offset as i64)); + keep.insert((proc.clone(), override_id, offset as i64)); let result = auxtools.set_breakpoint(&auxtools_types::InstructionRef { proc: auxtools_types::ProcRef { @@ -708,15 +708,13 @@ handle_request! { saved.retain(|k| { if !keep.contains(&k) { - if let Ok(Some(offset)) = auxtools.get_offset(k.0.as_str(), k.1 as u32, k.2 as u32) { - let _ = auxtools.unset_breakpoint(&auxtools_types::InstructionRef { - proc: auxtools_types::ProcRef { - path: k.0.clone(), - override_id: k.1 as u32 - }, - offset: offset, - }); - } + let _ = auxtools.unset_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: k.0.clone(), + override_id: k.1 as u32 + }, + offset: k.2 as u32, + }); false } else { true @@ -733,6 +731,8 @@ handle_request! { let inputs = params.breakpoints; let mut breakpoints = Vec::new(); + let saved = self.saved_breakpoints.entry(file_id).or_default(); + let mut keep = HashSet::new(); match &mut self.client { DebugClient::Extools(extools) => { @@ -747,9 +747,6 @@ handle_request! { return Ok(SetFunctionBreakpointsResponse { breakpoints }); }); - let saved = self.saved_breakpoints.entry(file_id).or_default(); - let mut keep = HashSet::new(); - for sbp in inputs { // parse function reference let mut proc = &sbp.name[..]; @@ -793,8 +790,67 @@ handle_request! { SetFunctionBreakpointsResponse { breakpoints } } - DebugClient::Auxtools(_) => { - return Err(Box::new(GenericError("auxtools can't set function breakpoints yet"))); + + DebugClient::Auxtools(auxtools) => { + let mut breakpoints = vec![]; + + for sbp in inputs { + // parse function reference + let mut proc = &sbp.name[..]; + let mut override_id = 0; + if let Some(idx) = sbp.name.find('#') { + proc = &sbp.name[..idx]; + override_id = sbp.name[idx+1..].parse()?; + } + + let offset = 0; + let tup = (proc.to_owned(), override_id, offset); + + saved.insert(tup.clone()); + keep.insert(tup.clone()); + + let result = auxtools.set_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: tup.0, + override_id: override_id as u32 + }, + offset: offset as u32, + })?; + + breakpoints.push(match result { + auxtools_types::BreakpointSetResult::Success { line } => { + Breakpoint { + verified: true, + line: line.map(|x| x as i64), + .. Default::default() + } + }, + + auxtools_types::BreakpointSetResult::Failed => { + Breakpoint { + verified: false, + .. Default::default() + } + } + }); + } + + saved.retain(|k| { + if !keep.contains(&k) { + let _ = auxtools.unset_breakpoint(&auxtools_types::InstructionRef { + proc: auxtools_types::ProcRef { + path: k.0.clone(), + override_id: k.1 as u32 + }, + offset: k.2 as u32, + }); + false + } else { + true + } + }); + + SetFunctionBreakpointsResponse { breakpoints } } } } From 5332f669ba11dacfe890e5a9b14a94b413ec0011 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 22:58:10 +0000 Subject: [PATCH 27/30] tabs to spaces --- src/langserver/debugger/auxtools.rs | 48 +++--- src/langserver/debugger/auxtools_types.rs | 186 +++++++++++----------- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index b553b3f48..1e50ece62 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -14,13 +14,13 @@ use super::dap_types; use super::SequenceNumber; enum StreamState { - // The client is waiting for a Stream to be sent from the thread - Waiting(mpsc::Receiver), + // The client is waiting for a Stream to be sent from the thread + Waiting(mpsc::Receiver), - Connected(TcpStream), + Connected(TcpStream), - // The server has finished being used - Disconnected, + // The server has finished being used + Disconnected, } pub struct Auxtools { @@ -318,28 +318,28 @@ impl Auxtools { } impl AuxtoolsThread { - fn spawn_listener( - self, - listener: TcpListener, - connection_sender: mpsc::Sender, - ) -> JoinHandle<()> { - thread::spawn(move || match listener.accept() { - Ok((stream, _)) => { - match connection_sender.send(stream.try_clone().unwrap()) { - Ok(_) => {} - Err(e) => { - eprintln!("Debug client thread failed to pass cloned TcpStream: {}", e); - return; - } + fn spawn_listener( + self, + listener: TcpListener, + connection_sender: mpsc::Sender, + ) -> JoinHandle<()> { + thread::spawn(move || match listener.accept() { + Ok((stream, _)) => { + match connection_sender.send(stream.try_clone().unwrap()) { + Ok(_) => {} + Err(e) => { + eprintln!("Debug client thread failed to pass cloned TcpStream: {}", e); + return; + } } - self.run(stream); - } + self.run(stream); + } - Err(e) => { - eprintln!("Debug client failed to accept connection: {}", e); - } - }) + Err(e) => { + eprintln!("Debug client failed to accept connection: {}", e); + } + }) } // returns true if we should disconnect diff --git a/src/langserver/debugger/auxtools_types.rs b/src/langserver/debugger/auxtools_types.rs index cd1b231d0..f01ed3ad0 100644 --- a/src/langserver/debugger/auxtools_types.rs +++ b/src/langserver/debugger/auxtools_types.rs @@ -8,130 +8,130 @@ pub const DEFAULT_PORT: u16 = 2448; // Message from client -> server #[derive(Serialize, Deserialize, Debug)] pub enum Request { - Disconnect, - BreakpointSet { - instruction: InstructionRef, - }, - BreakpointUnset { - instruction: InstructionRef, - }, - CatchRuntimes { - should_catch: bool, - }, - LineNumber { - proc: ProcRef, - offset: u32, - }, - Offset { - proc: ProcRef, - line: u32, - }, - Stacks, - StackFrames { - stack_id: u32, - start_frame: Option, - count: Option, - }, - Scopes { - frame_id: u32, - }, - Variables { - vars: VariablesRef, - }, - Continue { - kind: ContinueKind, - }, - Pause, + Disconnect, + BreakpointSet { + instruction: InstructionRef, + }, + BreakpointUnset { + instruction: InstructionRef, + }, + CatchRuntimes { + should_catch: bool, + }, + LineNumber { + proc: ProcRef, + offset: u32, + }, + Offset { + proc: ProcRef, + line: u32, + }, + Stacks, + StackFrames { + stack_id: u32, + start_frame: Option, + count: Option, + }, + Scopes { + frame_id: u32, + }, + Variables { + vars: VariablesRef, + }, + Continue { + kind: ContinueKind, + }, + Pause, } // Message from server -> client #[derive(Serialize, Deserialize, Debug)] pub enum Response { - Ack, - BreakpointSet { - result: BreakpointSetResult, - }, - BreakpointUnset { - success: bool, - }, - LineNumber { - line: Option, - }, - Offset { - offset: Option, - }, - Stacks { - stacks: Vec, - }, - StackFrames { - frames: Vec, - total_count: u32, - }, - Scopes { - arguments: Option, - locals: Option, - globals: Option, - }, - Variables { - vars: Vec, - }, + Ack, + BreakpointSet { + result: BreakpointSetResult, + }, + BreakpointUnset { + success: bool, + }, + LineNumber { + line: Option, + }, + Offset { + offset: Option, + }, + Stacks { + stacks: Vec, + }, + StackFrames { + frames: Vec, + total_count: u32, + }, + Scopes { + arguments: Option, + locals: Option, + globals: Option, + }, + Variables { + vars: Vec, + }, - // These responses can occur at any moment, even between a request and its response - // I guess they aren't really responses... - Disconnect, - Notification { - message: String, - }, - BreakpointHit { - reason: BreakpointReason, - }, + // These responses can occur at any moment, even between a request and its response + // I guess they aren't really responses... + Disconnect, + Notification { + message: String, + }, + BreakpointHit { + reason: BreakpointReason, + }, } #[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)] pub struct ProcRef { - pub path: String, - pub override_id: u32, + pub path: String, + pub override_id: u32, } #[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)] pub struct InstructionRef { - pub proc: ProcRef, - pub offset: u32, + pub proc: ProcRef, + pub offset: u32, } #[derive(Serialize, Deserialize, Debug)] pub enum BreakpointReason { - Breakpoint, - Step, - Pause, - Runtime(String), + Breakpoint, + Step, + Pause, + Runtime(String), } #[derive(Serialize, Deserialize, Debug)] pub enum ContinueKind { - Continue, - StepOver { stack_id: u32 }, - StepInto { stack_id: u32 }, - StepOut { stack_id: u32 }, + Continue, + StepOver { stack_id: u32 }, + StepInto { stack_id: u32 }, + StepOut { stack_id: u32 }, } #[derive(Serialize, Deserialize, Debug)] pub struct Stack { - pub id: u32, - pub name: String, + pub id: u32, + pub name: String, } #[derive(Serialize, Deserialize, Debug)] pub struct StackFrame { - pub id: u32, - pub instruction: InstructionRef, - pub line: Option, + pub id: u32, + pub instruction: InstructionRef, + pub line: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum BreakpointSetResult { - Success { line: Option }, - Failed, + Success { line: Option }, + Failed, } #[derive(Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Debug)] @@ -139,7 +139,7 @@ pub struct VariablesRef(pub i32); #[derive(Serialize, Deserialize, Debug)] pub struct Variable { - pub name: String, - pub value: String, - pub variables: Option, + pub name: String, + pub value: String, + pub variables: Option, } From 05c46783de31369794b4984b46f6a2e703f4bb27 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Thu, 26 Nov 2020 23:09:14 +0000 Subject: [PATCH 28/30] better error output when receiving incorrect response --- src/langserver/debugger/auxtools.rs | 47 +++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 1e50ece62..133194697 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -157,7 +157,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::LineNumber { line } => Ok(line), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("LineNumber", response))), } } @@ -172,7 +172,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Offset { offset } => Ok(offset), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Offset", response))), } } @@ -183,7 +183,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::BreakpointSet { result } => Ok(result), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("BreakpointSet", response))), } } @@ -194,7 +194,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::BreakpointUnset { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("BreakpointUnset", response))), } } @@ -205,7 +205,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Ack { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Ack", response))), } } @@ -216,7 +216,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Ack { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Ack", response))), } } @@ -227,7 +227,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Ack { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Ack", response))), } } @@ -238,7 +238,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Ack { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Ack", response))), } } @@ -247,7 +247,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Ack { .. } => Ok(()), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Ack", response))), } } @@ -256,7 +256,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Stacks { stacks } => Ok(stacks), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Stacks", response))), } } @@ -277,7 +277,7 @@ impl Auxtools { frames, total_count, } => Ok((frames, total_count)), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("StackFrames", response))), } } @@ -293,7 +293,7 @@ impl Auxtools { locals, globals, } => Ok((arguments, locals, globals)), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Scopes", response))), } } @@ -304,7 +304,7 @@ impl Auxtools { match self.read_response_or_disconnect()? { Response::Variables { vars } => Ok(vars), - _ => Err(Box::new(super::GenericError("received incorrect response"))), + response => Err(Box::new(UnexpectedResponse::new("Variables", response))), } } @@ -430,3 +430,24 @@ impl AuxtoolsThread { self.seq.issue_event(dap_types::TerminatedEvent::default()); } } + +#[derive(Debug)] +pub struct UnexpectedResponse(String); + +impl UnexpectedResponse { + fn new(expected: &'static str, received: Response) -> Self { + Self(format!("received unexpected response: expected {}, got {:?}", expected, received)) + } +} + +impl std::error::Error for UnexpectedResponse { + fn description(&self) -> &str { + &self.0 + } +} + +impl std::fmt::Display for UnexpectedResponse { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str(&self.0) + } +} \ No newline at end of file From cb2c0269bb51b9d974a403fa1cbe4fa26a560e75 Mon Sep 17 00:00:00 2001 From: William Wallace Date: Fri, 27 Nov 2020 23:04:06 +0000 Subject: [PATCH 29/30] pass DebugEngine option instead of dm::Context into Debugger:new --- src/langserver/debugger/mod.rs | 20 ++++++++------------ src/langserver/main.rs | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 30b08b44d..b87dcc3f0 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -53,6 +53,7 @@ use self::launched::{Launched, EngineParams}; use crate::jrpc_io; pub fn start_server( + engine: DebugEngine, dreamseeker_exe: String, db: DebugDatabaseBuilder, ) -> std::io::Result<(u16, std::thread::JoinHandle<()>)> { @@ -66,13 +67,8 @@ pub fn start_server( .spawn(move || { let (stream, _) = listener.accept().unwrap(); drop(listener); - let env = dm::detect_environment_default() - .expect("error detecting .dme") - .expect("no .dme found"); - let ctx = dm::Context::default(); - ctx.autodetect_config(&env); let mut input = std::io::BufReader::new(stream.try_clone().unwrap()); - let mut debugger = Debugger::new(ctx, dreamseeker_exe, db, Box::new(stream)); + let mut debugger = Debugger::new(engine, dreamseeker_exe, db, Box::new(stream)); jrpc_io::run_with_read(&mut input, |message| debugger.handle_input(message)); })?; @@ -119,7 +115,7 @@ pub fn debugger_main>(mut args: I) { objtree, extools_dll: None, }; - let mut debugger = Debugger::new(ctx, dreamseeker_exe, db, Box::new(std::io::stdout())); + let mut debugger = Debugger::new(ctx.config().debugger.engine, dreamseeker_exe, db, Box::new(std::io::stdout())); jrpc_io::run_until_stdin_eof(|message| debugger.handle_input(message)); } @@ -239,7 +235,7 @@ enum DebugClient { } struct Debugger { - context: dm::Context, + engine: DebugEngine, dreamseeker_exe: String, extools_dll: Option, db: DebugDatabase, @@ -254,9 +250,9 @@ struct Debugger { } impl Debugger { - fn new(context: dm::Context, dreamseeker_exe: String, mut db: DebugDatabaseBuilder, stream: OutStream) -> Self { + fn new(engine: DebugEngine, dreamseeker_exe: String, mut db: DebugDatabaseBuilder, stream: OutStream) -> Self { Debugger { - context, + engine, dreamseeker_exe, extools_dll: db.extools_dll.take(), db: db.build(), @@ -416,7 +412,7 @@ handle_request! { let debug = !params.base.noDebug.unwrap_or(false); let engine_params = if debug { - Some(match self.context.config().debugger.engine { + Some(match self.engine { DebugEngine::Extools => { let (port, extools) = ExtoolsHolder::listen(self.seq.clone())?; self.client = DebugClient::Extools(extools); @@ -463,7 +459,7 @@ handle_request! { } on AttachVsc(&mut self, params) { - self.client = match self.context.config().debugger.engine { + self.client = match self.engine { DebugEngine::Extools => { DebugClient::Extools(ExtoolsHolder::attach(self.seq.clone(), params.port.unwrap_or(extools::DEFAULT_PORT))?) } diff --git a/src/langserver/main.rs b/src/langserver/main.rs index 15b139e0d..270869d29 100644 --- a/src/langserver/main.rs +++ b/src/langserver/main.rs @@ -1900,7 +1900,7 @@ handle_method_call! { objtree: self.objtree.clone(), extools_dll: self.extools_dll.clone(), }; - let (port, handle) = debugger::start_server(params.dreamseeker_exe, db).map_err(invalid_request)?; + let (port, handle) = debugger::start_server(self.context.config().debugger.engine, params.dreamseeker_exe, db).map_err(invalid_request)?; self.threads.push(handle); extras::StartDebuggerResult { port } } From 7eb3d8c644e7eab7714987a3e1583c8c077b2e1d Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Fri, 27 Nov 2020 16:41:34 -0800 Subject: [PATCH 30/30] Revert dm-langserver name, fix up some whitespace --- src/langserver/Cargo.toml | 2 +- src/langserver/debugger/auxtools.rs | 8 ++++---- src/langserver/debugger/launched.rs | 2 +- src/langserver/debugger/mod.rs | 5 ++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/langserver/Cargo.toml b/src/langserver/Cargo.toml index ccc19f2a8..3b8e0d07a 100644 --- a/src/langserver/Cargo.toml +++ b/src/langserver/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Tad Hardesty "] edition = "2018" [[bin]] -name = "dm_langserver" +name = "dm-langserver" path = "main.rs" [dependencies] diff --git a/src/langserver/debugger/auxtools.rs b/src/langserver/debugger/auxtools.rs index 133194697..a3fad8baa 100644 --- a/src/langserver/debugger/auxtools.rs +++ b/src/langserver/debugger/auxtools.rs @@ -126,7 +126,7 @@ impl Auxtools { } } - fn send_or_disconnect(&mut self, request: Request) -> Result<(), Box> { + fn send_or_disconnect(&mut self, request: Request) -> Result<(), Box> { if let Err(e) = self.send(request) { self.disconnect(); return Err(e); @@ -332,7 +332,7 @@ impl AuxtoolsThread { return; } } - + self.run(stream); } @@ -341,7 +341,7 @@ impl AuxtoolsThread { } }) } - + // returns true if we should disconnect fn handle_response(&mut self, data: &[u8]) -> Result> { let response = bincode::deserialize::(data)?; @@ -450,4 +450,4 @@ impl std::fmt::Display for UnexpectedResponse { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_str(&self.0) } -} \ No newline at end of file +} diff --git a/src/langserver/debugger/launched.rs b/src/langserver/debugger/launched.rs index 67a2b58e9..baf72b872 100644 --- a/src/langserver/debugger/launched.rs +++ b/src/langserver/debugger/launched.rs @@ -34,7 +34,7 @@ pub enum EngineParams { }, Auxtools { port: u16, - dll: Option + dll: Option } } diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index b87dcc3f0..9b40b30b8 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -428,7 +428,7 @@ handle_request! { extools_dll = Some(self::extools_bundle::extract()?); } } - + EngineParams::Extools { port, dll: extools_dll, @@ -506,7 +506,6 @@ handle_request! { DebugClient::Auxtools(_) => {} } - } on Threads(&mut self, ()) { @@ -801,7 +800,7 @@ handle_request! { let offset = 0; let tup = (proc.to_owned(), override_id, offset); - + saved.insert(tup.clone()); keep.insert(tup.clone());