1mod handlers;
2mod state;
3mod util;
4
5use crate::{
6 error::{self, AdapterError, Error},
7 server::{state::ServerState, util::IdGenerator},
8 types::{ExitCode, Instruction},
9};
10use dap::{
11 events::{ExitedEventBody, OutputEventBody, StoppedEventBody},
12 prelude::*,
13 types::StartDebuggingRequestKind,
14};
15use forc_pkg::{
16 manifest::GenericManifestFile,
17 source::IPFSNode,
18 {self, BuildProfile, Built, BuiltPackage, PackageManifestFile},
19};
20use forc_test::{
21 execute::{DebugResult, TestExecutor},
22 setup::TestSetup,
23 BuiltTests,
24};
25use fuel_tx::GasCostsValues;
26use serde::{Deserialize, Serialize};
27use std::{
28 io::{BufReader, BufWriter, Read, Write},
29 process,
30 sync::Arc,
31};
32use sway_core::BuildTarget;
33
34pub const THREAD_ID: i64 = 0;
35pub const REGISTERS_VARIABLE_REF: i64 = 1;
36pub const INSTRUCTIONS_VARIABLE_REF: i64 = 2;
37
38#[derive(Debug, Clone, Default, Serialize, Deserialize)]
39pub struct AdditionalData {
40 pub program: String,
41}
42
43pub struct DapServer {
50 server: Server<Box<dyn Read>, Box<dyn Write>>,
52 breakpoint_id_gen: IdGenerator,
54 pub state: ServerState,
56}
57
58impl Default for DapServer {
59 fn default() -> Self {
60 Self::new(Box::new(std::io::stdin()), Box::new(std::io::stdout()))
61 }
62}
63
64impl DapServer {
65 pub fn new(input: Box<dyn Read>, output: Box<dyn Write>) -> Self {
71 let server = Server::new(BufReader::new(input), BufWriter::new(output));
72 DapServer {
73 server,
74 state: ServerState::default(),
75 breakpoint_id_gen: IdGenerator::default(),
76 }
77 }
78
79 pub fn start(&mut self) -> error::Result<()> {
81 loop {
82 let req = match self.server.poll_request()? {
83 Some(req) => req,
84 None => return Err(Error::AdapterError(AdapterError::MissingCommand)),
85 };
86
87 let response = self.handle_request(req)?;
89 self.server.respond(response)?;
90
91 if !self.state.initialized_event_sent {
93 let _ = self.server.send_event(Event::Initialized);
94 self.state.initialized_event_sent = true;
95 }
96
97 if self.should_launch() {
99 self.state.started_debugging = true;
100 match self.launch() {
101 Ok(true) => continue,
102 Ok(false) => self.exit(0), Err(e) => {
104 self.error(format!("Launch error: {e:?}"));
105 self.exit(1);
106 }
107 }
108 }
109 }
110 }
111
112 fn handle_request(&mut self, req: Request) -> error::Result<Response> {
114 let (result, exit_code) = self.handle_command(&req.command).into_tuple();
115 let response = match result {
116 Ok(rsp) => Ok(req.success(rsp)),
117 Err(e) => {
118 self.error(format!("{e:?}"));
119 Ok(req.error(&format!("{e:?}")))
120 }
121 };
122 if let Some(exit_code) = exit_code {
123 self.exit(exit_code);
124 }
125 response
126 }
127
128 pub fn handle_command(&mut self, command: &Command) -> HandlerResult {
130 match command {
131 Command::Attach(_) => self.handle_attach(),
132 Command::BreakpointLocations(ref args) => {
133 self.handle_breakpoint_locations_command(args)
134 }
135 Command::ConfigurationDone => self.handle_configuration_done(),
136 Command::Continue(_) => self.handle_continue(),
137 Command::Disconnect(_) => HandlerResult::ok_with_exit(ResponseBody::Disconnect, 0),
138 Command::Evaluate(args) => self.handle_evaluate(args),
139 Command::Initialize(_) => self.handle_initialize(),
140 Command::Launch(ref args) => self.handle_launch(args),
141 Command::Next(_) => self.handle_next(),
142 Command::Pause(_) => self.handle_pause(),
143 Command::Restart(_) => self.handle_restart(),
144 Command::Scopes(_) => self.handle_scopes(),
145 Command::SetBreakpoints(ref args) => self.handle_set_breakpoints_command(args),
146 Command::StackTrace(_) => self.handle_stack_trace_command(),
147 Command::StepIn(_) => {
148 self.error("This feature is not currently supported.".into());
149 HandlerResult::ok(ResponseBody::StepIn)
150 }
151 Command::StepOut(_) => {
152 self.error("This feature is not currently supported.".into());
153 HandlerResult::ok(ResponseBody::StepOut)
154 }
155 Command::Terminate(_) => HandlerResult::ok_with_exit(ResponseBody::Terminate, 0),
156 Command::TerminateThreads(_) => {
157 HandlerResult::ok_with_exit(ResponseBody::TerminateThreads, 0)
158 }
159 Command::Threads => self.handle_threads(),
160 Command::Variables(ref args) => self.handle_variables_command(args),
161 _ => HandlerResult::err(AdapterError::UnhandledCommand {
162 command: command.clone(),
163 }),
164 }
165 }
166
167 fn should_launch(&self) -> bool {
169 self.state.configuration_done
170 && !self.state.started_debugging
171 && matches!(self.state.mode, Some(StartDebuggingRequestKind::Launch))
172 }
173
174 fn log(&mut self, output: String) {
176 let _ = self.server.send_event(Event::Output(OutputEventBody {
177 output,
178 ..Default::default()
179 }));
180 }
181
182 fn error(&mut self, output: String) {
184 let _ = self.server.send_event(Event::Output(OutputEventBody {
185 output,
186 category: Some(types::OutputEventCategory::Stderr),
187 ..Default::default()
188 }));
189 }
190
191 fn log_test_results(&mut self) {
193 if !self.state.executors.is_empty() {
194 return;
195 }
196 let test_results = &self.state.test_results;
197 let test_lines = test_results
198 .iter()
199 .map(|r| {
200 let outcome = if r.passed() { "ok" } else { "failed" };
201 format!(
202 "test {} ... {} ({}ms, {} gas)",
203 r.name,
204 outcome,
205 r.duration.as_millis(),
206 r.gas_used
207 )
208 })
209 .collect::<Vec<_>>()
210 .join("\n");
211
212 let passed = test_results.iter().filter(|r| r.passed()).count();
213 let final_outcome = if passed == test_results.len() {
214 "OK"
215 } else {
216 "FAILED"
217 };
218
219 self.log(format!(
220 "{test_lines}\nResult: {final_outcome}. {passed} passed. {} failed.\n",
221 test_results.len() - passed
222 ));
223 }
224
225 pub fn launch(&mut self) -> Result<bool, AdapterError> {
227 let (pkg_to_debug, test_setup) = self.build_tests()?;
229 let entries = pkg_to_debug.bytecode.entries.iter().filter_map(|entry| {
230 if let Some(test_entry) = entry.kind.test() {
231 return Some((entry, test_entry));
232 }
233 None
234 });
235
236 let executors: Vec<TestExecutor> = entries
238 .filter_map(|(entry, test_entry)| {
239 let offset = u32::try_from(entry.finalized.imm)
240 .expect("test instruction offset out of range");
241 let name = entry.finalized.fn_name.clone();
242 if test_entry.file_path.as_path() != self.state.program_path.as_path() {
243 return None;
244 }
245
246 TestExecutor::build(
247 &pkg_to_debug.bytecode.bytes,
248 offset,
249 test_setup.clone(),
250 test_entry,
251 name.clone(),
252 GasCostsValues::default(),
255 )
256 .ok()
257 })
258 .collect();
259 self.state.init_executors(executors);
260
261 self.start_debugging_tests(false)
263 }
264
265 pub fn build_tests(&mut self) -> Result<(BuiltPackage, TestSetup), AdapterError> {
267 if let Some(pkg) = &self.state.built_package {
268 if let Some(setup) = &self.state.test_setup {
269 return Ok((pkg.clone(), setup.clone()));
270 }
271 }
272
273 let manifest_file = forc_pkg::manifest::ManifestFile::from_dir(&self.state.program_path)
275 .map_err(|err| AdapterError::BuildFailed {
276 reason: format!("read manifest file: {err:?}"),
277 })?;
278 let pkg_manifest: PackageManifestFile =
279 manifest_file
280 .clone()
281 .try_into()
282 .map_err(|err: anyhow::Error| AdapterError::BuildFailed {
283 reason: format!("package manifest: {err:?}"),
284 })?;
285 let member_manifests =
286 manifest_file
287 .member_manifests()
288 .map_err(|err| AdapterError::BuildFailed {
289 reason: format!("member manifests: {err:?}"),
290 })?;
291 let lock_path = manifest_file
292 .lock_path()
293 .map_err(|err| AdapterError::BuildFailed {
294 reason: format!("lock path: {err:?}"),
295 })?;
296 let build_plan = forc_pkg::BuildPlan::from_lock_and_manifests(
297 &lock_path,
298 &member_manifests,
299 false,
300 false,
301 &IPFSNode::default(),
302 )
303 .map_err(|err| AdapterError::BuildFailed {
304 reason: format!("build plan: {err:?}"),
305 })?;
306
307 let project_name = pkg_manifest.project_name();
308
309 let outputs = std::iter::once(build_plan.find_member_index(project_name).ok_or(
310 AdapterError::BuildFailed {
311 reason: format!("find built project: {project_name}"),
312 },
313 )?)
314 .collect();
315
316 let built_packages = forc_pkg::build(
317 &build_plan,
318 BuildTarget::default(),
319 &BuildProfile {
320 optimization_level: sway_core::OptLevel::Opt0,
321 include_tests: true,
322 ..Default::default()
323 },
324 &outputs,
325 &[],
326 &[],
327 None,
328 )
329 .map_err(|err| AdapterError::BuildFailed {
330 reason: format!("build packages: {err:?}"),
331 })?;
332
333 let pkg_to_debug = built_packages
335 .iter()
336 .find(|(_, pkg)| pkg.descriptor.manifest_file == pkg_manifest)
337 .map(|(_, pkg)| pkg)
338 .ok_or(AdapterError::BuildFailed {
339 reason: format!("find package: {project_name}"),
340 })?;
341
342 self.state.source_map = pkg_to_debug.source_map.clone();
343
344 let built = Built::Package(Arc::from(pkg_to_debug.clone()));
346
347 let built_tests = BuiltTests::from_built(built, &build_plan).map_err(|err| {
348 AdapterError::BuildFailed {
349 reason: format!("build tests: {err:?}"),
350 }
351 })?;
352
353 let pkg_tests = match built_tests {
354 BuiltTests::Package(pkg_tests) => pkg_tests,
355 BuiltTests::Workspace(_) => {
356 return Err(AdapterError::BuildFailed {
357 reason: "package tests: workspace tests not supported".into(),
358 })
359 }
360 };
361 let test_setup = pkg_tests.setup().map_err(|err| AdapterError::BuildFailed {
362 reason: format!("test setup: {err:?}"),
363 })?;
364 self.state.built_package = Some(pkg_to_debug.clone());
365 self.state.test_setup = Some(test_setup.clone());
366 Ok((pkg_to_debug.clone(), test_setup))
367 }
368
369 fn exit(&mut self, exit_code: i64) {
371 let _ = self
372 .server
373 .send_event(Event::Exited(ExitedEventBody { exit_code }));
374 process::exit(exit_code as i32);
375 }
376
377 fn stop(&mut self, pc: Instruction) -> Result<bool, AdapterError> {
378 let (hit_breakpoint_ids, reason) =
379 if let Ok(breakpoint_id) = self.state.vm_pc_to_breakpoint_id(pc) {
380 self.state.stopped_on_breakpoint_id = Some(breakpoint_id);
381 (
382 Some(vec![breakpoint_id]),
383 types::StoppedEventReason::Breakpoint,
384 )
385 } else {
386 self.state.stopped_on_breakpoint_id = None;
387 (None, types::StoppedEventReason::Step)
388 };
389
390 let _ = self.server.send_event(Event::Stopped(StoppedEventBody {
391 reason,
392 hit_breakpoint_ids,
393 description: None,
394 thread_id: Some(THREAD_ID),
395 preserve_focus_hint: None,
396 text: None,
397 all_threads_stopped: None,
398 }));
399 Ok(true)
400 }
401
402 fn start_debugging_tests(&mut self, single_stepping: bool) -> Result<bool, AdapterError> {
407 self.state.update_vm_breakpoints();
408
409 while let Some(executor) = self.state.executors.first_mut() {
410 executor.interpreter.set_single_stepping(single_stepping);
411 match executor.start_debugging()? {
412 DebugResult::TestComplete(result) => {
413 self.state.test_complete(result);
414 }
415 DebugResult::Breakpoint(pc) => {
416 executor.interpreter.set_single_stepping(false);
417 return self.stop(pc);
418 }
419 };
420 }
421 self.log_test_results();
422 Ok(false)
423 }
424
425 fn continue_debugging_tests(&mut self, single_stepping: bool) -> Result<bool, AdapterError> {
430 self.state.update_vm_breakpoints();
431
432 if let Some(executor) = self.state.executors.first_mut() {
433 executor.interpreter.set_single_stepping(single_stepping);
434 match executor.continue_debugging()? {
435 DebugResult::TestComplete(result) => {
436 self.state.test_complete(result);
437 return self.start_debugging_tests(single_stepping);
440 }
441 DebugResult::Breakpoint(pc) => {
442 executor.interpreter.set_single_stepping(false);
443 return self.stop(pc);
444 }
445 }
446 }
447 self.log_test_results();
448 Ok(false)
449 }
450}
451
452#[derive(Debug)]
454pub struct HandlerResult {
455 response: Result<ResponseBody, AdapterError>,
456 exit_code: Option<ExitCode>,
457}
458
459impl HandlerResult {
460 pub fn ok(response: ResponseBody) -> Self {
462 Self {
463 response: Ok(response),
464 exit_code: None,
465 }
466 }
467
468 pub fn ok_with_exit(response: ResponseBody, code: ExitCode) -> Self {
470 Self {
471 response: Ok(response),
472 exit_code: Some(code),
473 }
474 }
475
476 pub fn err_with_exit(error: AdapterError, code: ExitCode) -> Self {
478 Self {
479 response: Err(error),
480 exit_code: Some(code),
481 }
482 }
483
484 pub fn err(error: AdapterError) -> Self {
486 Self {
487 response: Err(error),
488 exit_code: None,
489 }
490 }
491
492 pub fn into_tuple(self) -> (Result<ResponseBody, AdapterError>, Option<ExitCode>) {
494 (self.response, self.exit_code)
495 }
496}