Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

Commit

Permalink
feat: dap wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
0xtekgrinder committed Mar 5, 2024
1 parent d351ad9 commit e49a563
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 4 deletions.
1 change: 1 addition & 0 deletions toolchains/solidity/core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion toolchains/solidity/core/crates/foundry-debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ exclude.workspace = true
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
dap = "0.4.1-alpha1"
foundry-evm-core = { git = "https://github.com/foundry-rs/foundry.git"}
foundry-evm-core = { git = "https://github.com/foundry-rs/foundry.git"}
thiserror = "1.0.57"
210 changes: 210 additions & 0 deletions toolchains/solidity/core/crates/foundry-debugger/src/dap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use std::io::{Read, Write};

use dap::errors::ServerError;
use dap::prelude::Event;
use dap::requests::{Command, Request};
use dap::responses::{
ContinueResponse, DisassembleResponse, ResponseBody, ScopesResponse, SetBreakpointsResponse,
SetExceptionBreakpointsResponse, SetInstructionBreakpointsResponse, StackTraceResponse,
ThreadsResponse,
};
use dap::server::Server;
use dap::types::{
SteppingGranularity, Thread,
};

pub struct DapSession<R: Read, W: Write> {
server: Server<R, W>,
running: bool,
next_breakpoint_id: i64,
}

impl<'a, R: Read, W: Write> DapSession<R, W> {
pub fn new(
server: Server<R, W>,
) -> Self {
Self {
server,
running: false,
next_breakpoint_id: 1,
}
}

pub fn run_loop(&mut self) -> Result<(), ServerError> {
self.running = true;

self.server.send_event(Event::Initialized)?;

while self.running {
let req = match self.server.poll_request()? {
Some(req) => req,
None => break,
};
match req.command {
Command::Disconnect(_) => {
eprintln!("INFO: ending debugging session");
self.server.respond(req.ack()?)?;
break;
}
Command::SetBreakpoints(_) => {
self.handle_set_source_breakpoints(req)?;
}
Command::SetExceptionBreakpoints(_) => {
self.handle_set_exceptions_breakpoints(req)?;
}
Command::SetInstructionBreakpoints(_) => {
self.handle_set_instruction_breakpoints(req)?;
}
Command::Threads => {
self.server.respond(req.success(ResponseBody::Threads(ThreadsResponse {
threads: vec![Thread { id: 0, name: "main".to_string() }],
})))?;
}
Command::StackTrace(_) => {
self.handle_stack_trace(req)?;
}
Command::Disassemble(_) => {
self.handle_disassemble(req)?;
}
Command::StepIn(ref args) => {
let granularity =
args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement);
match granularity {
SteppingGranularity::Instruction => self.handle_step(req)?,
_ => self.handle_next(req)?,
}
}
Command::StepOut(ref args) => {
let granularity =
args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement);
match granularity {
SteppingGranularity::Instruction => self.handle_step(req)?,
_ => self.handle_next(req)?,
}
}
Command::Next(ref args) => {
let granularity =
args.granularity.as_ref().unwrap_or(&SteppingGranularity::Statement);
match granularity {
SteppingGranularity::Instruction => self.handle_step(req)?,
_ => self.handle_next(req)?,
}
}
Command::Continue(_) => {
self.handle_continue(req)?;
}
Command::Scopes(_) => {
let scopes = vec![];

self.server.respond(
req.success(ResponseBody::Scopes(ScopesResponse { scopes })),
)?;
}
_ => {
eprintln!("ERROR: unhandled command: {:?}", req.command);
}
}
}
Ok(())
}

fn handle_stack_trace(&mut self, req: Request) -> Result<(), ServerError> {
let stack_frames = vec![];
let total_frames = Some(stack_frames.len() as i64);
self.server.respond(req.success(ResponseBody::StackTrace(StackTraceResponse {
stack_frames,
total_frames,
})))?;
Ok(())
}

