1use 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 s.as_ref().write_into(target);
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()?;
52 let max_strings = source.max_alloc(1);
53 if strings_len > max_strings {
54 return Err(DeserializationError::InvalidValue(alloc::format!(
55 "debug_types strings count {strings_len} exceeds budget {max_strings}"
56 )));
57 }
58 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
59 for _ in 0..strings_len {
60 strings.push(read_string(source)?);
61 }
62
63 let types_len = source.read_usize()?;
64 let types = source.read_many_iter(types_len)?.collect::<Result<_, _>>()?;
65
66 Ok(Self { version, strings, types })
67 }
68}
69
70impl Serializable for DebugSourcesSection {
74 fn write_into<W: ByteWriter>(&self, target: &mut W) {
75 target.write_u8(self.version);
76
77 target.write_usize(self.strings.len());
79 for s in &self.strings {
80 s.as_ref().write_into(target);
81 }
82
83 target.write_usize(self.files.len());
85 for file in &self.files {
86 file.write_into(target);
87 }
88 }
89}
90
91impl Deserializable for DebugSourcesSection {
92 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
93 let version = source.read_u8()?;
94 if version != DEBUG_SOURCES_VERSION {
95 return Err(DeserializationError::InvalidValue(alloc::format!(
96 "unsupported debug_sources version: {version}, expected {DEBUG_SOURCES_VERSION}"
97 )));
98 }
99
100 let strings_len = source.read_usize()?;
104 let max_strings = source.max_alloc(1);
105 if strings_len > max_strings {
106 return Err(DeserializationError::InvalidValue(alloc::format!(
107 "debug_sources strings count {strings_len} exceeds budget {max_strings}"
108 )));
109 }
110 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
111 for _ in 0..strings_len {
112 strings.push(read_string(source)?);
113 }
114
115 let files_len = source.read_usize()?;
116 let files = source.read_many_iter(files_len)?.collect::<Result<_, _>>()?;
117
118 Ok(Self { version, strings, files })
119 }
120}
121
122impl Serializable for DebugFunctionsSection {
126 fn write_into<W: ByteWriter>(&self, target: &mut W) {
127 target.write_u8(self.version);
128
129 target.write_usize(self.strings.len());
131 for s in &self.strings {
132 s.as_ref().write_into(target);
133 }
134
135 target.write_usize(self.functions.len());
137 for func in &self.functions {
138 func.write_into(target);
139 }
140 }
141}
142
143impl Deserializable for DebugFunctionsSection {
144 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
145 let version = source.read_u8()?;
146 if version != DEBUG_FUNCTIONS_VERSION {
147 return Err(DeserializationError::InvalidValue(alloc::format!(
148 "unsupported debug_functions version: {version}, expected {DEBUG_FUNCTIONS_VERSION}"
149 )));
150 }
151
152 let strings_len = source.read_usize()?;
156 let max_strings = source.max_alloc(1);
157 if strings_len > max_strings {
158 return Err(DeserializationError::InvalidValue(alloc::format!(
159 "debug_functions strings count {strings_len} exceeds budget {max_strings}"
160 )));
161 }
162 let mut strings = alloc::vec::Vec::with_capacity(strings_len);
163 for _ in 0..strings_len {
164 strings.push(read_string(source)?);
165 }
166
167 let functions_len = source.read_usize()?;
168 let functions = source.read_many_iter(functions_len)?.collect::<Result<_, _>>()?;
169
170 Ok(Self { version, strings, functions })
171 }
172}
173
174const TYPE_TAG_PRIMITIVE: u8 = 0;
179const TYPE_TAG_POINTER: u8 = 1;
180const TYPE_TAG_ARRAY: u8 = 2;
181const TYPE_TAG_STRUCT: u8 = 3;
182const TYPE_TAG_FUNCTION: u8 = 4;
183const TYPE_TAG_UNKNOWN: u8 = 5;
184
185impl Serializable for DebugTypeInfo {
186 fn write_into<W: ByteWriter>(&self, target: &mut W) {
187 match self {
188 Self::Primitive(prim) => {
189 target.write_u8(TYPE_TAG_PRIMITIVE);
190 target.write_u8(*prim as u8);
191 },
192 Self::Pointer { pointee_type_idx } => {
193 target.write_u8(TYPE_TAG_POINTER);
194 target.write_u32(pointee_type_idx.as_u32());
195 },
196 Self::Array { element_type_idx, count } => {
197 target.write_u8(TYPE_TAG_ARRAY);
198 target.write_u32(element_type_idx.as_u32());
199 target.write_bool(count.is_some());
200 if let Some(count) = count {
201 target.write_u32(*count);
202 }
203 },
204 Self::Struct { name_idx, size, fields } => {
205 target.write_u8(TYPE_TAG_STRUCT);
206 target.write_u32(*name_idx);
207 target.write_u32(*size);
208 target.write_usize(fields.len());
209 for field in fields {
210 field.write_into(target);
211 }
212 },
213 Self::Function { return_type_idx, param_type_indices } => {
214 target.write_u8(TYPE_TAG_FUNCTION);
215 target.write_bool(return_type_idx.is_some());
216 if let Some(idx) = return_type_idx {
217 target.write_u32(idx.as_u32());
218 }
219 target.write_usize(param_type_indices.len());
220 for idx in param_type_indices {
221 target.write_u32(idx.as_u32());
222 }
223 },
224 Self::Unknown => {
225 target.write_u8(TYPE_TAG_UNKNOWN);
226 },
227 }
228 }
229}
230
231impl Deserializable for DebugTypeInfo {
232 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
233 let tag = source.read_u8()?;
234 match tag {
235 TYPE_TAG_PRIMITIVE => {
236 let prim_tag = source.read_u8()?;
237 let prim = DebugPrimitiveType::from_discriminant(prim_tag).ok_or_else(|| {
238 DeserializationError::InvalidValue(alloc::format!(
239 "invalid primitive type tag: {prim_tag}"
240 ))
241 })?;
242 Ok(Self::Primitive(prim))
243 },
244 TYPE_TAG_POINTER => {
245 let pointee_type_idx = DebugTypeIdx::from(source.read_u32()?);
246 Ok(Self::Pointer { pointee_type_idx })
247 },
248 TYPE_TAG_ARRAY => {
249 let element_type_idx = DebugTypeIdx::from(source.read_u32()?);
250 let has_count = source.read_bool()?;
251 let count = if has_count { Some(source.read_u32()?) } else { None };
252 Ok(Self::Array { element_type_idx, count })
253 },
254 TYPE_TAG_STRUCT => {
255 let name_idx = source.read_u32()?;
256 let size = source.read_u32()?;
257 let fields_len = source.read_usize()?;
258 let fields = source.read_many_iter(fields_len)?.collect::<Result<_, _>>()?;
259 Ok(Self::Struct { name_idx, size, fields })
260 },
261 TYPE_TAG_FUNCTION => {
262 let has_return = source.read_bool()?;
263 let return_type_idx = if has_return {
264 Some(DebugTypeIdx::from(source.read_u32()?))
265 } else {
266 None
267 };
268 let param_type_indices = alloc::vec::Vec::<DebugTypeIdx>::read_from(source)?;
269 Ok(Self::Function { return_type_idx, param_type_indices })
270 },
271 TYPE_TAG_UNKNOWN => Ok(Self::Unknown),
272 _ => Err(DeserializationError::InvalidValue(alloc::format!("invalid type tag: {tag}"))),
273 }
274 }
275}
276
277impl Serializable for DebugFieldInfo {
281 fn write_into<W: ByteWriter>(&self, target: &mut W) {
282 target.write_u32(self.name_idx);
283 target.write_u32(self.type_idx.as_u32());
284 target.write_u32(self.offset);
285 }
286}
287
288impl Deserializable for DebugFieldInfo {
289 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
290 let name_idx = source.read_u32()?;
291 let type_idx = DebugTypeIdx::from(source.read_u32()?);
292 let offset = source.read_u32()?;
293 Ok(Self { name_idx, type_idx, offset })
294 }
295}
296
297impl Serializable for DebugFileInfo {
301 fn write_into<W: ByteWriter>(&self, target: &mut W) {
302 target.write_u32(self.path_idx);
303
304 target.write_bool(self.checksum.is_some());
305 if let Some(checksum) = &self.checksum {
306 target.write_bytes(checksum.as_ref());
307 }
308 }
309}
310
311impl Deserializable for DebugFileInfo {
312 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
313 let path_idx = source.read_u32()?;
314
315 let has_checksum = source.read_bool()?;
316 let checksum = if has_checksum {
317 let bytes = source.read_slice(32)?;
318 let mut arr = [0u8; 32];
319 arr.copy_from_slice(bytes);
320 Some(alloc::boxed::Box::new(arr))
321 } else {
322 None
323 };
324
325 Ok(Self { path_idx, checksum })
326 }
327}
328
329impl Serializable for DebugFunctionInfo {
333 fn write_into<W: ByteWriter>(&self, target: &mut W) {
334 target.write_u32(self.name_idx);
335
336 target.write_bool(self.linkage_name_idx.is_some());
337 if let Some(idx) = self.linkage_name_idx {
338 target.write_u32(idx);
339 }
340
341 target.write_u32(self.file_idx);
342 target.write_u32(self.line.to_u32());
343 target.write_u32(self.column.to_u32());
344
345 target.write_bool(self.type_idx.is_some());
346 if let Some(idx) = self.type_idx {
347 target.write_u32(idx.as_u32());
348 }
349
350 target.write_bool(self.mast_root.is_some());
351 if let Some(root) = &self.mast_root {
352 root.write_into(target);
353 }
354
355 target.write_usize(self.variables.len());
357 for var in &self.variables {
358 var.write_into(target);
359 }
360
361 target.write_usize(self.inlined_calls.len());
363 for call in &self.inlined_calls {
364 call.write_into(target);
365 }
366 }
367}
368
369impl Deserializable for DebugFunctionInfo {
370 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
371 let name_idx = source.read_u32()?;
372
373 let has_linkage_name = source.read_bool()?;
374 let linkage_name_idx = if has_linkage_name {
375 Some(source.read_u32()?)
376 } else {
377 None
378 };
379
380 let file_idx = source.read_u32()?;
381 let line_raw = source.read_u32()?;
382 let column_raw = source.read_u32()?;
383 let line = LineNumber::new(line_raw).unwrap_or_default();
384 let column = ColumnNumber::new(column_raw).unwrap_or_default();
385
386 let has_type = source.read_bool()?;
387 let type_idx = if has_type {
388 Some(DebugTypeIdx::from(source.read_u32()?))
389 } else {
390 None
391 };
392
393 let has_mast_root = source.read_bool()?;
394 let mast_root = if has_mast_root {
395 Some(Word::read_from(source)?)
396 } else {
397 None
398 };
399
400 let vars_len = source.read_usize()?;
402 let variables = source.read_many_iter(vars_len)?.collect::<Result<_, _>>()?;
403
404 let calls_len = source.read_usize()?;
406 let inlined_calls = source.read_many_iter(calls_len)?.collect::<Result<_, _>>()?;
407
408 Ok(Self {
409 name_idx,
410 linkage_name_idx,
411 file_idx,
412 line,
413 column,
414 type_idx,
415 mast_root,
416 variables,
417 inlined_calls,
418 })
419 }
420}
421
422impl Serializable for DebugVariableInfo {
426 fn write_into<W: ByteWriter>(&self, target: &mut W) {
427 target.write_u32(self.name_idx);
428 target.write_u32(self.type_idx.as_u32());
429 target.write_u32(self.arg_index);
430 target.write_u32(self.line.to_u32());
431 target.write_u32(self.column.to_u32());
432 target.write_u32(self.scope_depth);
433 }
434}
435
436impl Deserializable for DebugVariableInfo {
437 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
438 let name_idx = source.read_u32()?;
439 let type_idx = DebugTypeIdx::from(source.read_u32()?);
440 let arg_index = source.read_u32()?;
441 let line_raw = source.read_u32()?;
442 let column_raw = source.read_u32()?;
443 let line = LineNumber::new(line_raw).unwrap_or_default();
444 let column = ColumnNumber::new(column_raw).unwrap_or_default();
445 let scope_depth = source.read_u32()?;
446 Ok(Self {
447 name_idx,
448 type_idx,
449 arg_index,
450 line,
451 column,
452 scope_depth,
453 })
454 }
455}
456
457impl Serializable for DebugInlinedCallInfo {
461 fn write_into<W: ByteWriter>(&self, target: &mut W) {
462 target.write_u32(self.callee_idx);
463 target.write_u32(self.file_idx);
464 target.write_u32(self.line.to_u32());
465 target.write_u32(self.column.to_u32());
466 }
467}
468
469impl Deserializable for DebugInlinedCallInfo {
470 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
471 let callee_idx = source.read_u32()?;
472 let file_idx = source.read_u32()?;
473 let line_raw = source.read_u32()?;
474 let column_raw = source.read_u32()?;
475 let line = LineNumber::new(line_raw).unwrap_or_default();
476 let column = ColumnNumber::new(column_raw).unwrap_or_default();
477 Ok(Self { callee_idx, file_idx, line, column })
478 }
479}
480
481fn read_string<R: ByteReader>(source: &mut R) -> Result<Arc<str>, DeserializationError> {
485 let len = source.read_usize()?;
486 let bytes = source.read_slice(len)?;
487 let s = core::str::from_utf8(bytes).map_err(|err| {
488 DeserializationError::InvalidValue(alloc::format!("invalid utf-8 in string: {err}"))
489 })?;
490 Ok(Arc::from(s))
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 struct FixedBudgetReader<'a> {
498 inner: miden_core::serde::SliceReader<'a>,
499 max_bytes: usize,
500 }
501
502 impl<'a> FixedBudgetReader<'a> {
503 fn new(bytes: &'a [u8], max_bytes: usize) -> Self {
504 Self {
505 inner: miden_core::serde::SliceReader::new(bytes),
506 max_bytes,
507 }
508 }
509 }
510
511 impl<'a> ByteReader for FixedBudgetReader<'a> {
512 fn read_u8(&mut self) -> Result<u8, DeserializationError> {
513 self.inner.read_u8()
514 }
515
516 fn peek_u8(&self) -> Result<u8, DeserializationError> {
517 self.inner.peek_u8()
518 }
519
520 fn read_slice(&mut self, len: usize) -> Result<&[u8], DeserializationError> {
521 self.inner.read_slice(len)
522 }
523
524 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], DeserializationError> {
525 self.inner.read_array()
526 }
527
528 fn check_eor(&self, num_bytes: usize) -> Result<(), DeserializationError> {
529 self.inner.check_eor(num_bytes)
530 }
531
532 fn has_more_bytes(&self) -> bool {
533 self.inner.has_more_bytes()
534 }
535
536 fn max_alloc(&self, element_size: usize) -> usize {
537 if element_size == 0 {
538 usize::MAX
539 } else {
540 self.max_bytes.checked_div(element_size).unwrap_or(0)
541 }
542 }
543 }
544
545 fn section_with_strings(version: u8, strings_len: usize) -> alloc::vec::Vec<u8> {
546 let mut bytes = alloc::vec::Vec::new();
547 bytes.write_u8(version);
548 bytes.write_usize(strings_len);
549 for _ in 0..strings_len {
550 "".write_into(&mut bytes);
551 }
552 bytes.write_usize(0);
553 bytes
554 }
555
556 fn function_type_bytes(params_len: usize) -> alloc::vec::Vec<u8> {
557 let mut bytes = alloc::vec::Vec::new();
558 bytes.write_u8(TYPE_TAG_FUNCTION);
559 bytes.write_bool(false);
560 bytes.write_usize(params_len);
561 for _ in 0..params_len {
562 bytes.write_u32(0);
563 }
564 bytes
565 }
566
567 fn roundtrip<T: Serializable + Deserializable + PartialEq + core::fmt::Debug>(value: &T) {
568 let mut bytes = alloc::vec::Vec::new();
569 value.write_into(&mut bytes);
570 let result = T::read_from(&mut miden_core::serde::SliceReader::new(&bytes)).unwrap();
571 assert_eq!(value, &result);
572 }
573
574 #[test]
575 fn test_debug_types_section_roundtrip() {
576 let mut section = DebugTypesSection::new();
577
578 let i32_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::I32));
580 let felt_type_idx = section.add_type(DebugTypeInfo::Primitive(DebugPrimitiveType::Felt));
581
582 section.add_type(DebugTypeInfo::Pointer { pointee_type_idx: i32_type_idx });
584
585 section.add_type(DebugTypeInfo::Array {
587 element_type_idx: felt_type_idx,
588 count: Some(4),
589 });
590
591 let x_idx = section.add_string(Arc::from("x"));
593 let y_idx = section.add_string(Arc::from("y"));
594 let point_idx = section.add_string(Arc::from("Point"));
595 section.add_type(DebugTypeInfo::Struct {
596 name_idx: point_idx,
597 size: 16,
598 fields: alloc::vec![
599 DebugFieldInfo {
600 name_idx: x_idx,
601 type_idx: felt_type_idx,
602 offset: 0,
603 },
604 DebugFieldInfo {
605 name_idx: y_idx,
606 type_idx: felt_type_idx,
607 offset: 8,
608 },
609 ],
610 });
611
612 roundtrip(§ion);
613 }
614
615 #[test]
616 fn test_debug_sources_section_roundtrip() {
617 let mut section = DebugSourcesSection::new();
618
619 let path_idx = section.add_string(Arc::from("test.rs"));
620 section.add_file(DebugFileInfo::new(path_idx));
621
622 let path2_idx = section.add_string(Arc::from("main.rs"));
623 section.add_file(DebugFileInfo::new(path2_idx).with_checksum([42u8; 32]));
624
625 roundtrip(§ion);
626 }
627
628 #[test]
629 fn test_debug_functions_section_roundtrip() {
630 let mut section = DebugFunctionsSection::new();
631
632 let name_idx = section.add_string(Arc::from("test_function"));
633
634 let line = LineNumber::new(10).unwrap();
635 let column = ColumnNumber::new(1).unwrap();
636 let mut func = DebugFunctionInfo::new(name_idx, 0, line, column);
637 let var_name_idx = section.add_string(Arc::from("x"));
638 let var_line = LineNumber::new(10).unwrap();
639 let var_column = ColumnNumber::new(5).unwrap();
640 func.add_variable(
641 DebugVariableInfo::new(var_name_idx, DebugTypeIdx::from(0), var_line, var_column)
642 .with_arg_index(1),
643 );
644 section.add_function(func);
645
646 roundtrip(§ion);
647 }
648
649 #[test]
650 fn test_empty_sections_roundtrip() {
651 roundtrip(&DebugTypesSection::new());
652 roundtrip(&DebugSourcesSection::new());
653 roundtrip(&DebugFunctionsSection::new());
654 }
655
656 #[test]
657 fn test_all_primitive_types_roundtrip() {
658 let mut section = DebugTypesSection::new();
659
660 for prim in [
661 DebugPrimitiveType::Void,
662 DebugPrimitiveType::Bool,
663 DebugPrimitiveType::I8,
664 DebugPrimitiveType::U8,
665 DebugPrimitiveType::I16,
666 DebugPrimitiveType::U16,
667 DebugPrimitiveType::I32,
668 DebugPrimitiveType::U32,
669 DebugPrimitiveType::I64,
670 DebugPrimitiveType::U64,
671 DebugPrimitiveType::I128,
672 DebugPrimitiveType::U128,
673 DebugPrimitiveType::F32,
674 DebugPrimitiveType::F64,
675 DebugPrimitiveType::Felt,
676 DebugPrimitiveType::Word,
677 ] {
678 section.add_type(DebugTypeInfo::Primitive(prim));
679 }
680
681 roundtrip(§ion);
682 }
683
684 #[test]
685 fn test_function_type_roundtrip() {
686 let ty = DebugTypeInfo::Function {
687 return_type_idx: Some(DebugTypeIdx::from(0)),
688 param_type_indices: alloc::vec![
689 DebugTypeIdx::from(1),
690 DebugTypeIdx::from(2),
691 DebugTypeIdx::from(3)
692 ],
693 };
694 roundtrip(&ty);
695
696 let void_fn = DebugTypeInfo::Function {
697 return_type_idx: None,
698 param_type_indices: alloc::vec![],
699 };
700 roundtrip(&void_fn);
701 }
702
703 #[test]
704 fn test_file_info_with_checksum_roundtrip() {
705 let file = DebugFileInfo::new(0).with_checksum([42u8; 32]);
706 roundtrip(&file);
707 }
708
709 #[test]
710 fn test_function_with_mast_root_roundtrip() {
711 let line1 = LineNumber::new(1).unwrap();
712 let col1 = ColumnNumber::new(1).unwrap();
713 let mut func = DebugFunctionInfo::new(0, 0, line1, col1)
714 .with_linkage_name(1)
715 .with_type(DebugTypeIdx::from(2))
716 .with_mast_root(Word::default());
717
718 let var_line = LineNumber::new(5).unwrap();
719 let var_col = ColumnNumber::new(10).unwrap();
720 func.add_variable(
721 DebugVariableInfo::new(0, DebugTypeIdx::from(0), var_line, var_col)
722 .with_arg_index(1)
723 .with_scope_depth(2),
724 );
725
726 let call_line = LineNumber::new(20).unwrap();
727 let call_col = ColumnNumber::new(5).unwrap();
728 func.add_inlined_call(DebugInlinedCallInfo::new(0, 0, call_line, call_col));
729
730 roundtrip(&func);
731 }
732
733 #[test]
734 fn test_debug_section_string_bounds() {
735 let types_bytes = section_with_strings(DEBUG_TYPES_VERSION, 2);
736 let sources_bytes = section_with_strings(DEBUG_SOURCES_VERSION, 2);
737 let functions_bytes = section_with_strings(DEBUG_FUNCTIONS_VERSION, 2);
738
739 let mut reader = FixedBudgetReader::new(&types_bytes, 1);
740 let err = DebugTypesSection::read_from(&mut reader).unwrap_err();
741 let DeserializationError::InvalidValue(message) = err else {
742 panic!("expected InvalidValue error");
743 };
744 assert!(message.contains("exceeds budget"));
745
746 let mut reader = FixedBudgetReader::new(&sources_bytes, 1);
747 let err = DebugSourcesSection::read_from(&mut reader).unwrap_err();
748 let DeserializationError::InvalidValue(message) = err else {
749 panic!("expected InvalidValue error");
750 };
751 assert!(message.contains("exceeds budget"));
752
753 let mut reader = FixedBudgetReader::new(&functions_bytes, 1);
754 let err = DebugFunctionsSection::read_from(&mut reader).unwrap_err();
755 let DeserializationError::InvalidValue(message) = err else {
756 panic!("expected InvalidValue error");
757 };
758 assert!(message.contains("exceeds budget"));
759
760 let types_ok = section_with_strings(DEBUG_TYPES_VERSION, 1);
761 let sources_ok = section_with_strings(DEBUG_SOURCES_VERSION, 1);
762 let functions_ok = section_with_strings(DEBUG_FUNCTIONS_VERSION, 1);
763
764 let mut reader = FixedBudgetReader::new(&types_ok, 1);
765 assert_eq!(DebugTypesSection::read_from(&mut reader).unwrap().strings.len(), 1);
766
767 let mut reader = FixedBudgetReader::new(&sources_ok, 1);
768 assert_eq!(DebugSourcesSection::read_from(&mut reader).unwrap().strings.len(), 1);
769
770 let mut reader = FixedBudgetReader::new(&functions_ok, 1);
771 assert_eq!(DebugFunctionsSection::read_from(&mut reader).unwrap().strings.len(), 1);
772 }
773
774 #[test]
775 fn test_function_params_bounds() {
776 let too_many = function_type_bytes(2);
777 let mut reader = FixedBudgetReader::new(&too_many, 4);
778 let err = DebugTypeInfo::read_from(&mut reader).unwrap_err();
779 assert!(matches!(err, DeserializationError::InvalidValue(_)));
780
781 let ok = function_type_bytes(1);
782 let mut reader = FixedBudgetReader::new(&ok, 4);
783 let ty = DebugTypeInfo::read_from(&mut reader).unwrap();
784 match ty {
785 DebugTypeInfo::Function { param_type_indices, .. } => {
786 assert_eq!(param_type_indices.len(), 1);
787 },
788 _ => panic!("expected function type"),
789 }
790 }
791}