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