miden_mast_package/debug_info/
serialization.rs1use alloc::sync::Arc;
4
5use miden_core::{
6 Word,
7 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
8};
9use miden_debug_types::{ColumnNumber, LineNumber};
10
11use super::{
12 DEBUG_FUNCTIONS_VERSION, DEBUG_SOURCES_VERSION, DEBUG_TYPES_VERSION, DebugFieldInfo,
13 DebugFileInfo, DebugFunctionInfo, DebugFunctionsSection, DebugInlinedCallInfo,
14 DebugPrimitiveType, DebugSourcesSection, DebugTypeIdx, DebugTypeInfo, DebugTypesSection,
15 DebugVariableInfo,
16};
17
18impl Serializable for DebugTypesSection {
22 fn write_into<W: ByteWriter>(&self, target: &mut W) {
23 target.write_u8(self.version);
24
25 target.write_usize(self.strings.len());
27 for s in &self.strings {
28 write_string(target, s);
29 }
30
31 target.write_usize(self.types.len());
33 for ty in &self.types {
34 ty.write_into(target);
35 }
36 }
37}
38
39impl Deserializable for DebugTypesSection {
40 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
41 let version = source.read_u8()?;
42 if version != DEBUG_TYPES_VERSION {
43 return Err(DeserializationError::InvalidValue(alloc::format!(
44 "unsupported debug_types version: {version}, expected {DEBUG_TYPES_VERSION}"
45 )));
46 }
47
48 let strings_len = source.read_usize()?;
49 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
50 for _ in 0..strings_len {
51 strings.push(read_string(source)?);
52 }
53
54 let types_len = source.read_usize()?;
55 let types = source.read_many_iter(types_len)?.collect::<Result<_, _>>()?;
56
57 Ok(Self { version, strings, types })
58 }
59}
60
61impl Serializable for DebugSourcesSection {
65 fn write_into<W: ByteWriter>(&self, target: &mut W) {
66 target.write_u8(self.version);
67
68 target.write_usize(self.strings.len());
70 for s in &self.strings {
71 write_string(target, s);
72 }
73
74 target.write_usize(self.files.len());
76 for file in &self.files {
77 file.write_into(target);
78 }
79 }
80}
81
82impl Deserializable for DebugSourcesSection {
83 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
84 let version = source.read_u8()?;
85 if version != DEBUG_SOURCES_VERSION {
86 return Err(DeserializationError::InvalidValue(alloc::format!(
87 "unsupported debug_sources version: {version}, expected {DEBUG_SOURCES_VERSION}"
88 )));
89 }
90
91 let strings_len = source.read_usize()?;
92 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
93 for _ in 0..strings_len {
94 strings.push(read_string(source)?);
95 }
96
97 let files_len = source.read_usize()?;
98 let files = source.read_many_iter(files_len)?.collect::<Result<_, _>>()?;
99
100 Ok(Self { version, strings, files })
101 }
102}
103
104impl Serializable for DebugFunctionsSection {
108 fn write_into<W: ByteWriter>(&self, target: &mut W) {
109 target.write_u8(self.version);
110
111 target.write_usize(self.strings.len());
113 for s in &self.strings {
114 write_string(target, s);
115 }
116
117 target.write_usize(self.functions.len());
119 for func in &self.functions {
120 func.write_into(target);
121 }
122 }
123}
124
125impl Deserializable for DebugFunctionsSection {
126 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
127 let version = source.read_u8()?;
128 if version != DEBUG_FUNCTIONS_VERSION {
129 return Err(DeserializationError::InvalidValue(alloc::format!(
130 "unsupported debug_functions version: {version}, expected {DEBUG_FUNCTIONS_VERSION}"
131 )));
132 }
133
134 let strings_len = source.read_usize()?;
135 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
136 for _ in 0..strings_len {
137 strings.push(read_string(source)?);
138 }
139
140 let functions_len = source.read_usize()?;
141 let functions = source.read_many_iter(functions_len)?.collect::<Result<_, _>>()?;
142
143 Ok(Self { version, strings, functions })
144 }
145}
146
147const TYPE_TAG_PRIMITIVE: u8 = 0;
152const TYPE_TAG_POINTER: u8 = 1;
153const TYPE_TAG_ARRAY: u8 = 2;
154const TYPE_TAG_STRUCT: u8 = 3;
155const TYPE_TAG_FUNCTION: u8 = 4;
156const TYPE_TAG_UNKNOWN: u8 = 5;
157
158impl Serializable for DebugTypeInfo {
159 fn write_into<W: ByteWriter>(&self, target: &mut W) {
160 match self {
161 Self::Primitive(prim) => {
162 target.write_u8(TYPE_TAG_PRIMITIVE);
163 target.write_u8(*prim as u8);
164 },
165 Self::Pointer { pointee_type_idx } => {
166 target.write_u8(TYPE_TAG_POINTER);
167 target.write_u32(pointee_type_idx.as_u32());
168 },
169 Self::Array { element_type_idx, count } => {
170 target.write_u8(TYPE_TAG_ARRAY);
171 target.write_u32(element_type_idx.as_u32());
172 target.write_bool(count.is_some());
173 if let Some(count) = count {
174 target.write_u32(*count);
175 }
176 },
177 Self::Struct { name_idx, size, fields } => {
178 target.write_u8(TYPE_TAG_STRUCT);
179 target.write_u32(*name_idx);
180 target.write_u32(*size);
181 target.write_usize(fields.len());
182 for field in fields {
183 field.write_into(target);
184 }
185 },
186 Self::Function { return_type_idx, param_type_indices } => {
187 target.write_u8(TYPE_TAG_FUNCTION);
188 target.write_bool(return_type_idx.is_some());
189 if let Some(idx) = return_type_idx {
190 target.write_u32(idx.as_u32());
191 }
192 target.write_usize(param_type_indices.len());
193 for idx in param_type_indices {
194 target.write_u32(idx.as_u32());
195 }
196 },
197 Self::Unknown => {
198 target.write_u8(TYPE_TAG_UNKNOWN);
199 },
200 }
201 }
202}
203
204impl Deserializable for DebugTypeInfo {
205 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
206 let tag = source.read_u8()?;
207 match tag {
208 TYPE_TAG_PRIMITIVE => {
209 let prim_tag = source.read_u8()?;
210 let prim = DebugPrimitiveType::from_discriminant(prim_tag).ok_or_else(|| {
211 DeserializationError::InvalidValue(alloc::format!(
212 "invalid primitive type tag: {prim_tag}"
213 ))
214 })?;
215 Ok(Self::Primitive(prim))
216 },
217 TYPE_TAG_POINTER => {
218 let pointee_type_idx = DebugTypeIdx::from(source.read_u32()?);
219 Ok(Self::Pointer { pointee_type_idx })
220 },
221 TYPE_TAG_ARRAY => {
222 let element_type_idx = DebugTypeIdx::from(source.read_u32()?);
223 let has_count = source.read_bool()?;
224 let count = if has_count { Some(source.read_u32()?) } else { None };
225 Ok(Self::Array { element_type_idx, count })
226 },
227 TYPE_TAG_STRUCT => {
228 let name_idx = source.read_u32()?;
229 let size = source.read_u32()?;
230 let fields_len = source.read_usize()?;
231 let fields = source.read_many_iter(fields_len)?.collect::<Result<_, _>>()?;
232 Ok(Self::Struct { name_idx, size, fields })
233 },
234 TYPE_TAG_FUNCTION => {
235 let has_return = source.read_bool()?;
236 let return_type_idx = if has_return {
237 Some(DebugTypeIdx::from(source.read_u32()?))
238 } else {
239 None
240 };
241 let params_len = source.read_usize()?;
242 let mut param_type_indices = alloc::vec::Vec::with_capacity(params_len);
243 for _ in 0..params_len {
244 param_type_indices.push(DebugTypeIdx::from(source.read_u32()?));
245 }
246 Ok(Self::Function { return_type_idx, param_type_indices })
247 },
248 TYPE_TAG_UNKNOWN => Ok(Self::Unknown),
249 _ => Err(DeserializationError::InvalidValue(alloc::format!("invalid type tag: {tag}"))),
250 }
251 }
252}
253
254impl Serializable for DebugFieldInfo {
258 fn write_into<W: ByteWriter>(&self, target: &mut W) {
259 target.write_u32(self.name_idx);
260 target.write_u32(self.type_idx.as_u32());
261 target.write_u32(self.offset);
262 }
263}
264
265impl Deserializable for DebugFieldInfo {
266 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
267 let name_idx = source.read_u32()?;
268 let type_idx = DebugTypeIdx::from(source.read_u32()?);
269 let offset = source.read_u32()?;
270 Ok(Self { name_idx, type_idx, offset })
271 }
272}
273
274impl Serializable for DebugFileInfo {
278 fn write_into<W: ByteWriter>(&self, target: &mut W) {
279 target.write_u32(self.path_idx);
280
281 target.write_bool(self.checksum.is_some());
282 if let Some(checksum) = &self.checksum {
283 target.write_bytes(checksum.as_ref());
284 }
285 }
286}
287
288impl Deserializable for DebugFileInfo {
289 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
290 let path_idx = source.read_u32()?;
291
292 let has_checksum = source.read_bool()?;
293 let checksum = if has_checksum {
294 let bytes = source.read_slice(32)?;
295 let mut arr = [0u8; 32];
296 arr.copy_from_slice(bytes);
297 Some(alloc::boxed::Box::new(arr))
298 } else {
299 None
300 };
301
302 Ok(Self { path_idx, checksum })
303 }
304}
305
306impl Serializable for DebugFunctionInfo {
310 fn write_into<W: ByteWriter>(&self, target: &mut W) {
311 target.write_u32(self.name_idx);
312
313 target.write_bool(self.linkage_name_idx.is_some());
314 if let Some(idx) = self.linkage_name_idx {
315 target.write_u32(idx);
316 }
317
318 target.write_u32(self.file_idx);
319 target.write_u32(self.line.to_u32());
320 target.write_u32(self.column.to_u32());
321
322 target.write_bool(self.type_idx.is_some());
323 if let Some(idx) = self.type_idx {
324 target.write_u32(idx.as_u32());
325 }
326
327 target.write_bool(self.mast_root.is_some());
328 if let Some(root) = &self.mast_root {
329 root.write_into(target);
330 }
331
332 target.write_usize(self.variables.len());
334 for var in &self.variables {
335 var.write_into(target);
336 }
337
338 target.write_usize(self.inlined_calls.len());
340 for call in &self.inlined_calls {
341 call.write_into(target);
342 }
343 }
344}
345
346impl Deserializable for DebugFunctionInfo {
347 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
348 let name_idx = source.read_u32()?;
349
350 let has_linkage_name = source.read_bool()?;
351 let linkage_name_idx = if has_linkage_name {
352 Some(source.read_u32()?)
353 } else {
354 None
355 };
356
357 let file_idx = source.read_u32()?;
358 let line_raw = source.read_u32()?;
359 let column_raw = source.read_u32()?;
360 let line = LineNumber::new(line_raw).unwrap_or_default();
361 let column = ColumnNumber::new(column_raw).unwrap_or_default();
362
363 let has_type = source.read_bool()?;
364 let type_idx = if has_type {
365 Some(DebugTypeIdx::from(source.read_u32()?))
366 } else {
367 None
368 };
369
370 let has_mast_root = source.read_bool()?;
371 let mast_root = if has_mast_root {
372 Some(Word::read_from(source)?)
373 } else {
374 None
375 };
376
377 let vars_len = source.read_usize()?;
379 let variables = source.read_many_iter(vars_len)?.collect::<Result<_, _>>()?;
380
381 let calls_len = source.read_usize()?;
383 let inlined_calls = source.read_many_iter(calls_len)?.collect::<Result<_, _>>()?;
384
385 Ok(Self {
386 name_idx,
387 linkage_name_idx,
388 file_idx,
389 line,
390 column,
391 type_idx,
392 mast_root,
393 variables,
394 inlined_calls,
395 })
396 }
397}
398
399impl Serializable for DebugVariableInfo {
403 fn write_into<W: ByteWriter>(&self, target: &mut W) {
404 target.write_u32(self.name_idx);
405 target.write_u32(self.type_idx.as_u32());
406 target.write_u32(self.arg_index);
407 target.write_u32(self.line.to_u32());
408 target.write_u32(self.column.to_u32());
409 target.write_u32(self.scope_depth);
410 }
411}
412
413impl Deserializable for DebugVariableInfo {
414 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
415 let name_idx = source.read_u32()?;
416 let type_idx = DebugTypeIdx::from(source.read_u32()?);
417 let arg_index = source.read_u32()?;
418 let line_raw = source.read_u32()?;
419 let column_raw = source.read_u32()?;
420 let line = LineNumber::new(line_raw).unwrap_or_default();
421 let column = ColumnNumber::new(column_raw).unwrap_or_default();
422 let scope_depth = source.read_u32()?;
423 Ok(Self {
424 name_idx,
425 type_idx,
426 arg_index,
427 line,
428 column,
429 scope_depth,
430 })
431 }
432}
433
434impl Serializable for DebugInlinedCallInfo {
438 fn write_into<W: ByteWriter>(&self, target: &mut W) {
439 target.write_u32(self.callee_idx);
440 target.write_u32(self.file_idx);
441 target.write_u32(self.line.to_u32());
442 target.write_u32(self.column.to_u32());
443 }
444}
445
446impl Deserializable for DebugInlinedCallInfo {
447 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
448 let callee_idx = source.read_u32()?;
449 let file_idx = source.read_u32()?;
450 let line_raw = source.read_u32()?;
451 let column_raw = source.read_u32()?;
452 let line = LineNumber::new(line_raw).unwrap_or_default();
453 let column = ColumnNumber::new(column_raw).unwrap_or_default();
454 Ok(Self { callee_idx, file_idx, line, column })
455 }
456}
457
458fn write_string<W: ByteWriter>(target: &mut W, s: &str) {
462 let bytes = s.as_bytes();
463 target.write_usize(bytes.len());
464 target.write_bytes(bytes);
465}
466
467fn read_string<R: ByteReader>(source: &mut R) -> Result<Arc<str>, DeserializationError> {
468 let len = source.read_usize()?;
469 let bytes = source.read_slice(len)?;
470 let s = core::str::from_utf8(bytes).map_err(|err| {
471 DeserializationError::InvalidValue(alloc::format!("invalid utf-8 in string: {err}"))
472 })?;
473 Ok(Arc::from(s))
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479
480 fn roundtrip<T: Serializable + Deserializable + PartialEq + core::fmt::Debug>(value: &T) {
481 let mut bytes = alloc::vec::Vec::new();
482 value.write_into(&mut bytes);
483 let result = T::read_from(&mut miden_core::serde::SliceReader::new(&bytes)).unwrap();
484 assert_eq!(value, &result);
485 }
486
487 #[test]
488 fn test_debug_types_section_roundtrip() {
489 let mut section = DebugTypesSection::new();
490
491 let i32_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::I32));
493 let felt_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::Felt));
494
495 section.add_type(DebugTypeInfo::Pointer { pointee_type_idx: i32_type_idx });
497
498 section.add_type(DebugTypeInfo::Array {
500 element_type_idx: felt_type_idx,
501 count: Some(4),
502 });
503
504 let x_idx = section.add_string(Arc::from("x"));
506 let y_idx = section.add_string(Arc::from("y"));
507 let point_idx = section.add_string(Arc::from("Point"));
508 section.add_type(DebugTypeInfo::Struct {
509 name_idx: point_idx,
510 size: 16,
511 fields: alloc::vec![
512 DebugFieldInfo {
513 name_idx: x_idx,
514 type_idx: felt_type_idx,
515 offset: 0,
516 },
517 DebugFieldInfo {
518 name_idx: y_idx,
519 type_idx: felt_type_idx,
520 offset: 8,
521 },
522 ],
523 });
524
525 roundtrip(§ion);
526 }
527
528 #[test]
529 fn test_debug_sources_section_roundtrip() {
530 let mut section = DebugSourcesSection::new();
531
532 let path_idx = section.add_string(Arc::from("test.rs"));
533 section.add_file(DebugFileInfo::new(path_idx));
534
535 let path2_idx = section.add_string(Arc::from("main.rs"));
536 section.add_file(DebugFileInfo::new(path2_idx).with_checksum([42u8; 32]));
537
538 roundtrip(§ion);
539 }
540
541 #[test]
542 fn test_debug_functions_section_roundtrip() {
543 let mut section = DebugFunctionsSection::new();
544
545 let name_idx = section.add_string(Arc::from("test_function"));
546
547 let line = LineNumber::new(10).unwrap();
548 let column = ColumnNumber::new(1).unwrap();
549 let mut func = DebugFunctionInfo::new(name_idx, 0, line, column);
550 let var_name_idx = section.add_string(Arc::from("x"));
551 let var_line = LineNumber::new(10).unwrap();
552 let var_column = ColumnNumber::new(5).unwrap();
553 func.add_variable(
554 DebugVariableInfo::new(var_name_idx, DebugTypeIdx::from(0), var_line, var_column)
555 .with_arg_index(1),
556 );
557 section.add_function(func);
558
559 roundtrip(§ion);
560 }
561
562 #[test]
563 fn test_empty_sections_roundtrip() {
564 roundtrip(&DebugTypesSection::new());
565 roundtrip(&DebugSourcesSection::new());
566 roundtrip(&DebugFunctionsSection::new());
567 }
568
569 #[test]
570 fn test_all_primitive_types_roundtrip() {
571 let mut section = DebugTypesSection::new();
572
573 for prim in [
574 DebugPrimitiveType::Void,
575 DebugPrimitiveType::Bool,
576 DebugPrimitiveType::I8,
577 DebugPrimitiveType::U8,
578 DebugPrimitiveType::I16,
579 DebugPrimitiveType::U16,
580 DebugPrimitiveType::I32,
581 DebugPrimitiveType::U32,
582 DebugPrimitiveType::I64,
583 DebugPrimitiveType::U64,
584 DebugPrimitiveType::I128,
585 DebugPrimitiveType::U128,
586 DebugPrimitiveType::F32,
587 DebugPrimitiveType::F64,
588 DebugPrimitiveType::Felt,
589 DebugPrimitiveType::Word,
590 ] {
591 section.add_type(DebugTypeInfo::Primitive(prim));
592 }
593
594 roundtrip(§ion);
595 }
596
597 #[test]
598 fn test_function_type_roundtrip() {
599 let ty = DebugTypeInfo::Function {
600 return_type_idx: Some(DebugTypeIdx::from(0)),
601 param_type_indices: alloc::vec![
602 DebugTypeIdx::from(1),
603 DebugTypeIdx::from(2),
604 DebugTypeIdx::from(3)
605 ],
606 };
607 roundtrip(&ty);
608
609 let void_fn = DebugTypeInfo::Function {
610 return_type_idx: None,
611 param_type_indices: alloc::vec![],
612 };
613 roundtrip(&void_fn);
614 }
615
616 #[test]
617 fn test_file_info_with_checksum_roundtrip() {
618 let file = DebugFileInfo::new(0).with_checksum([42u8; 32]);
619 roundtrip(&file);
620 }
621
622 #[test]
623 fn test_function_with_mast_root_roundtrip() {
624 let line1 = LineNumber::new(1).unwrap();
625 let col1 = ColumnNumber::new(1).unwrap();
626 let mut func = DebugFunctionInfo::new(0, 0, line1, col1)
627 .with_linkage_name(1)
628 .with_type(DebugTypeIdx::from(2))
629 .with_mast_root(Word::default());
630
631 let var_line = LineNumber::new(5).unwrap();
632 let var_col = ColumnNumber::new(10).unwrap();
633 func.add_variable(
634 DebugVariableInfo::new(0, DebugTypeIdx::from(0), var_line, var_col)
635 .with_arg_index(1)
636 .with_scope_depth(2),
637 );
638
639 let call_line = LineNumber::new(20).unwrap();
640 let call_col = ColumnNumber::new(5).unwrap();
641 func.add_inlined_call(DebugInlinedCallInfo::new(0, 0, call_line, call_col));
642
643 roundtrip(&func);
644 }
645}