1use alloc::{string::String, sync::Arc, vec::Vec};
4
5use miden_core::{
6 Word,
7 mast::MastNodeId,
8 serde::{
9 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
10 read_bounded_len,
11 },
12};
13use miden_debug_types::{ByteIndex, ColumnNumber, LineNumber, Location, Uri};
14
15use super::{
16 DEBUG_ERROR_MESSAGES_VERSION, DEBUG_FUNCTIONS_VERSION, DEBUG_SOURCE_GRAPH_VERSION,
17 DEBUG_SOURCE_MAP_VERSION, DEBUG_SOURCES_VERSION, DEBUG_TYPES_VERSION, DebugErrorMessage,
18 DebugErrorMessagesSection, DebugFieldInfo, DebugFileInfo, DebugFunctionInfo,
19 DebugFunctionsSection, DebugPrimitiveType, DebugSourceAsmOp, DebugSourceGraphSection,
20 DebugSourceInlineCall, DebugSourceMapSection, DebugSourceNode, DebugSourceNodeId,
21 DebugSourceVar, DebugSourcesSection, DebugTypeIdx, DebugTypeInfo, DebugTypesSection,
22 DebugVariantInfo, PackageDebugInfo,
23};
24
25impl Serializable for PackageDebugInfo {
29 fn write_into<W: ByteWriter>(&self, target: &mut W) {
30 target.write_bool(self.types.is_some());
31 if let Some(types) = self.types.as_ref() {
32 types.write_into(target);
33 }
34 target.write_bool(self.sources.is_some());
35 if let Some(sources) = self.sources.as_ref() {
36 sources.write_into(target);
37 }
38 target.write_bool(self.functions.is_some());
39 if let Some(functions) = self.functions.as_ref() {
40 functions.write_into(target);
41 }
42 target.write_bool(self.source_graph.is_some());
43 if let Some(source_graph) = self.source_graph.as_ref() {
44 source_graph.write_into(target);
45 }
46 target.write_bool(self.source_map.is_some());
47 if let Some(source_map) = self.source_map.as_ref() {
48 source_map.write_into(target);
49 }
50 target.write_bool(self.error_messages.is_some());
51 if let Some(error_messages) = self.error_messages.as_ref() {
52 error_messages.write_into(target);
53 }
54 }
55}
56
57impl Deserializable for PackageDebugInfo {
58 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
59 let types = if source.read_bool()? {
60 Some(DebugTypesSection::read_from(source)?)
61 } else {
62 None
63 };
64 let sources = if source.read_bool()? {
65 Some(DebugSourcesSection::read_from(source)?)
66 } else {
67 None
68 };
69 let functions = if source.read_bool()? {
70 Some(DebugFunctionsSection::read_from(source)?)
71 } else {
72 None
73 };
74 let source_graph = if source.read_bool()? {
75 Some(DebugSourceGraphSection::read_from(source)?)
76 } else {
77 None
78 };
79 let source_map = if source.read_bool()? {
80 Some(DebugSourceMapSection::read_from(source)?)
81 } else {
82 None
83 };
84 let error_messages = if source.read_bool()? {
85 Some(DebugErrorMessagesSection::read_from(source)?)
86 } else {
87 None
88 };
89 Ok(Self {
90 types,
91 sources,
92 functions,
93 source_graph,
94 source_map,
95 error_messages,
96 })
97 }
98}
99
100impl Serializable for DebugTypesSection {
104 fn write_into<W: ByteWriter>(&self, target: &mut W) {
105 target.write_u8(self.version);
106
107 target.write_usize(self.strings.len());
109 for s in &self.strings {
110 s.as_ref().write_into(target);
111 }
112
113 target.write_usize(self.types.len());
115 for ty in &self.types {
116 ty.write_into(target);
117 }
118 }
119}
120
121impl Deserializable for DebugTypesSection {
122 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
123 let version = source.read_u8()?;
124 if version != DEBUG_TYPES_VERSION {
125 return Err(DeserializationError::InvalidValue(alloc::format!(
126 "unsupported debug_types version: {version}, expected {DEBUG_TYPES_VERSION}"
127 )));
128 }
129
130 let strings_len = read_bounded_len(source, "debug_types strings", 1)?;
134 let mut strings = Vec::with_capacity(strings_len);
135 for _ in 0..strings_len {
136 strings.push(read_string(source)?);
137 }
138
139 let types_len = read_bounded_len(source, "debug_types types", 1)?;
140 let types = source.read_many_iter(types_len)?.collect::<Result<_, _>>()?;
141
142 Ok(Self { version, strings, types })
143 }
144}
145
146impl Serializable for DebugSourcesSection {
150 fn write_into<W: ByteWriter>(&self, target: &mut W) {
151 target.write_u8(self.version);
152
153 target.write_usize(self.strings.len());
155 for s in &self.strings {
156 s.as_ref().write_into(target);
157 }
158
159 target.write_usize(self.files.len());
161 for file in &self.files {
162 file.write_into(target);
163 }
164 }
165}
166
167impl Deserializable for DebugSourcesSection {
168 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
169 let version = source.read_u8()?;
170 if version != DEBUG_SOURCES_VERSION {
171 return Err(DeserializationError::InvalidValue(alloc::format!(
172 "unsupported debug_sources version: {version}, expected {DEBUG_SOURCES_VERSION}"
173 )));
174 }
175
176 let strings_len = read_bounded_len(source, "debug_sources strings", 1)?;
180 let mut strings = Vec::with_capacity(strings_len);
181 for _ in 0..strings_len {
182 strings.push(read_string(source)?);
183 }
184
185 let files_len = read_bounded_len(source, "debug_sources files", 1)?;
186 let files = source.read_many_iter(files_len)?.collect::<Result<_, _>>()?;
187
188 Ok(Self { version, strings, files })
189 }
190}
191
192impl Serializable for DebugFunctionsSection {
196 fn write_into<W: ByteWriter>(&self, target: &mut W) {
197 target.write_u8(self.version);
198
199 target.write_usize(self.strings.len());
201 for s in &self.strings {
202 s.as_ref().write_into(target);
203 }
204
205 target.write_usize(self.functions.len());
207 for func in &self.functions {
208 func.write_into(target);
209 }
210 }
211}
212
213impl Deserializable for DebugFunctionsSection {
214 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
215 let version = source.read_u8()?;
216 if version != DEBUG_FUNCTIONS_VERSION {
217 return Err(DeserializationError::InvalidValue(alloc::format!(
218 "unsupported debug_functions version: {version}, expected {DEBUG_FUNCTIONS_VERSION}"
219 )));
220 }
221
222 let strings_len = read_bounded_len(source, "debug_functions strings", 1)?;
226 let mut strings = Vec::with_capacity(strings_len);
227 for _ in 0..strings_len {
228 strings.push(read_string(source)?);
229 }
230
231 let functions_len = read_bounded_len(source, "debug_functions functions", 1)?;
232 let functions = source.read_many_iter(functions_len)?.collect::<Result<_, _>>()?;
233
234 Ok(Self { version, strings, functions })
235 }
236}
237
238impl Serializable for DebugSourceNode {
242 fn write_into<W: ByteWriter>(&self, target: &mut W) {
243 target.write_u32(self.exec_node.into());
244 self.children.write_into(target);
245 target.write_u32(self.op_start);
246 target.write_u32(self.op_end);
247 }
248}
249
250impl Deserializable for DebugSourceNode {
251 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
252 Ok(Self {
253 exec_node: MastNodeId::new_unchecked(source.read_u32()?),
254 children: read_debug_source_node_ids(source, "debug_source_node children")?,
255 op_start: source.read_u32()?,
256 op_end: source.read_u32()?,
257 })
258 }
259
260 fn min_serialized_size() -> usize {
261 12 + Vec::<DebugSourceNodeId>::min_serialized_size()
262 }
263}
264
265impl Serializable for DebugSourceGraphSection {
266 fn write_into<W: ByteWriter>(&self, target: &mut W) {
267 target.write_u8(self.version());
268 self.nodes().write_into(target);
269 self.roots().write_into(target);
270 }
271}
272
273impl Deserializable for DebugSourceGraphSection {
274 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
275 let version = source.read_u8()?;
276 if version != DEBUG_SOURCE_GRAPH_VERSION {
277 return Err(DeserializationError::InvalidValue(alloc::format!(
278 "unsupported debug_source_graph version: {version}, expected {DEBUG_SOURCE_GRAPH_VERSION}"
279 )));
280 }
281
282 let nodes_len = read_bounded_len(source, "debug_source_graph nodes", 1)?;
283 let nodes = source.read_many_iter(nodes_len)?.collect::<Result<_, _>>()?;
284 let roots = read_debug_source_node_ids(source, "debug_source_graph roots")?;
285 Ok(Self::from_parts(nodes, roots))
286 }
287}
288
289impl Serializable for DebugSourceAsmOp {
293 fn write_into<W: ByteWriter>(&self, target: &mut W) {
294 self.source_node.write_into(target);
295 target.write_u32(self.op_idx);
296 write_location(&self.location, target);
297 self.context_name.write_into(target);
298 self.op.write_into(target);
299 target.write_u8(self.num_cycles);
300 }
301}
302
303impl Deserializable for DebugSourceAsmOp {
304 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
305 let source_node = DebugSourceNodeId::read_from(source)?;
306 let op_idx = source.read_u32()?;
307 let location = read_location(source)?;
308 let context_name = String::read_from(source)?;
309 let op = String::read_from(source)?;
310 let num_cycles = source.read_u8()?;
311 Ok(Self {
312 source_node,
313 op_idx,
314 location,
315 context_name,
316 op,
317 num_cycles,
318 })
319 }
320
321 fn min_serialized_size() -> usize {
322 DebugSourceNodeId::min_serialized_size() + 4 + 1 + 1 + 1 + 1
323 }
324}
325
326impl Serializable for DebugSourceVar {
327 fn write_into<W: ByteWriter>(&self, target: &mut W) {
328 self.source_node.write_into(target);
329 target.write_u32(self.op_idx);
330 self.var.write_into(target);
331 }
332}
333
334impl Deserializable for DebugSourceVar {
335 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
336 Ok(Self {
337 source_node: DebugSourceNodeId::read_from(source)?,
338 op_idx: source.read_u32()?,
339 var: Deserializable::read_from(source)?,
340 })
341 }
342
343 fn min_serialized_size() -> usize {
344 DebugSourceNodeId::min_serialized_size() + 4
345 }
346}
347
348impl Serializable for DebugSourceInlineCall {
349 fn write_into<W: ByteWriter>(&self, target: &mut W) {
350 self.source_node.write_into(target);
351 target.write_u32(self.op_idx);
352 target.write_u32(self.callee_idx);
353 target.write_u32(self.file_idx);
354 target.write_u32(self.line.to_u32());
355 target.write_u32(self.column.to_u32());
356 }
357}
358
359impl Deserializable for DebugSourceInlineCall {
360 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
361 let source_node = DebugSourceNodeId::read_from(source)?;
362 let op_idx = source.read_u32()?;
363 let callee_idx = source.read_u32()?;
364 let file_idx = source.read_u32()?;
365 let line_raw = source.read_u32()?;
366 let column_raw = source.read_u32()?;
367 let line = LineNumber::new(line_raw).ok_or_else(|| {
368 DeserializationError::InvalidValue(alloc::format!(
369 "debug source inline call line {line_raw} is invalid"
370 ))
371 })?;
372 let column = ColumnNumber::new(column_raw).ok_or_else(|| {
373 DeserializationError::InvalidValue(alloc::format!(
374 "debug source inline call column {column_raw} is invalid"
375 ))
376 })?;
377 Ok(Self::new(source_node, op_idx, callee_idx, file_idx, line, column))
378 }
379
380 fn min_serialized_size() -> usize {
381 DebugSourceNodeId::min_serialized_size() + 20
382 }
383}
384
385impl Serializable for DebugSourceMapSection {
386 fn write_into<W: ByteWriter>(&self, target: &mut W) {
387 target.write_u8(self.version());
388 target.write_usize(self.locations().len());
389 for location in self.locations() {
390 write_required_location(location, target);
391 }
392
393 target.write_usize(self.strings().len());
394 for string in self.strings() {
395 string.write_into(target);
396 }
397
398 target.write_usize(self.asm_ops().len());
399 for asm_op in self.asm_ops() {
400 write_source_asm_op(asm_op, self.locations(), self.strings(), target);
401 }
402
403 self.debug_vars().write_into(target);
404 self.inline_calls().write_into(target);
405 }
406}
407
408impl Deserializable for DebugSourceMapSection {
409 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
410 let version = source.read_u8()?;
411 if version != DEBUG_SOURCE_MAP_VERSION {
412 return Err(DeserializationError::InvalidValue(alloc::format!(
413 "unsupported debug_source_map version: {version}, expected {DEBUG_SOURCE_MAP_VERSION}"
414 )));
415 }
416
417 let locations_len = read_bounded_len(
418 source,
419 "debug_source_map locations",
420 MIN_REQUIRED_LOCATION_SERIALIZED_SIZE,
421 )?;
422 let mut locations = Vec::with_capacity(locations_len);
423 for _ in 0..locations_len {
424 locations.push(read_required_location(source)?);
425 }
426
427 let strings_len = read_bounded_len(source, "debug_source_map strings", 1)?;
428 let mut strings = Vec::with_capacity(strings_len);
429 for _ in 0..strings_len {
430 strings.push(read_owned_string(source)?);
431 }
432
433 let asm_ops_len = read_bounded_len(
434 source,
435 "debug_source_map asm ops",
436 min_source_map_asm_op_row_serialized_size(),
437 )?;
438 let mut asm_ops = Vec::with_capacity(asm_ops_len);
439 for _ in 0..asm_ops_len {
440 asm_ops.push(read_source_asm_op(source, &locations, &strings)?);
441 }
442
443 let debug_vars_len = read_bounded_len(source, "debug_source_map debug vars", 1)?;
444 let debug_vars = source.read_many_iter(debug_vars_len)?.collect::<Result<_, _>>()?;
445 let inline_calls_len = read_bounded_len(source, "debug_source_map inline calls", 1)?;
446 let inline_calls = source.read_many_iter(inline_calls_len)?.collect::<Result<_, _>>()?;
447 Ok(Self::from_parts_with_inline_calls(asm_ops, debug_vars, inline_calls))
448 }
449}
450
451fn write_source_asm_op<W: ByteWriter>(
452 asm_op: &DebugSourceAsmOp,
453 locations: &[Location],
454 strings: &[String],
455 target: &mut W,
456) {
457 asm_op.source_node.write_into(target);
458 target.write_u32(asm_op.op_idx);
459 if let Some(location) = asm_op.location.as_ref() {
460 target.write_bool(true);
461 let location_idx = locations
462 .iter()
463 .position(|candidate| candidate == location)
464 .expect("debug source map location table should contain every row location");
465 target.write_u32(location_idx as u32);
466 } else {
467 target.write_bool(false);
468 }
469 write_source_map_string_ref(&asm_op.context_name, strings, target);
470 write_source_map_string_ref(&asm_op.op, strings, target);
471 target.write_u8(asm_op.num_cycles);
472}
473
474fn read_source_asm_op<R: ByteReader>(
475 source: &mut R,
476 locations: &[Location],
477 strings: &[String],
478) -> Result<DebugSourceAsmOp, DeserializationError> {
479 let source_node = DebugSourceNodeId::read_from(source)?;
480 let op_idx = source.read_u32()?;
481 let location = if source.read_bool()? {
482 let location_idx = source.read_u32()? as usize;
483 Some(locations.get(location_idx).cloned().ok_or_else(|| {
484 DeserializationError::InvalidValue(alloc::format!(
485 "debug source asm op location index {location_idx} out of bounds for {} locations",
486 locations.len()
487 ))
488 })?)
489 } else {
490 None
491 };
492 let context_name = read_source_map_string_ref(source, strings)?;
493 let op = read_source_map_string_ref(source, strings)?;
494 let num_cycles = source.read_u8()?;
495 Ok(DebugSourceAsmOp::new(
496 source_node,
497 op_idx,
498 location,
499 context_name,
500 op,
501 num_cycles,
502 ))
503}
504
505fn min_source_map_asm_op_row_serialized_size() -> usize {
506 DebugSourceNodeId::min_serialized_size() + 4 + 1 + 4 + 4 + 1
508}
509
510fn write_source_map_string_ref<W: ByteWriter>(string: &String, strings: &[String], target: &mut W) {
511 let string_idx = strings
512 .iter()
513 .position(|candidate| candidate == string)
514 .expect("debug source map string table should contain every row string");
515 target.write_u32(string_idx as u32);
516}
517
518fn read_source_map_string_ref<R: ByteReader>(
519 source: &mut R,
520 strings: &[String],
521) -> Result<String, DeserializationError> {
522 let string_idx = source.read_u32()? as usize;
523 strings.get(string_idx).cloned().ok_or_else(|| {
524 DeserializationError::InvalidValue(alloc::format!(
525 "debug source asm op string index {string_idx} out of bounds for {} strings",
526 strings.len()
527 ))
528 })
529}
530
531impl Serializable for DebugErrorMessage {
535 fn write_into<W: ByteWriter>(&self, target: &mut W) {
536 target.write_u64(self.err_code);
537 self.message.as_ref().write_into(target);
538 }
539}
540
541impl Deserializable for DebugErrorMessage {
542 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
543 Ok(Self {
544 err_code: source.read_u64()?,
545 message: read_string(source)?,
546 })
547 }
548
549 fn min_serialized_size() -> usize {
550 8 + 1
551 }
552}
553
554impl Serializable for DebugErrorMessagesSection {
555 fn write_into<W: ByteWriter>(&self, target: &mut W) {
556 target.write_u8(self.version());
557 self.messages().write_into(target);
558 }
559}
560
561impl Deserializable for DebugErrorMessagesSection {
562 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
563 let version = source.read_u8()?;
564 if version != DEBUG_ERROR_MESSAGES_VERSION {
565 return Err(DeserializationError::InvalidValue(alloc::format!(
566 "unsupported debug_error_messages version: {version}, expected {DEBUG_ERROR_MESSAGES_VERSION}"
567 )));
568 }
569
570 let messages_len = read_bounded_len(source, "debug_error_messages messages", 1)?;
571 let messages = source.read_many_iter(messages_len)?.collect::<Result<_, _>>()?;
572 Ok(Self::from_parts(messages))
573 }
574}
575
576fn write_location<W: ByteWriter>(location: &Option<Location>, target: &mut W) {
577 if let Some(location) = location {
578 target.write_bool(true);
579 write_required_location(location, target);
580 } else {
581 target.write_bool(false);
582 }
583}
584
585fn read_location<R: ByteReader>(source: &mut R) -> Result<Option<Location>, DeserializationError> {
586 if !source.read_bool()? {
587 return Ok(None);
588 }
589
590 let uri = Uri::read_from(source)?;
591 let start = ByteIndex::new(source.read_u32()?);
592 let end = ByteIndex::new(source.read_u32()?);
593 Ok(Some(Location::new(uri, start, end)))
594}
595
596const MIN_REQUIRED_LOCATION_SERIALIZED_SIZE: usize = 9;
597
598fn write_required_location<W: ByteWriter>(location: &Location, target: &mut W) {
599 location.uri.write_into(target);
600 target.write_u32(location.start.to_u32());
601 target.write_u32(location.end.to_u32());
602}
603
604fn read_required_location<R: ByteReader>(source: &mut R) -> Result<Location, DeserializationError> {
605 let uri = Uri::read_from(source)?;
606 let start = ByteIndex::new(source.read_u32()?);
607 let end = ByteIndex::new(source.read_u32()?);
608 Ok(Location::new(uri, start, end))
609}
610
611const TYPE_TAG_PRIMITIVE: u8 = 0;
616const TYPE_TAG_POINTER: u8 = 1;
617const TYPE_TAG_ARRAY: u8 = 2;
618const TYPE_TAG_STRUCT: u8 = 3;
619const TYPE_TAG_FUNCTION: u8 = 4;
620const TYPE_TAG_UNKNOWN: u8 = 5;
621const TYPE_TAG_ENUM: u8 = 6;
622
623impl Serializable for DebugTypeInfo {
624 fn write_into<W: ByteWriter>(&self, target: &mut W) {
625 match self {
626 Self::Primitive(prim) => {
627 target.write_u8(TYPE_TAG_PRIMITIVE);
628 target.write_u8(*prim as u8);
629 },
630 Self::Pointer { pointee_type_idx } => {
631 target.write_u8(TYPE_TAG_POINTER);
632 target.write_u32(pointee_type_idx.as_u32());
633 },
634 Self::Array { element_type_idx, count } => {
635 target.write_u8(TYPE_TAG_ARRAY);
636 target.write_u32(element_type_idx.as_u32());
637 target.write_bool(count.is_some());
638 if let Some(count) = count {
639 target.write_u32(*count);
640 }
641 },
642 Self::Struct { name_idx, size, fields } => {
643 target.write_u8(TYPE_TAG_STRUCT);
644 target.write_u32(*name_idx);
645 target.write_u32(*size);
646 target.write_usize(fields.len());
647 for field in fields {
648 field.write_into(target);
649 }
650 },
651 Self::Function { return_type_idx, param_type_indices } => {
652 target.write_u8(TYPE_TAG_FUNCTION);
653 target.write_bool(return_type_idx.is_some());
654 if let Some(idx) = return_type_idx {
655 target.write_u32(idx.as_u32());
656 }
657 target.write_usize(param_type_indices.len());
658 for idx in param_type_indices {
659 target.write_u32(idx.as_u32());
660 }
661 },
662 Self::Enum {
663 name_idx,
664 size,
665 discriminant_type_idx,
666 variants,
667 } => {
668 target.write_u8(TYPE_TAG_ENUM);
669 target.write_u32(*name_idx);
670 target.write_u32(*size);
671 target.write_u32(discriminant_type_idx.as_u32());
672 target.write_usize(variants.len());
673 for variant in variants {
674 variant.write_into(target);
675 }
676 },
677 Self::Unknown => {
678 target.write_u8(TYPE_TAG_UNKNOWN);
679 },
680 }
681 }
682}
683
684impl Deserializable for DebugTypeInfo {
685 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
686 let tag = source.read_u8()?;
687 match tag {
688 TYPE_TAG_PRIMITIVE => {
689 let prim_tag = source.read_u8()?;
690 let prim = DebugPrimitiveType::from_discriminant(prim_tag).ok_or_else(|| {
691 DeserializationError::InvalidValue(alloc::format!(
692 "invalid primitive type tag: {prim_tag}"
693 ))
694 })?;
695 Ok(Self::Primitive(prim))
696 },
697 TYPE_TAG_POINTER => {
698 let pointee_type_idx = DebugTypeIdx::from(source.read_u32()?);
699 Ok(Self::Pointer { pointee_type_idx })
700 },
701 TYPE_TAG_ARRAY => {
702 let element_type_idx = DebugTypeIdx::from(source.read_u32()?);
703 let has_count = source.read_bool()?;
704 let count = if has_count { Some(source.read_u32()?) } else { None };
705 Ok(Self::Array { element_type_idx, count })
706 },
707 TYPE_TAG_STRUCT => {
708 let name_idx = source.read_u32()?;
709 let size = source.read_u32()?;
710 let fields_len = read_bounded_len(source, "debug struct fields", 1)?;
711 let fields = source.read_many_iter(fields_len)?.collect::<Result<_, _>>()?;
712 Ok(Self::Struct { name_idx, size, fields })
713 },
714 TYPE_TAG_FUNCTION => {
715 let has_return = source.read_bool()?;
716 let return_type_idx = if has_return {
717 Some(DebugTypeIdx::from(source.read_u32()?))
718 } else {
719 None
720 };
721 let param_type_indices =
722 read_debug_type_indices(source, "debug function parameters")?;
723 Ok(Self::Function { return_type_idx, param_type_indices })
724 },
725 TYPE_TAG_ENUM => {
726 let name_idx = source.read_u32()?;
727 let size = source.read_u32()?;
728 let discriminant_type_idx = DebugTypeIdx::from(source.read_u32()?);
729 let variants_len = read_bounded_len(source, "debug enum variants", 1)?;
730 let variants = source.read_many_iter(variants_len)?.collect::<Result<_, _>>()?;
731 Ok(Self::Enum {
732 name_idx,
733 size,
734 discriminant_type_idx,
735 variants,
736 })
737 },
738 TYPE_TAG_UNKNOWN => Ok(Self::Unknown),
739 _ => Err(DeserializationError::InvalidValue(alloc::format!("invalid type tag: {tag}"))),
740 }
741 }
742}
743
744impl Serializable for DebugFieldInfo {
748 fn write_into<W: ByteWriter>(&self, target: &mut W) {
749 target.write_u32(self.name_idx);
750 target.write_u32(self.type_idx.as_u32());
751 target.write_u32(self.offset);
752 }
753}
754
755impl Deserializable for DebugFieldInfo {
756 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
757 let name_idx = source.read_u32()?;
758 let type_idx = DebugTypeIdx::from(source.read_u32()?);
759 let offset = source.read_u32()?;
760 Ok(Self { name_idx, type_idx, offset })
761 }
762}
763
764impl Serializable for DebugVariantInfo {
768 fn write_into<W: ByteWriter>(&self, target: &mut W) {
769 target.write_u32(self.name_idx);
770 target.write_bool(self.type_idx.is_some());
771 if let Some(type_idx) = self.type_idx {
772 target.write_u32(type_idx.as_u32());
773 }
774 target.write_bool(self.payload_offset.is_some());
775 if let Some(payload_offset) = self.payload_offset {
776 target.write_u32(payload_offset);
777 }
778 target.write_u64((self.discriminant >> 64) as u64);
779 target.write_u64(self.discriminant as u64);
780 }
781}
782
783impl Deserializable for DebugVariantInfo {
784 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
785 let name_idx = source.read_u32()?;
786 let type_idx = if source.read_bool()? {
787 Some(DebugTypeIdx::from(source.read_u32()?))
788 } else {
789 None
790 };
791 let payload_offset = if source.read_bool()? {
792 Some(source.read_u32()?)
793 } else {
794 None
795 };
796 let hi = source.read_u64()? as u128;
797 let lo = source.read_u64()? as u128;
798 Ok(Self {
799 name_idx,
800 type_idx,
801 payload_offset,
802 discriminant: (hi << 64) | lo,
803 })
804 }
805}
806
807impl Serializable for DebugFileInfo {
811 fn write_into<W: ByteWriter>(&self, target: &mut W) {
812 target.write_u32(self.path_idx);
813
814 target.write_bool(self.checksum.is_some());
815 if let Some(checksum) = &self.checksum {
816 target.write_bytes(checksum.as_ref());
817 }
818 }
819}
820
821impl Deserializable for DebugFileInfo {
822 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
823 let path_idx = source.read_u32()?;
824
825 let has_checksum = source.read_bool()?;
826 let checksum = if has_checksum {
827 let bytes = source.read_slice(32)?;
828 let mut arr = [0u8; 32];
829 arr.copy_from_slice(bytes);
830 Some(alloc::boxed::Box::new(arr))
831 } else {
832 None
833 };
834
835 Ok(Self { path_idx, checksum })
836 }
837}
838
839impl Serializable for DebugFunctionInfo {
843 fn write_into<W: ByteWriter>(&self, target: &mut W) {
844 target.write_u32(self.name_idx);
845
846 target.write_bool(self.linkage_name_idx.is_some());
847 if let Some(idx) = self.linkage_name_idx {
848 target.write_u32(idx);
849 }
850
851 target.write_u32(self.file_idx);
852 target.write_u32(self.line.to_u32());
853 target.write_u32(self.column.to_u32());
854
855 target.write_bool(self.type_idx.is_some());
856 if let Some(idx) = self.type_idx {
857 target.write_u32(idx.as_u32());
858 }
859
860 target.write_bool(self.mast_root.is_some());
861 if let Some(root) = &self.mast_root {
862 root.write_into(target);
863 }
864 }
865}
866
867impl Deserializable for DebugFunctionInfo {
868 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
869 let name_idx = source.read_u32()?;
870
871 let has_linkage_name = source.read_bool()?;
872 let linkage_name_idx = if has_linkage_name {
873 Some(source.read_u32()?)
874 } else {
875 None
876 };
877
878 let file_idx = source.read_u32()?;
879 let line_raw = source.read_u32()?;
880 let column_raw = source.read_u32()?;
881 let line = LineNumber::new(line_raw).unwrap_or_default();
882 let column = ColumnNumber::new(column_raw).unwrap_or_default();
883
884 let has_type = source.read_bool()?;
885 let type_idx = if has_type {
886 Some(DebugTypeIdx::from(source.read_u32()?))
887 } else {
888 None
889 };
890
891 let has_mast_root = source.read_bool()?;
892 let mast_root = if has_mast_root {
893 Some(Word::read_from(source)?)
894 } else {
895 None
896 };
897
898 Ok(Self {
899 name_idx,
900 linkage_name_idx,
901 file_idx,
902 line,
903 column,
904 type_idx,
905 mast_root,
906 })
907 }
908}
909
910fn read_string<R: ByteReader>(source: &mut R) -> Result<Arc<str>, DeserializationError> {
914 let len = read_bounded_len(source, "debug string bytes", 1)?;
915 let bytes = source.read_slice(len)?;
916 let s = core::str::from_utf8(bytes).map_err(|err| {
917 DeserializationError::InvalidValue(alloc::format!("invalid utf-8 in string: {err}"))
918 })?;
919 Ok(Arc::from(s))
920}
921
922fn read_owned_string<R: ByteReader>(source: &mut R) -> Result<String, DeserializationError> {
923 read_string(source).map(|value| String::from(value.as_ref()))
924}
925
926fn read_debug_source_node_ids<R: ByteReader>(
927 source: &mut R,
928 label: &str,
929) -> Result<Vec<DebugSourceNodeId>, DeserializationError> {
930 let len = read_bounded_len(source, label, DebugSourceNodeId::min_serialized_size())?;
931 source.read_many_iter(len)?.collect::<Result<_, _>>()
932}
933
934fn read_debug_type_indices<R: ByteReader>(
935 source: &mut R,
936 label: &str,
937) -> Result<Vec<DebugTypeIdx>, DeserializationError> {
938 let len = read_bounded_len(source, label, DebugTypeIdx::min_serialized_size())?;
939 source.read_many_iter(len)?.collect::<Result<_, _>>()
940}
941
942#[cfg(test)]
943mod tests {
944 use miden_core::operations::{DebugVarInfo, DebugVarLocation};
945
946 use super::*;
947
948 struct FixedBudgetReader<'a> {
949 inner: miden_core::serde::SliceReader<'a>,
950 max_bytes: usize,
951 }
952
953 impl<'a> FixedBudgetReader<'a> {
954 fn new(bytes: &'a [u8], max_bytes: usize) -> Self {
955 Self {
956 inner: miden_core::serde::SliceReader::new(bytes),
957 max_bytes,
958 }
959 }
960 }
961
962 impl<'a> ByteReader for FixedBudgetReader<'a> {
963 fn read_u8(&mut self) -> Result<u8, DeserializationError> {
964 self.inner.read_u8()
965 }
966
967 fn peek_u8(&self) -> Result<u8, DeserializationError> {
968 self.inner.peek_u8()
969 }
970
971 fn read_slice(&mut self, len: usize) -> Result<&[u8], DeserializationError> {
972 self.inner.read_slice(len)
973 }
974
975 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], DeserializationError> {
976 self.inner.read_array()
977 }
978
979 fn check_eor(&self, num_bytes: usize) -> Result<(), DeserializationError> {
980 self.inner.check_eor(num_bytes)
981 }
982
983 fn has_more_bytes(&self) -> bool {
984 self.inner.has_more_bytes()
985 }
986
987 fn max_alloc(&self, element_size: usize) -> usize {
988 if element_size == 0 {
989 usize::MAX
990 } else {
991 self.max_bytes.checked_div(element_size).unwrap_or(0)
992 }
993 }
994 }
995
996 fn section_with_strings(version: u8, strings_len: usize) -> Vec<u8> {
997 let mut bytes = Vec::new();
998 bytes.write_u8(version);
999 bytes.write_usize(strings_len);
1000 for _ in 0..strings_len {
1001 "".write_into(&mut bytes);
1002 }
1003 bytes.write_usize(0);
1004 bytes
1005 }
1006
1007 fn function_type_bytes(params_len: usize) -> Vec<u8> {
1008 let mut bytes = Vec::new();
1009 bytes.write_u8(TYPE_TAG_FUNCTION);
1010 bytes.write_bool(false);
1011 bytes.write_usize(params_len);
1012 for _ in 0..params_len {
1013 bytes.write_u32(0);
1014 }
1015 bytes
1016 }
1017
1018 fn roundtrip<T: Serializable + Deserializable + PartialEq + core::fmt::Debug>(value: &T) {
1019 let mut bytes = Vec::new();
1020 value.write_into(&mut bytes);
1021 let result = T::read_from(&mut miden_core::serde::SliceReader::new(&bytes)).unwrap();
1022 assert_eq!(value, &result);
1023 }
1024
1025 #[test]
1026 fn test_debug_types_section_roundtrip() {
1027 let mut section = DebugTypesSection::new();
1028
1029 let i32_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::I32));
1031 let felt_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::Felt));
1032
1033 section.add_type(DebugTypeInfo::Pointer { pointee_type_idx: i32_type_idx });
1035
1036 section.add_type(DebugTypeInfo::Array {
1038 element_type_idx: felt_type_idx,
1039 count: Some(4),
1040 });
1041
1042 let x_idx = section.add_string(Arc::from("x"));
1044 let y_idx = section.add_string(Arc::from("y"));
1045 let point_idx = section.add_string(Arc::from("Point"));
1046 section.add_type(DebugTypeInfo::Struct {
1047 name_idx: point_idx,
1048 size: 16,
1049 fields: alloc::vec![
1050 DebugFieldInfo {
1051 name_idx: x_idx,
1052 type_idx: felt_type_idx,
1053 offset: 0,
1054 },
1055 DebugFieldInfo {
1056 name_idx: y_idx,
1057 type_idx: felt_type_idx,
1058 offset: 8,
1059 },
1060 ],
1061 });
1062
1063 let status_idx = section.add_string(Arc::from("Status"));
1065 let ok_idx = section.add_string(Arc::from("Ok"));
1066 let err_idx = section.add_string(Arc::from("Err"));
1067 section.add_type(DebugTypeInfo::Enum {
1068 name_idx: status_idx,
1069 size: 8,
1070 discriminant_type_idx: i32_type_idx,
1071 variants: alloc::vec![
1072 DebugVariantInfo {
1073 name_idx: ok_idx,
1074 type_idx: None,
1075 payload_offset: None,
1076 discriminant: 0,
1077 },
1078 DebugVariantInfo {
1079 name_idx: err_idx,
1080 type_idx: Some(felt_type_idx),
1081 payload_offset: Some(8),
1082 discriminant: 1,
1083 },
1084 ],
1085 });
1086
1087 roundtrip(§ion);
1088 }
1089
1090 #[test]
1091 fn test_debug_sources_section_roundtrip() {
1092 let mut section = DebugSourcesSection::new();
1093
1094 let path_idx = section.add_string(Arc::from("test.rs"));
1095 section.add_file(DebugFileInfo::new(path_idx));
1096
1097 let path2_idx = section.add_string(Arc::from("main.rs"));
1098 section.add_file(DebugFileInfo::new(path2_idx).with_checksum([42u8; 32]));
1099
1100 roundtrip(§ion);
1101 }
1102
1103 #[test]
1104 fn test_debug_functions_section_roundtrip() {
1105 let mut section = DebugFunctionsSection::new();
1106
1107 let name_idx = section.add_string(Arc::from("test_function"));
1108
1109 let line = LineNumber::new(10).unwrap();
1110 let column = ColumnNumber::new(1).unwrap();
1111 let func = DebugFunctionInfo::new(name_idx, 0, line, column);
1112 section.add_function(func);
1113
1114 roundtrip(§ion);
1115 }
1116
1117 #[test]
1118 fn test_debug_source_graph_section_roundtrip() {
1119 let section = DebugSourceGraphSection::from_parts(
1120 alloc::vec![
1121 DebugSourceNode::new(MastNodeId::new_unchecked(0), alloc::vec![], 0, 1),
1122 DebugSourceNode::new(
1123 MastNodeId::new_unchecked(1),
1124 alloc::vec![DebugSourceNodeId::from(0)],
1125 1,
1126 3,
1127 ),
1128 ],
1129 alloc::vec![DebugSourceNodeId::from(1)],
1130 );
1131
1132 roundtrip(§ion);
1133 }
1134
1135 #[test]
1136 fn test_debug_source_map_section_roundtrip() {
1137 let source_node = DebugSourceNodeId::from(0);
1138 let section = DebugSourceMapSection::from_parts_with_inline_calls(
1139 alloc::vec![DebugSourceAsmOp::new(
1140 source_node,
1141 2,
1142 None,
1143 "test::ctx".into(),
1144 "add".into(),
1145 1,
1146 )],
1147 alloc::vec![DebugSourceVar::new(
1148 source_node,
1149 2,
1150 DebugVarInfo::new("x", DebugVarLocation::Stack(0)),
1151 )],
1152 alloc::vec![DebugSourceInlineCall::new(
1153 source_node,
1154 2,
1155 0,
1156 0,
1157 LineNumber::new(10).unwrap(),
1158 ColumnNumber::new(5).unwrap(),
1159 )],
1160 );
1161
1162 roundtrip(§ion);
1163 }
1164
1165 #[test]
1166 fn test_debug_source_map_locationless_asm_op_min_size() {
1167 let source_node = DebugSourceNodeId::from(0);
1168 let asm_op = |op_idx| {
1169 DebugSourceAsmOp::new(source_node, op_idx, None, "test::ctx".into(), "add".into(), 1)
1170 };
1171 let section = DebugSourceMapSection::from_parts(
1172 alloc::vec![asm_op(2), asm_op(3), asm_op(4)],
1175 alloc::vec![],
1176 );
1177
1178 let bytes = section.to_bytes();
1179 let deserialized = DebugSourceMapSection::read_from_bytes(&bytes).unwrap();
1180 assert_eq!(deserialized.asm_ops(), section.asm_ops());
1181 }
1182
1183 #[test]
1184 fn test_debug_source_map_locations_are_deduplicated() {
1185 let source_node = DebugSourceNodeId::from(0);
1186 let location =
1187 Location::new(Uri::new("file://test.masm"), ByteIndex::new(10), ByteIndex::new(14));
1188 let section = DebugSourceMapSection::from_parts(
1189 alloc::vec![
1190 DebugSourceAsmOp::new(
1191 source_node,
1192 0,
1193 Some(location.clone()),
1194 "test::ctx".into(),
1195 "push.1".into(),
1196 1,
1197 ),
1198 DebugSourceAsmOp::new(
1199 source_node,
1200 1,
1201 Some(location.clone()),
1202 "test::ctx".into(),
1203 "add".into(),
1204 1,
1205 ),
1206 ],
1207 alloc::vec![],
1208 );
1209
1210 assert_eq!(section.locations(), &[location]);
1211
1212 let bytes = section.to_bytes();
1213 let deserialized = DebugSourceMapSection::read_from_bytes(&bytes).unwrap();
1214 assert_eq!(deserialized.locations(), section.locations());
1215 assert_eq!(deserialized.asm_ops(), section.asm_ops());
1216 }
1217
1218 #[test]
1219 fn test_debug_source_map_strings_are_deduplicated() {
1220 let source_node = DebugSourceNodeId::from(0);
1221 let section = DebugSourceMapSection::from_parts(
1222 alloc::vec![
1223 DebugSourceAsmOp::new(source_node, 0, None, "test::ctx".into(), "add".into(), 1,),
1224 DebugSourceAsmOp::new(source_node, 1, None, "test::ctx".into(), "mul".into(), 1,),
1225 DebugSourceAsmOp::new(source_node, 2, None, "test::other".into(), "add".into(), 1,),
1226 ],
1227 alloc::vec![],
1228 );
1229
1230 assert_eq!(
1231 section.strings(),
1232 &[
1233 String::from("test::ctx"),
1234 String::from("add"),
1235 String::from("mul"),
1236 String::from("test::other"),
1237 ]
1238 );
1239
1240 let bytes = section.to_bytes();
1241 let deserialized = DebugSourceMapSection::read_from_bytes(&bytes).unwrap();
1242 assert_eq!(deserialized.strings(), section.strings());
1243 assert_eq!(deserialized.asm_ops(), section.asm_ops());
1244 }
1245
1246 #[test]
1247 fn test_debug_error_messages_section_roundtrip() {
1248 let section = DebugErrorMessagesSection::from_parts(alloc::vec![DebugErrorMessage::new(
1249 42,
1250 Arc::from("assertion message"),
1251 )]);
1252
1253 roundtrip(§ion);
1254 assert_eq!(section.message(42).as_deref(), Some("assertion message"));
1255 }
1256
1257 #[test]
1258 fn test_empty_sections_roundtrip() {
1259 roundtrip(&DebugTypesSection::new());
1260 roundtrip(&DebugSourcesSection::new());
1261 roundtrip(&DebugFunctionsSection::new());
1262 roundtrip(&DebugSourceGraphSection::new());
1263 roundtrip(&DebugSourceMapSection::new());
1264 roundtrip(&DebugErrorMessagesSection::new());
1265 }
1266
1267 #[test]
1268 fn test_all_primitive_types_roundtrip() {
1269 let mut section = DebugTypesSection::new();
1270
1271 for prim in [
1272 DebugPrimitiveType::Void,
1273 DebugPrimitiveType::Bool,
1274 DebugPrimitiveType::I8,
1275 DebugPrimitiveType::U8,
1276 DebugPrimitiveType::I16,
1277 DebugPrimitiveType::U16,
1278 DebugPrimitiveType::I32,
1279 DebugPrimitiveType::U32,
1280 DebugPrimitiveType::I64,
1281 DebugPrimitiveType::U64,
1282 DebugPrimitiveType::I128,
1283 DebugPrimitiveType::U128,
1284 DebugPrimitiveType::F32,
1285 DebugPrimitiveType::F64,
1286 DebugPrimitiveType::Felt,
1287 DebugPrimitiveType::Word,
1288 DebugPrimitiveType::U256,
1289 ] {
1290 section.add_type(DebugTypeInfo::Primitive(prim));
1291 }
1292
1293 roundtrip(§ion);
1294 }
1295
1296 #[test]
1297 fn test_function_type_roundtrip() {
1298 let ty = DebugTypeInfo::Function {
1299 return_type_idx: Some(DebugTypeIdx::from(0)),
1300 param_type_indices: alloc::vec![
1301 DebugTypeIdx::from(1),
1302 DebugTypeIdx::from(2),
1303 DebugTypeIdx::from(3)
1304 ],
1305 };
1306 roundtrip(&ty);
1307
1308 let void_fn = DebugTypeInfo::Function {
1309 return_type_idx: None,
1310 param_type_indices: alloc::vec![],
1311 };
1312 roundtrip(&void_fn);
1313 }
1314
1315 #[test]
1316 fn test_file_info_with_checksum_roundtrip() {
1317 let file = DebugFileInfo::new(0).with_checksum([42u8; 32]);
1318 roundtrip(&file);
1319 }
1320
1321 #[test]
1322 fn test_function_with_mast_root_roundtrip() {
1323 let line1 = LineNumber::new(1).unwrap();
1324 let col1 = ColumnNumber::new(1).unwrap();
1325 let func = DebugFunctionInfo::new(0, 0, line1, col1)
1326 .with_linkage_name(1)
1327 .with_type(DebugTypeIdx::from(2))
1328 .with_mast_root(Word::default());
1329
1330 roundtrip(&func);
1331 }
1332
1333 #[test]
1334 fn test_debug_functions_v1_is_rejected() {
1335 let bytes = section_with_strings(1, 0);
1336 let mut reader = miden_core::serde::SliceReader::new(&bytes);
1337 let err = DebugFunctionsSection::read_from(&mut reader).unwrap_err();
1338 let DeserializationError::InvalidValue(message) = err else {
1339 panic!("expected InvalidValue error");
1340 };
1341 assert!(message.contains("unsupported debug_functions version: 1"));
1342 }
1343
1344 #[test]
1345 fn test_debug_section_string_bounds() {
1346 let types_bytes = section_with_strings(DEBUG_TYPES_VERSION, 2);
1347 let sources_bytes = section_with_strings(DEBUG_SOURCES_VERSION, 2);
1348 let functions_bytes = section_with_strings(DEBUG_FUNCTIONS_VERSION, 2);
1349
1350 let mut reader = FixedBudgetReader::new(&types_bytes, 1);
1351 let err = DebugTypesSection::read_from(&mut reader).unwrap_err();
1352 let DeserializationError::InvalidValue(message) = err else {
1353 panic!("expected InvalidValue error");
1354 };
1355 assert!(message.contains("exceeds budget"));
1356
1357 let mut reader = FixedBudgetReader::new(&sources_bytes, 1);
1358 let err = DebugSourcesSection::read_from(&mut reader).unwrap_err();
1359 let DeserializationError::InvalidValue(message) = err else {
1360 panic!("expected InvalidValue error");
1361 };
1362 assert!(message.contains("exceeds budget"));
1363
1364 let mut reader = FixedBudgetReader::new(&functions_bytes, 1);
1365 let err = DebugFunctionsSection::read_from(&mut reader).unwrap_err();
1366 let DeserializationError::InvalidValue(message) = err else {
1367 panic!("expected InvalidValue error");
1368 };
1369 assert!(message.contains("exceeds budget"));
1370
1371 let types_ok = section_with_strings(DEBUG_TYPES_VERSION, 1);
1372 let sources_ok = section_with_strings(DEBUG_SOURCES_VERSION, 1);
1373 let functions_ok = section_with_strings(DEBUG_FUNCTIONS_VERSION, 1);
1374
1375 let mut reader = FixedBudgetReader::new(&types_ok, 1);
1376 assert_eq!(DebugTypesSection::read_from(&mut reader).unwrap().strings.len(), 1);
1377
1378 let mut reader = FixedBudgetReader::new(&sources_ok, 1);
1379 assert_eq!(DebugSourcesSection::read_from(&mut reader).unwrap().strings.len(), 1);
1380
1381 let mut reader = FixedBudgetReader::new(&functions_ok, 1);
1382 assert_eq!(DebugFunctionsSection::read_from(&mut reader).unwrap().strings.len(), 1);
1383 }
1384
1385 #[test]
1386 fn test_debug_functions_rejects_oversized_string_table_count() {
1387 let bytes = [0x02, 0x08, 0x2a, 0xfe, 0xfe, 0x01];
1388 let mut reader = miden_core::serde::SliceReader::new(&bytes);
1389 let err = DebugFunctionsSection::read_from(&mut reader).unwrap_err();
1390 let DeserializationError::InvalidValue(message) = err else {
1391 panic!("expected InvalidValue error");
1392 };
1393 assert!(message.contains("debug_functions strings count"));
1394 assert!(message.contains("exceeds remaining input"));
1395 }
1396
1397 #[test]
1398 fn test_function_params_bounds() {
1399 let too_many = function_type_bytes(2);
1400 let mut reader = FixedBudgetReader::new(&too_many, 4);
1401 let err = DebugTypeInfo::read_from(&mut reader).unwrap_err();
1402 assert!(matches!(err, DeserializationError::InvalidValue(_)));
1403
1404 let ok = function_type_bytes(1);
1405 let mut reader = FixedBudgetReader::new(&ok, 4);
1406 let ty = DebugTypeInfo::read_from(&mut reader).unwrap();
1407 match ty {
1408 DebugTypeInfo::Function { param_type_indices, .. } => {
1409 assert_eq!(param_type_indices.len(), 1);
1410 },
1411 _ => panic!("expected function type"),
1412 }
1413 }
1414}