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