fn handle_scopes(&mut self, req: Request) -> Result<(), ServerError> {
let scopes = vec![];
self.server.respond(
req.success(ResponseBody::Scopes(ScopesResponse { scopes })),
)?;
Ok(())
}

fn handle_set_exceptions_breakpoints(&mut self, req: Request) -> Result<(), ServerError> {
self.server.respond(req.success(ResponseBody::SetExceptionBreakpoints(
SetExceptionBreakpointsResponse { breakpoints: None },
)))?;
Ok(())
}

fn handle_disassemble(&mut self, req: Request) -> Result<(), ServerError> {
let Command::Disassemble(ref args) = req.command else {
unreachable!("handle_disassemble called on a non disassemble request");
};

let instructions = vec![];

self.server.respond(
req.success(ResponseBody::Disassemble(DisassembleResponse { instructions })),
)?;
Ok(())
}

fn handle_step(&mut self, req: Request) -> Result<(), ServerError> {
eprintln!("INFO: stepped by instruction");
self.server.respond(req.ack()?)?;
Ok(())
}

fn handle_next(&mut self, req: Request) -> Result<(), ServerError> {
eprintln!("INFO: stepped by statement");
self.server.respond(req.ack()?)?;
Ok(())
}

fn handle_continue(&mut self, req: Request) -> Result<(), ServerError> {
eprintln!("INFO: continue");
self.server.respond(req.success(ResponseBody::Continue(ContinueResponse {
all_threads_continued: Some(true),
})))?;
Ok(())
}

fn get_next_breakpoint_id(&mut self) -> i64 {
let id = self.next_breakpoint_id;
self.next_breakpoint_id += 1;
id
}

fn handle_set_instruction_breakpoints(&mut self, req: Request) -> Result<(), ServerError> {
let Command::SetInstructionBreakpoints(ref args) = req.command else {
unreachable!("handle_set_instruction_breakpoints called on a different request");
};

let breakpoints = vec![];

// response to request
self.server.respond(req.success(ResponseBody::SetInstructionBreakpoints(
SetInstructionBreakpointsResponse { breakpoints },
)))?;
Ok(())
}

fn handle_set_source_breakpoints(&mut self, req: Request) -> Result<(), ServerError> {
let Command::SetBreakpoints(ref args) = req.command else {
unreachable!("handle_set_source_breakpoints called on a different request");
};

let breakpoints = vec![];

self.server.respond(
req.success(ResponseBody::SetBreakpoints(SetBreakpointsResponse { breakpoints })),
)?;
Ok(())
}
}

pub fn run_session<R: Read, W: Write>(
server: Server<R, W>,
) -> Result<(), ServerError> {
let mut session =
DapSession::new(server);

session.run_loop()
}
43 changes: 40 additions & 3 deletions toolchains/solidity/core/crates/foundry-debugger/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
fn main() {
println!("Hello, world!");
}
use std::io::{BufReader, BufWriter};
use ::dap::server::Server;
use ::dap::requests::Command;
use ::dap::responses::ResponseBody;

use thiserror::Error;

mod dap;

#[derive(Error, Debug)]
enum MyAdapterError {
#[error("Unhandled command")]
UnhandledCommandError,

#[error("Missing command")]
MissingCommandError,
}

type DynResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

fn main() -> DynResult<()> {
let output = BufWriter::new(std::io::stdout());
let input = BufReader::new(std::io::stdin());
let mut server = Server::new(input, output);

// TODO handle launch command once we know what to send
let req = match server.poll_request()? {
Some(req) => req,
None => return Err(Box::new(MyAdapterError::MissingCommandError)),
};
if let Command::Initialize(_) = req.command {
let rsp = req.success(ResponseBody::Initialize(Default::default()));

server.respond(rsp)?;
let _ = dap::run_session(server);
Ok(())
} else {
Err(Box::new(MyAdapterError::UnhandledCommandError))
}
}

0 comments on commit e49a563

Please sign in to comment.