1use alloc::{format, string::ToString, sync::Arc, vec::Vec};
2use core::{fmt, num::NonZeroU32};
3
4use miden_core::serde::{
5 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, read_bounded_len,
6};
7use miden_debug_types::Location;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 Felt,
13 ast::{TypeExpr, types::Type},
14};
15
16#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct DebugVarInfo {
25 name: Arc<str>,
27 ty: Option<Type>,
29 declared_type: Option<Arc<TypeExpr>>,
31 arg_index: Option<NonZeroU32>,
33 location: Option<Location>,
37 value_location: DebugVarLocation,
39}
40
41impl DebugVarInfo {
42 pub fn new(name: impl Into<Arc<str>>, value_location: DebugVarLocation) -> Self {
44 Self {
45 name: name.into(),
46 ty: None,
47 declared_type: None,
48 arg_index: None,
49 location: None,
50 value_location,
51 }
52 }
53
54 pub fn name(&self) -> &Arc<str> {
56 &self.name
57 }
58
59 pub fn ty(&self) -> Option<&Type> {
61 self.ty.as_ref()
62 }
63
64 pub fn declared_type(&self) -> Option<Arc<TypeExpr>> {
66 self.declared_type.clone()
67 }
68
69 pub fn set_ty(&mut self, ty: Type, declared_type: Option<Arc<TypeExpr>>) {
71 self.ty = Some(ty);
72 self.declared_type = declared_type;
73 }
74
75 pub fn arg_index(&self) -> Option<NonZeroU32> {
78 self.arg_index
79 }
80
81 pub fn set_arg_index(&mut self, arg_index: u32) {
86 self.arg_index =
87 Some(NonZeroU32::new(arg_index).expect("argument index must be 1-based (non-zero)"));
88 }
89
90 pub fn location(&self) -> Option<&Location> {
93 self.location.as_ref()
94 }
95
96 pub fn set_location(&mut self, location: Location) {
100 self.location = Some(location);
101 }
102
103 pub fn value_location(&self) -> &DebugVarLocation {
105 &self.value_location
106 }
107
108 pub fn set_value_location(&mut self, value_location: DebugVarLocation) {
110 self.value_location = value_location;
111 }
112}
113
114impl fmt::Display for DebugVarInfo {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "var.{}", self.name)?;
117
118 if let Some(arg_index) = self.arg_index {
119 write!(f, "[arg{arg_index}]")?;
120 }
121
122 write!(f, " = {}", self.value_location)?;
123
124 if let Some(loc) = &self.location {
125 write!(f, " [{}@{}..{}]", loc.uri, loc.start, loc.end)?;
126 }
127
128 Ok(())
129 }
130}
131
132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub enum DebugFrameBase {
139 Local(i16),
141 Memory(u32),
143}
144
145#[derive(Clone, Debug, Eq, PartialEq)]
151#[cfg_attr(feature = "serde", derive(Serialize))]
152pub struct DebugLocationExpression {
153 operations: Vec<DebugLocationExpressionOp>,
154}
155
156#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
158#[error(
159 "debug location expression has {operation_count} operations, but at most {MAX_DEBUG_LOCATION_EXPRESSION_OPS} are supported"
160)]
161pub struct DebugLocationExpressionError {
162 operation_count: usize,
163}
164
165impl DebugLocationExpressionError {
166 pub fn operation_count(&self) -> usize {
168 self.operation_count
169 }
170}
171
172#[cfg(feature = "serde")]
173#[derive(Deserialize)]
174struct DebugLocationExpressionSerde {
175 #[serde(deserialize_with = "deserialize_debug_location_expression_operations")]
176 operations: Vec<DebugLocationExpressionOp>,
177}
178
179#[cfg(feature = "serde")]
180fn deserialize_debug_location_expression_operations<'de, D>(
181 deserializer: D,
182) -> Result<Vec<DebugLocationExpressionOp>, D::Error>
183where
184 D: serde::Deserializer<'de>,
185{
186 struct OperationsVisitor;
187
188 impl<'de> serde::de::Visitor<'de> for OperationsVisitor {
189 type Value = Vec<DebugLocationExpressionOp>;
190
191 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
192 write!(
193 formatter,
194 "at most {MAX_DEBUG_LOCATION_EXPRESSION_OPS} debug location operations"
195 )
196 }
197
198 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
199 where
200 A: serde::de::SeqAccess<'de>,
201 {
202 if let Some(operation_count) = sequence.size_hint()
203 && operation_count > MAX_DEBUG_LOCATION_EXPRESSION_OPS
204 {
205 return Err(serde::de::Error::custom(DebugLocationExpressionError {
206 operation_count,
207 }));
208 }
209
210 let mut operations = Vec::with_capacity(sequence.size_hint().unwrap_or(0).min(8));
211 while let Some(operation) = sequence.next_element()? {
212 if operations.len() == MAX_DEBUG_LOCATION_EXPRESSION_OPS {
213 return Err(serde::de::Error::custom(DebugLocationExpressionError {
214 operation_count: operations.len() + 1,
215 }));
216 }
217 operations.push(operation);
218 }
219 Ok(operations)
220 }
221 }
222
223 deserializer.deserialize_seq(OperationsVisitor)
224}
225
226const MAX_DEBUG_LOCATION_EXPRESSION_OPS: usize = 256;
227
228impl DebugLocationExpression {
229 pub fn new(
236 operations: Vec<DebugLocationExpressionOp>,
237 ) -> Result<Self, DebugLocationExpressionError> {
238 validate_debug_location_expression_len(operations.len())?;
239 Ok(Self { operations })
240 }
241
242 pub fn operations(&self) -> &[DebugLocationExpressionOp] {
244 &self.operations
245 }
246
247 pub fn is_empty(&self) -> bool {
249 self.operations.is_empty()
250 }
251}
252
253#[cfg(feature = "serde")]
254impl<'de> Deserialize<'de> for DebugLocationExpression {
255 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
256 where
257 D: serde::Deserializer<'de>,
258 {
259 let expression = DebugLocationExpressionSerde::deserialize(deserializer)?;
260 Self::new(expression.operations).map_err(serde::de::Error::custom)
261 }
262}
263
264#[derive(Clone, Copy, Debug, Eq, PartialEq)]
271#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
272pub enum DebugLocationExpressionOp {
273 ReadStack(u8),
275 ReadMemory(u32),
277 ReadLocal(i16),
279 ConstU64(u64),
281 ConstI64(i64),
283 AddUnsigned(u64),
285 Add,
287 Sub,
289 DerefBytes,
292 FrameBaseAddress {
294 base: DebugFrameBase,
296 byte_offset: i64,
298 },
299}
300
301#[derive(Clone, Debug, Eq, PartialEq)]
307pub enum DebugVarLocation {
308 Stack(u8),
310 Memory(u32),
312 Const(Felt),
314 Local(i16),
320 Unavailable,
322 ResolvedFrameBase {
328 base: DebugFrameBase,
330 byte_offset: i64,
332 },
333 Expression(DebugLocationExpression),
335}
336
337impl fmt::Display for DebugVarLocation {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 match self {
340 Self::Stack(pos) => write!(f, "stack[{pos}]"),
341 Self::Memory(addr) => write!(f, "mem[{addr}]"),
342 Self::Const(val) => write!(f, "const({})", val.as_canonical_u64()),
343 Self::Local(offset) => write!(f, "FMP{offset:+}"),
344 Self::Unavailable => f.write_str("unavailable"),
345 Self::ResolvedFrameBase { base, byte_offset } => match base {
346 DebugFrameBase::Local(offset) => {
347 write!(f, "frame-base(FMP{offset:+}){byte_offset:+}")
348 },
349 DebugFrameBase::Memory(address) => {
350 write!(f, "frame-base(mem[{address}]){byte_offset:+}")
351 },
352 },
353 Self::Expression(expression) => {
354 f.write_str("expr(")?;
355 f.debug_list().entries(expression.operations()).finish()?;
356 f.write_str(")")
357 },
358 }
359 }
360}
361
362impl Serializable for DebugVarLocation {
366 fn write_into<W: ByteWriter>(&self, target: &mut W) {
367 match self {
368 Self::Stack(pos) => {
369 target.write_u8(0);
370 target.write_u8(*pos);
371 },
372 Self::Memory(addr) => {
373 target.write_u8(1);
374 target.write_u32(*addr);
375 },
376 Self::Const(felt) => {
377 target.write_u8(2);
378 target.write_u64(felt.as_canonical_u64());
379 },
380 Self::Local(offset) => {
381 target.write_u8(3);
382 target.write_bytes(&offset.to_le_bytes());
383 },
384 Self::Unavailable => {
385 target.write_u8(4);
386 },
387 Self::ResolvedFrameBase { base, byte_offset } => {
388 target.write_u8(5);
389 write_debug_frame_base(*base, target);
390 target.write_bytes(&byte_offset.to_le_bytes());
391 },
392 Self::Expression(expression) => {
393 target.write_u8(6);
394 expression.write_into(target);
395 },
396 }
397 }
398}
399
400impl Deserializable for DebugVarLocation {
401 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
402 let tag = source.read_u8()?;
403 match tag {
404 0 => Ok(Self::Stack(source.read_u8()?)),
405 1 => Ok(Self::Memory(source.read_u32()?)),
406 2 => {
407 let value = source.read_u64()?;
408 Ok(Self::Const(Felt::new_unchecked(value)))
409 },
410 3 => {
411 let bytes = source.read_array::<2>()?;
412 Ok(Self::Local(i16::from_le_bytes(bytes)))
413 },
414 4 => Ok(Self::Unavailable),
415 5 => {
416 let base = read_debug_frame_base(source)?;
417 let bytes = source.read_array::<8>()?;
418 let byte_offset = i64::from_le_bytes(bytes);
419 Ok(Self::ResolvedFrameBase { base, byte_offset })
420 },
421 6 => Ok(Self::Expression(DebugLocationExpression::read_from(source)?)),
422 _ => Err(DeserializationError::InvalidValue(format!(
423 "invalid DebugVarLocation tag: {tag}"
424 ))),
425 }
426 }
427
428 fn min_serialized_size() -> usize {
429 u8::min_serialized_size()
431 }
432}
433
434impl Serializable for DebugLocationExpression {
435 fn write_into<W: ByteWriter>(&self, target: &mut W) {
436 target.write_usize(self.operations.len());
437 for operation in &self.operations {
438 operation.write_into(target);
439 }
440 }
441}
442
443impl Deserializable for DebugLocationExpression {
444 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
445 let count = read_bounded_len(source, "debug location expression operations", 1)?;
446 validate_debug_location_expression_len(count)
447 .map_err(|error| DeserializationError::InvalidValue(error.to_string()))?;
448 let mut operations = Vec::with_capacity(count.min(8));
449 for _ in 0..count {
450 operations.push(DebugLocationExpressionOp::read_from(source)?);
451 }
452 Ok(Self { operations })
453 }
454
455 fn min_serialized_size() -> usize {
456 usize::min_serialized_size()
457 }
458}
459
460fn validate_debug_location_expression_len(
461 operation_count: usize,
462) -> Result<(), DebugLocationExpressionError> {
463 if operation_count > MAX_DEBUG_LOCATION_EXPRESSION_OPS {
464 return Err(DebugLocationExpressionError { operation_count });
465 }
466 Ok(())
467}
468
469impl Serializable for DebugLocationExpressionOp {
470 fn write_into<W: ByteWriter>(&self, target: &mut W) {
471 match self {
472 Self::ReadStack(position) => {
473 target.write_u8(0);
474 target.write_u8(*position);
475 },
476 Self::ReadMemory(address) => {
477 target.write_u8(1);
478 target.write_u32(*address);
479 },
480 Self::ReadLocal(offset) => {
481 target.write_u8(2);
482 target.write_bytes(&offset.to_le_bytes());
483 },
484 Self::ConstU64(value) => {
485 target.write_u8(3);
486 target.write_u64(*value);
487 },
488 Self::ConstI64(value) => {
489 target.write_u8(4);
490 target.write_bytes(&value.to_le_bytes());
491 },
492 Self::AddUnsigned(value) => {
493 target.write_u8(5);
494 target.write_u64(*value);
495 },
496 Self::Add => target.write_u8(6),
497 Self::Sub => target.write_u8(7),
498 Self::DerefBytes => target.write_u8(8),
499 Self::FrameBaseAddress { base, byte_offset } => {
500 target.write_u8(9);
501 write_debug_frame_base(*base, target);
502 target.write_bytes(&byte_offset.to_le_bytes());
503 },
504 }
505 }
506}
507
508impl Deserializable for DebugLocationExpressionOp {
509 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
510 match source.read_u8()? {
511 0 => Ok(Self::ReadStack(source.read_u8()?)),
512 1 => Ok(Self::ReadMemory(source.read_u32()?)),
513 2 => Ok(Self::ReadLocal(i16::from_le_bytes(source.read_array::<2>()?))),
514 3 => Ok(Self::ConstU64(source.read_u64()?)),
515 4 => Ok(Self::ConstI64(i64::from_le_bytes(source.read_array::<8>()?))),
516 5 => Ok(Self::AddUnsigned(source.read_u64()?)),
517 6 => Ok(Self::Add),
518 7 => Ok(Self::Sub),
519 8 => Ok(Self::DerefBytes),
520 9 => {
521 let base = read_debug_frame_base(source)?;
522 let byte_offset = i64::from_le_bytes(source.read_array::<8>()?);
523 Ok(Self::FrameBaseAddress { base, byte_offset })
524 },
525 tag => Err(DeserializationError::InvalidValue(format!(
526 "invalid DebugLocationExpressionOp tag: {tag}"
527 ))),
528 }
529 }
530
531 fn min_serialized_size() -> usize {
532 u8::min_serialized_size()
533 }
534}
535
536fn write_debug_frame_base<W: ByteWriter>(base: DebugFrameBase, target: &mut W) {
537 match base {
538 DebugFrameBase::Local(offset) => {
539 target.write_u8(0);
540 target.write_bytes(&offset.to_le_bytes());
541 },
542 DebugFrameBase::Memory(address) => {
543 target.write_u8(1);
544 target.write_u32(address);
545 },
546 }
547}
548
549fn read_debug_frame_base<R: ByteReader>(
550 source: &mut R,
551) -> Result<DebugFrameBase, DeserializationError> {
552 match source.read_u8()? {
553 0 => Ok(DebugFrameBase::Local(i16::from_le_bytes(source.read_array::<2>()?))),
554 1 => Ok(DebugFrameBase::Memory(source.read_u32()?)),
555 tag => Err(DeserializationError::InvalidValue(format!(
556 "invalid resolved debug frame-base tag: {tag}"
557 ))),
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use alloc::{string::ToString, vec::Vec};
564
565 use miden_core::serde::{Deserializable, Serializable, SliceReader};
566 use miden_debug_types::{ByteIndex, Uri};
567
568 use super::*;
569
570 #[test]
571 fn debug_var_info_display_simple() {
572 let var = DebugVarInfo::new("x", DebugVarLocation::Stack(0));
573 assert_eq!(var.to_string(), "var.x = stack[0]");
574 }
575
576 #[test]
577 fn debug_var_info_display_with_arg() {
578 let mut var = DebugVarInfo::new("param", DebugVarLocation::Stack(2));
579 var.set_arg_index(1);
580 assert_eq!(var.to_string(), "var.param[arg1] = stack[2]");
581 }
582
583 #[test]
584 fn debug_var_info_display_with_location() {
585 let mut var = DebugVarInfo::new("y", DebugVarLocation::Memory(100));
586 var.set_location(Location::new(
587 Uri::new("test.rs"),
588 ByteIndex::from(0u32),
589 ByteIndex::from(5u32),
590 ));
591 assert_eq!(var.to_string(), "var.y = mem[100] [test.rs@0..5]");
592 }
593
594 #[test]
595 fn debug_var_location_display() {
596 assert_eq!(DebugVarLocation::Stack(0).to_string(), "stack[0]");
597 assert_eq!(DebugVarLocation::Memory(256).to_string(), "mem[256]");
598 assert_eq!(DebugVarLocation::Const(Felt::new_unchecked(42)).to_string(), "const(42)");
599 assert_eq!(DebugVarLocation::Local(-3).to_string(), "FMP-3");
600 assert_eq!(
601 DebugVarLocation::ResolvedFrameBase {
602 base: DebugFrameBase::Local(-3),
603 byte_offset: 12,
604 }
605 .to_string(),
606 "frame-base(FMP-3)+12"
607 );
608 assert_eq!(DebugVarLocation::Unavailable.to_string(), "unavailable");
609 assert_eq!(
610 DebugVarLocation::Expression(
611 DebugLocationExpression::new(vec![
612 DebugLocationExpressionOp::FrameBaseAddress {
613 base: DebugFrameBase::Local(-2),
614 byte_offset: 4,
615 },
616 DebugLocationExpressionOp::AddUnsigned(8),
617 DebugLocationExpressionOp::DerefBytes,
618 ])
619 .unwrap(),
620 )
621 .to_string(),
622 "expr([FrameBaseAddress { base: Local(-2), byte_offset: 4 }, AddUnsigned(8), DerefBytes])"
623 );
624 }
625
626 #[test]
627 fn debug_var_location_serialization_round_trip() {
628 let locations = [
629 DebugVarLocation::Stack(7),
630 DebugVarLocation::Memory(0xdead_beef),
631 DebugVarLocation::Const(Felt::new_unchecked(999)),
632 DebugVarLocation::Local(-3),
633 DebugVarLocation::Unavailable,
634 DebugVarLocation::ResolvedFrameBase {
635 base: DebugFrameBase::Local(-3),
636 byte_offset: 28,
637 },
638 DebugVarLocation::ResolvedFrameBase {
639 base: DebugFrameBase::Memory(100),
640 byte_offset: -16,
641 },
642 DebugVarLocation::Expression(
643 DebugLocationExpression::new(vec![
644 DebugLocationExpressionOp::ReadStack(2),
645 DebugLocationExpressionOp::ConstI64(-4),
646 DebugLocationExpressionOp::Add,
647 DebugLocationExpressionOp::DerefBytes,
648 ])
649 .unwrap(),
650 ),
651 ];
652
653 for loc in &locations {
654 let mut bytes = Vec::new();
655 loc.write_into(&mut bytes);
656 let mut reader = SliceReader::new(&bytes);
657 let deser = DebugVarLocation::read_from(&mut reader).unwrap();
658 assert_eq!(&deser, loc);
659 }
660 }
661
662 #[test]
663 fn debug_location_expression_wire_encoding_is_stable() {
664 let expression = DebugLocationExpression::new(vec![
665 DebugLocationExpressionOp::ReadStack(0x2a),
666 DebugLocationExpressionOp::ReadMemory(0x1234_5678),
667 DebugLocationExpressionOp::ReadLocal(-2),
668 DebugLocationExpressionOp::ConstU64(0x0102_0304_0506_0708),
669 DebugLocationExpressionOp::ConstI64(-2),
670 DebugLocationExpressionOp::AddUnsigned(0x1112_1314_1516_1718),
671 DebugLocationExpressionOp::Add,
672 DebugLocationExpressionOp::Sub,
673 DebugLocationExpressionOp::DerefBytes,
674 DebugLocationExpressionOp::FrameBaseAddress {
675 base: DebugFrameBase::Local(-4),
676 byte_offset: 0x0102_0304_0506_0708,
677 },
678 DebugLocationExpressionOp::FrameBaseAddress {
679 base: DebugFrameBase::Memory(0xa1b2_c3d4),
680 byte_offset: -3,
681 },
682 ])
683 .unwrap();
684 let expected = vec![
685 0x17, 0x00, 0x2a, 0x01, 0x78, 0x56, 0x34, 0x12, 0x02, 0xfe, 0xff, 0x03, 0x08, 0x07,
686 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x04, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
687 0xff, 0x05, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x06, 0x07, 0x08, 0x09,
688 0x00, 0xfc, 0xff, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x09, 0x01, 0xd4,
689 0xc3, 0xb2, 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
690 ];
691
692 let mut bytes = Vec::new();
693 expression.write_into(&mut bytes);
694 assert_eq!(bytes, expected);
695
696 let mut reader = SliceReader::new(&expected);
697 assert_eq!(DebugLocationExpression::read_from(&mut reader).unwrap(), expression);
698 }
699
700 #[test]
701 fn debug_var_location_min_serialized_size_matches_shortest_variant() {
702 let location = DebugVarLocation::Unavailable;
703 let min_serialized_size = DebugVarLocation::min_serialized_size();
704 let mut bytes = Vec::new();
705 location.write_into(&mut bytes);
706
707 assert_eq!(min_serialized_size, 1);
708 assert_eq!(bytes.len(), min_serialized_size);
709 }
710
711 #[test]
712 fn debug_location_expression_rejects_unknown_operation() {
713 let mut bytes = Vec::new();
714 bytes.write_usize(1);
715 bytes.write_u8(u8::MAX);
716
717 let mut reader = SliceReader::new(&bytes);
718 let err = DebugLocationExpression::read_from(&mut reader).unwrap_err();
719 let DeserializationError::InvalidValue(message) = err else {
720 panic!("expected InvalidValue error");
721 };
722 assert!(message.contains("invalid DebugLocationExpressionOp tag"));
723 }
724
725 #[test]
726 fn debug_location_expression_caps_operation_count_before_allocation() {
727 let count = MAX_DEBUG_LOCATION_EXPRESSION_OPS + 1;
728 let mut bytes = Vec::new();
729 bytes.write_usize(count);
730 bytes.resize(bytes.len() + count, 0);
731
732 let mut reader = SliceReader::new(&bytes);
733 let err = DebugLocationExpression::read_from(&mut reader).unwrap_err();
734 let DeserializationError::InvalidValue(message) = err else {
735 panic!("expected InvalidValue error");
736 };
737 assert!(message.contains("at most 256"));
738 }
739
740 #[test]
741 fn debug_location_expression_constructor_rejects_oversized_input() {
742 let operations =
743 vec![DebugLocationExpressionOp::Add; MAX_DEBUG_LOCATION_EXPRESSION_OPS + 1];
744 let error = DebugLocationExpression::new(operations).unwrap_err();
745
746 assert_eq!(error.operation_count(), MAX_DEBUG_LOCATION_EXPRESSION_OPS + 1);
747 }
748
749 #[test]
750 fn debug_var_info_set_value_location() {
751 let mut var = DebugVarInfo::new("x", DebugVarLocation::Stack(0));
752 var.set_value_location(DebugVarLocation::ResolvedFrameBase {
753 base: DebugFrameBase::Local(-2),
754 byte_offset: 12,
755 });
756 assert_eq!(
757 var.value_location(),
758 &DebugVarLocation::ResolvedFrameBase {
759 base: DebugFrameBase::Local(-2),
760 byte_offset: 12,
761 }
762 );
763 }
764
765 #[cfg(feature = "serde")]
766 #[test]
767 fn serde_round_trips_location_expressions() {
768 let expression = DebugLocationExpression::new(vec![
769 DebugLocationExpressionOp::ReadLocal(-2),
770 DebugLocationExpressionOp::DerefBytes,
771 ])
772 .unwrap();
773 let json = serde_json::to_string(&expression).unwrap();
774
775 assert_eq!(serde_json::from_str::<DebugLocationExpression>(&json).unwrap(), expression);
776 }
777
778 #[cfg(feature = "serde")]
779 #[test]
780 fn serde_rejects_oversized_location_expressions() {
781 let expression = DebugLocationExpression {
782 operations: vec![DebugLocationExpressionOp::Add; MAX_DEBUG_LOCATION_EXPRESSION_OPS + 1],
783 };
784 let json = serde_json::to_string(&expression).unwrap();
785 let error = serde_json::from_str::<DebugLocationExpression>(&json).unwrap_err();
786
787 assert!(error.to_string().contains("at most 256"));
788 }
789}