1use crate::{context::Context, pretty::DebugWithContext, ConstantContent, ConstantValue, Value};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
15pub struct Type(pub slotmap::DefaultKey);
16
17impl DebugWithContext for Type {
18 fn fmt_with_context(
19 &self,
20 formatter: &mut std::fmt::Formatter,
21 context: &Context,
22 ) -> std::fmt::Result {
23 self.get_content(context)
24 .fmt_with_context(formatter, context)
25 }
26}
27
28#[derive(Debug, Clone, DebugWithContext, Hash, PartialEq, Eq)]
29pub enum TypeContent {
30 Never,
31 Unit,
32 Bool,
33 Uint(u16),
34 B256,
35 StringSlice,
36 StringArray(u64),
37 Array(Type, u64),
38 Union(Vec<Type>),
39 Struct(Vec<Type>),
40 Slice,
41 Pointer,
42 TypedPointer(Type),
43 TypedSlice(Type),
44}
45
46impl Type {
47 fn get_or_create_unique_type(context: &mut Context, t: TypeContent) -> Type {
48 #[allow(clippy::map_entry)]
50 if !context.type_map.contains_key(&t) {
51 let new_type = Type(context.types.insert(t.clone()));
52 context.type_map.insert(t, new_type);
53 new_type
54 } else {
55 context.type_map.get(&t).copied().unwrap()
56 }
57 }
58
59 pub fn get_type(context: &Context, t: &TypeContent) -> Option<Type> {
61 context.type_map.get(t).copied()
62 }
63
64 pub fn create_basic_types(context: &mut Context) {
65 Self::get_or_create_unique_type(context, TypeContent::Never);
66 Self::get_or_create_unique_type(context, TypeContent::Unit);
67 Self::get_or_create_unique_type(context, TypeContent::Bool);
68 Self::get_or_create_unique_type(context, TypeContent::Uint(8));
69 Self::get_or_create_unique_type(context, TypeContent::Uint(16));
70 Self::get_or_create_unique_type(context, TypeContent::Uint(32));
71 Self::get_or_create_unique_type(context, TypeContent::Uint(64));
72 Self::get_or_create_unique_type(context, TypeContent::Uint(256));
73 Self::get_or_create_unique_type(context, TypeContent::B256);
74 Self::get_or_create_unique_type(context, TypeContent::Slice);
75 Self::get_or_create_unique_type(context, TypeContent::Pointer);
76 }
77
78 pub fn get_content<'a>(&self, context: &'a Context) -> &'a TypeContent {
80 &context.types[self.0]
81 }
82
83 pub fn get_never(context: &Context) -> Type {
85 Self::get_type(context, &TypeContent::Never).expect("create_basic_types not called")
86 }
87
88 pub fn get_unit(context: &Context) -> Type {
90 Self::get_type(context, &TypeContent::Unit).expect("create_basic_types not called")
91 }
92
93 pub fn get_bool(context: &Context) -> Type {
95 Self::get_type(context, &TypeContent::Bool).expect("create_basic_types not called")
96 }
97
98 pub fn new_uint(context: &mut Context, width: u16) -> Type {
100 Self::get_or_create_unique_type(context, TypeContent::Uint(width))
101 }
102
103 pub fn get_uint8(context: &Context) -> Type {
105 Self::get_type(context, &TypeContent::Uint(8)).expect("create_basic_types not called")
106 }
107
108 pub fn get_uint16(context: &Context) -> Type {
110 Self::get_type(context, &TypeContent::Uint(16)).expect("create_basic_types not called")
111 }
112
113 pub fn get_uint32(context: &Context) -> Type {
115 Self::get_type(context, &TypeContent::Uint(32)).expect("create_basic_types not called")
116 }
117
118 pub fn get_uint64(context: &Context) -> Type {
120 Self::get_type(context, &TypeContent::Uint(64)).expect("create_basic_types not called")
121 }
122
123 pub fn get_uint256(context: &Context) -> Type {
125 Self::get_type(context, &TypeContent::Uint(256)).expect("create_basic_types not called")
126 }
127
128 pub fn get_uint(context: &Context, width: u16) -> Option<Type> {
130 Self::get_type(context, &TypeContent::Uint(width))
131 }
132
133 pub fn get_b256(context: &Context) -> Type {
135 Self::get_type(context, &TypeContent::B256).expect("create_basic_types not called")
136 }
137
138 pub fn get_ptr(context: &Context) -> Type {
140 Self::get_type(context, &TypeContent::Pointer).expect("create_basic_types not called")
141 }
142
143 pub fn new_untyped_slice(context: &mut Context) -> Type {
144 Self::get_or_create_unique_type(context, TypeContent::Slice)
145 }
146
147 pub fn new_string_array(context: &mut Context, len: u64) -> Type {
149 Self::get_or_create_unique_type(context, TypeContent::StringArray(len))
150 }
151
152 pub fn new_array(context: &mut Context, elm_ty: Type, len: u64) -> Type {
154 Self::get_or_create_unique_type(context, TypeContent::Array(elm_ty, len))
155 }
156
157 pub fn new_union(context: &mut Context, fields: Vec<Type>) -> Type {
159 Self::get_or_create_unique_type(context, TypeContent::Union(fields))
160 }
161
162 pub fn new_struct(context: &mut Context, fields: Vec<Type>) -> Type {
164 Self::get_or_create_unique_type(context, TypeContent::Struct(fields))
165 }
166
167 pub fn new_typed_pointer(context: &mut Context, to_ty: Type) -> Type {
169 Self::get_or_create_unique_type(context, TypeContent::TypedPointer(to_ty))
170 }
171
172 pub fn get_slice(context: &Context) -> Type {
174 Self::get_type(context, &TypeContent::Slice).expect("create_basic_types not called")
175 }
176
177 pub fn get_typed_slice(context: &mut Context, item_ty: Type) -> Type {
179 Self::get_or_create_unique_type(context, TypeContent::TypedSlice(item_ty))
180 }
181
182 pub fn as_string(&self, context: &Context) -> String {
184 let sep_types_str = |agg_content: &Vec<Type>, sep: &str| {
185 agg_content
186 .iter()
187 .map(|ty| ty.as_string(context))
188 .collect::<Vec<_>>()
189 .join(sep)
190 };
191
192 match self.get_content(context) {
193 TypeContent::Never => "never".into(),
194 TypeContent::Unit => "()".into(),
195 TypeContent::Bool => "bool".into(),
196 TypeContent::Uint(nbits) => format!("u{nbits}"),
197 TypeContent::B256 => "b256".into(),
198 TypeContent::StringSlice => "str".into(),
199 TypeContent::StringArray(n) => format!("string<{n}>"),
200 TypeContent::Array(ty, cnt) => {
201 format!("[{}; {}]", ty.as_string(context), cnt)
202 }
203 TypeContent::Union(agg) => {
204 format!("( {} )", sep_types_str(agg, " | "))
205 }
206 TypeContent::Struct(agg) => {
207 format!("{{ {} }}", sep_types_str(agg, ", "))
208 }
209 TypeContent::Slice => "slice".into(),
210 TypeContent::Pointer => "ptr".into(),
211 TypeContent::TypedSlice(ty) => format!("__slice[{}]", ty.as_string(context)),
212 TypeContent::TypedPointer(ty) => format!("__ptr {}", ty.as_string(context)),
213 }
214 }
215
216 pub fn eq(&self, context: &Context, other: &Type) -> bool {
219 match (self.get_content(context), other.get_content(context)) {
220 (TypeContent::Unit, TypeContent::Unit) => true,
221 (TypeContent::Bool, TypeContent::Bool) => true,
222 (TypeContent::Uint(l), TypeContent::Uint(r)) => l == r,
223 (TypeContent::B256, TypeContent::B256) => true,
224
225 (TypeContent::StringSlice, TypeContent::StringSlice) => true,
226 (TypeContent::StringArray(l), TypeContent::StringArray(r)) => l == r,
227
228 (TypeContent::Array(l, llen), TypeContent::Array(r, rlen)) => {
229 llen == rlen && l.eq(context, r)
230 }
231
232 (TypeContent::TypedSlice(l), TypeContent::TypedSlice(r)) => l.eq(context, r),
233
234 (TypeContent::Struct(l), TypeContent::Struct(r))
235 | (TypeContent::Union(l), TypeContent::Union(r)) => {
236 l.len() == r.len() && l.iter().zip(r.iter()).all(|(l, r)| l.eq(context, r))
237 }
238 (_, TypeContent::Union(_)) => other.eq(context, self),
240 (TypeContent::Union(l), _) => l.iter().any(|field_ty| other.eq(context, field_ty)),
241 (TypeContent::Never, _) => true,
243 (TypeContent::Slice, TypeContent::Slice) => true,
244 (TypeContent::Pointer, TypeContent::Pointer) => true,
245 (TypeContent::TypedPointer(l), TypeContent::TypedPointer(r)) => l.eq(context, r),
246 _ => false,
247 }
248 }
249
250 pub fn is_never(&self, context: &Context) -> bool {
252 matches!(*self.get_content(context), TypeContent::Never)
253 }
254
255 pub fn is_bool(&self, context: &Context) -> bool {
257 matches!(*self.get_content(context), TypeContent::Bool)
258 }
259
260 pub fn is_unit(&self, context: &Context) -> bool {
262 matches!(*self.get_content(context), TypeContent::Unit)
263 }
264
265 pub fn is_uint(&self, context: &Context) -> bool {
267 matches!(*self.get_content(context), TypeContent::Uint(_))
268 }
269
270 pub fn is_uint8(&self, context: &Context) -> bool {
272 matches!(*self.get_content(context), TypeContent::Uint(8))
273 }
274
275 pub fn is_uint32(&self, context: &Context) -> bool {
277 matches!(*self.get_content(context), TypeContent::Uint(32))
278 }
279
280 pub fn is_uint64(&self, context: &Context) -> bool {
282 matches!(*self.get_content(context), TypeContent::Uint(64))
283 }
284
285 pub fn is_uint_of(&self, context: &Context, width: u16) -> bool {
287 matches!(*self.get_content(context), TypeContent::Uint(width_) if width == width_)
288 }
289
290 pub fn is_b256(&self, context: &Context) -> bool {
292 matches!(*self.get_content(context), TypeContent::B256)
293 }
294
295 pub fn is_string_slice(&self, context: &Context) -> bool {
297 matches!(*self.get_content(context), TypeContent::StringSlice)
298 }
299
300 pub fn is_string_array(&self, context: &Context) -> bool {
302 matches!(*self.get_content(context), TypeContent::StringArray(_))
303 }
304
305 pub fn is_array(&self, context: &Context) -> bool {
307 matches!(*self.get_content(context), TypeContent::Array(..))
308 }
309
310 pub fn is_union(&self, context: &Context) -> bool {
312 matches!(*self.get_content(context), TypeContent::Union(_))
313 }
314
315 pub fn is_struct(&self, context: &Context) -> bool {
317 matches!(*self.get_content(context), TypeContent::Struct(_))
318 }
319
320 pub fn is_enum(&self, context: &Context) -> bool {
322 if !self.is_struct(context) {
330 return false;
331 }
332
333 let field_tys = self.get_field_types(context);
334
335 field_tys.len() == 2 && field_tys[0].is_uint(context) && field_tys[1].is_union(context)
336 }
337
338 pub fn is_aggregate(&self, context: &Context) -> bool {
340 self.is_struct(context) || self.is_union(context) || self.is_array(context)
342 }
343
344 pub fn is_slice(&self, context: &Context) -> bool {
346 matches!(*self.get_content(context), TypeContent::Slice)
347 }
348
349 pub fn is_ptr(&self, context: &Context) -> bool {
352 matches!(
353 *self.get_content(context),
354 TypeContent::TypedPointer(_) | TypeContent::Pointer
355 )
356 }
357
358 pub fn get_pointee_type(&self, context: &Context) -> Option<Type> {
360 if let TypeContent::TypedPointer(to_ty) = self.get_content(context) {
361 Some(*to_ty)
362 } else {
363 None
364 }
365 }
366
367 pub fn get_uint_width(&self, context: &Context) -> Option<u16> {
369 if let TypeContent::Uint(width) = self.get_content(context) {
370 Some(*width)
371 } else {
372 None
373 }
374 }
375
376 pub fn get_indexed_type(&self, context: &Context, indices: &[u64]) -> Option<Type> {
378 if indices.is_empty() {
379 return None;
380 }
381
382 indices.iter().try_fold(*self, |ty, idx| {
383 ty.get_field_type(context, *idx)
384 .or_else(|| match ty.get_content(context) {
385 TypeContent::Array(ty, len) if idx < len => Some(*ty),
386 _ => None,
387 })
388 })
389 }
390
391 pub fn get_value_indexed_type(&self, context: &Context, indices: &[Value]) -> Option<Type> {
393 indices.iter().try_fold(*self, |ty, idx_val| {
398 idx_val
399 .get_constant(context)
400 .and_then(|const_ref| {
401 if let ConstantValue::Uint(n) = const_ref.get_content(context).value {
402 Some(n)
403 } else {
404 None
405 }
406 })
407 .and_then(|idx| ty.get_field_type(context, idx))
408 .or_else(|| ty.get_array_elem_type(context))
409 })
410 }
411
412 pub fn get_indexed_offset(&self, context: &Context, indices: &[u64]) -> Option<u64> {
416 indices
417 .iter()
418 .try_fold((*self, 0), |(ty, accum_offset), idx| {
419 if ty.is_struct(context) {
420 let prev_idxs_offset = (0..(*idx)).try_fold(0, |accum, pre_idx| {
423 ty.get_field_type(context, pre_idx)
424 .map(|field_ty| accum + field_ty.size(context).in_bytes_aligned())
425 })?;
426 ty.get_field_type(context, *idx)
427 .map(|field_ty| (field_ty, accum_offset + prev_idxs_offset))
428 } else if ty.is_union(context) {
429 let union_size_in_bytes = ty.size(context).in_bytes();
432 ty.get_field_type(context, *idx).map(|field_ty| {
433 (
434 field_ty,
435 accum_offset
436 + (union_size_in_bytes - field_ty.size(context).in_bytes()),
437 )
438 })
439 } else {
440 assert!(
441 ty.is_array(context),
442 "Expected aggregate type. Got {}.",
443 ty.as_string(context)
444 );
445 ty.get_array_elem_type(context).map(|elm_ty| {
447 let prev_idxs_offset = ty
448 .get_array_elem_type(context)
449 .unwrap()
450 .size(context)
451 .in_bytes()
452 * idx;
453 (elm_ty, accum_offset + prev_idxs_offset)
454 })
455 }
456 })
457 .map(|pair| pair.1)
458 }
459
460 pub fn get_value_indexed_offset(&self, context: &Context, indices: &[Value]) -> Option<u64> {
463 let const_indices: Vec<_> = indices
464 .iter()
465 .map_while(|idx| {
466 if let Some(ConstantContent {
467 value: ConstantValue::Uint(idx),
468 ty: _,
469 }) = idx.get_constant(context).map(|c| c.get_content(context))
470 {
471 Some(*idx)
472 } else {
473 None
474 }
475 })
476 .collect();
477 (const_indices.len() == indices.len())
478 .then(|| self.get_indexed_offset(context, &const_indices))
479 .flatten()
480 }
481
482 pub fn get_field_type(&self, context: &Context, idx: u64) -> Option<Type> {
483 if let TypeContent::Struct(fields) | TypeContent::Union(fields) = self.get_content(context)
484 {
485 fields.get(idx as usize).cloned()
486 } else {
487 None
489 }
490 }
491
492 pub fn get_array_elem_type(&self, context: &Context) -> Option<Type> {
494 if let TypeContent::Array(ty, _) = *self.get_content(context) {
495 Some(ty)
496 } else {
497 None
498 }
499 }
500
501 pub fn get_typed_slice_elem_type(&self, context: &Context) -> Option<Type> {
503 if let TypeContent::TypedSlice(ty) = *self.get_content(context) {
504 Some(ty)
505 } else {
506 None
507 }
508 }
509
510 pub fn get_array_len(&self, context: &Context) -> Option<u64> {
512 if let TypeContent::Array(_, n) = *self.get_content(context) {
513 Some(n)
514 } else {
515 None
516 }
517 }
518
519 pub fn get_string_len(&self, context: &Context) -> Option<u64> {
521 if let TypeContent::StringArray(n) = *self.get_content(context) {
522 Some(n)
523 } else {
524 None
525 }
526 }
527
528 pub fn get_field_types(&self, context: &Context) -> Vec<Type> {
530 match self.get_content(context) {
531 TypeContent::Struct(fields) | TypeContent::Union(fields) => fields.clone(),
532 _ => vec![],
533 }
534 }
535
536 pub fn get_struct_field_offset_and_type(
540 &self,
541 context: &Context,
542 field_idx: u64,
543 ) -> Option<(u64, Type)> {
544 if !self.is_struct(context) {
545 return None;
546 }
547
548 let field_idx = field_idx as usize;
549 let field_types = self.get_field_types(context);
550 let field_offs_in_bytes = field_types
551 .iter()
552 .take(field_idx)
553 .map(|field_ty| {
554 field_ty.size(context).in_bytes_aligned()
556 })
557 .sum::<u64>();
558
559 Some((field_offs_in_bytes, field_types[field_idx]))
560 }
561
562 pub fn get_union_field_offset_and_type(
566 &self,
567 context: &Context,
568 field_idx: u64,
569 ) -> Option<(u64, Type)> {
570 if !self.is_union(context) {
571 return None;
572 }
573
574 let field_idx = field_idx as usize;
575 let field_type = self.get_field_types(context)[field_idx];
576 let union_size_in_bytes = self.size(context).in_bytes();
577 let field_size_in_bytes = field_type.size(context).in_bytes();
580
581 Some((union_size_in_bytes - field_size_in_bytes, field_type))
583 }
584
585 pub fn size(&self, context: &Context) -> TypeSize {
589 match self.get_content(context) {
590 TypeContent::Uint(8) | TypeContent::Bool | TypeContent::Unit | TypeContent::Never => {
591 TypeSize::new(1)
592 }
593 TypeContent::Uint(16)
595 | TypeContent::Uint(32)
596 | TypeContent::Uint(64)
597 | TypeContent::TypedPointer(_)
598 | TypeContent::Pointer => TypeSize::new(8),
599 TypeContent::Uint(256) => TypeSize::new(32),
600 TypeContent::Uint(_) => unreachable!(),
601 TypeContent::Slice => TypeSize::new(16),
602 TypeContent::TypedSlice(..) => TypeSize::new(16),
603 TypeContent::B256 => TypeSize::new(32),
604 TypeContent::StringSlice => TypeSize::new(16),
605 TypeContent::StringArray(n) => {
606 TypeSize::new(super::size_bytes_round_up_to_word_alignment!(*n))
607 }
608 TypeContent::Array(el_ty, cnt) => TypeSize::new(cnt * el_ty.size(context).in_bytes()),
609 TypeContent::Struct(field_tys) => {
610 TypeSize::new(
612 field_tys
613 .iter()
614 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
615 .sum(),
616 )
617 }
618 TypeContent::Union(field_tys) => {
619 TypeSize::new(
621 field_tys
622 .iter()
623 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
624 .max()
625 .unwrap_or(0),
626 )
627 }
628 }
629 }
630}
631
632#[macro_export]
634macro_rules! size_bytes_round_up_to_word_alignment {
635 ($bytes_expr: expr) => {
636 ($bytes_expr + 7) - (($bytes_expr + 7) % 8)
637 };
638}
639
640pub trait TypeOption {
642 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool;
643}
644
645impl TypeOption for Option<Type> {
646 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool {
647 self.filter(|ty| pred(ty, context)).is_some()
648 }
649}
650
651#[derive(Clone, Debug)]
653pub struct TypeSize {
654 size_in_bytes: u64,
655}
656
657impl TypeSize {
658 pub(crate) fn new(size_in_bytes: u64) -> Self {
659 Self { size_in_bytes }
660 }
661
662 pub fn in_bytes(&self) -> u64 {
664 self.size_in_bytes
665 }
666
667 pub fn in_bytes_aligned(&self) -> u64 {
669 (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
670 }
671
672 pub fn in_words(&self) -> u64 {
674 self.size_in_bytes.div_ceil(8)
675 }
676}
677
678#[derive(Clone, Debug, serde::Serialize)]
684pub enum Padding {
685 Left { target_size: usize },
686 Right { target_size: usize },
687}
688
689impl Padding {
690 pub fn default_for_u8(_value: u8) -> Self {
692 Self::Right { target_size: 1 }
694 }
695
696 pub fn default_for_u64(_value: u64) -> Self {
698 Self::Right { target_size: 8 }
700 }
701
702 pub fn default_for_byte_array(value: &[u8]) -> Self {
704 Self::Right {
705 target_size: value.len(),
706 }
707 }
708
709 pub fn default_for_aggregate(aggregate_size: usize) -> Self {
712 Self::Right {
713 target_size: aggregate_size,
714 }
715 }
716
717 pub fn target_size(&self) -> usize {
719 use Padding::*;
720 match self {
721 Left { target_size } | Right { target_size } => *target_size,
722 }
723 }
724}
725
726#[cfg(test)]
727mod tests {
728 pub use super::*;
729 mod memory_layout {
731 use super::*;
732 use crate::{Backtrace, Context};
733 use once_cell::sync::Lazy;
734 use sway_features::ExperimentalFeatures;
735 use sway_types::SourceEngine;
736
737 #[test]
738 fn boolean() {
740 let context = create_context();
741
742 let s_bool = Type::get_bool(&context).size(&context);
743
744 assert_eq!(s_bool.in_bytes(), 1);
745 }
746
747 #[test]
748 fn unit() {
750 let context = create_context();
751
752 let s_unit = Type::get_unit(&context).size(&context);
753
754 assert_eq!(s_unit.in_bytes(), 1);
755 }
756
757 #[test]
758 fn unsigned_u8() {
760 let context = create_context();
761
762 let s_u8 = Type::get_uint8(&context).size(&context);
763
764 assert_eq!(s_u8.in_bytes(), 1);
765 }
766
767 #[test]
768 fn unsigned_u16_u32_u64() {
770 let context = create_context();
771
772 let s_u16 = Type::get_uint16(&context).size(&context);
773 let s_u32 = Type::get_uint32(&context).size(&context);
774 let s_u64 = Type::get_uint64(&context).size(&context);
775
776 assert_eq!(s_u16.in_bytes(), 8);
777 assert_eq!(s_u16.in_bytes(), s_u16.in_bytes_aligned());
778
779 assert_eq!(s_u32.in_bytes(), 8);
780 assert_eq!(s_u32.in_bytes(), s_u32.in_bytes_aligned());
781
782 assert_eq!(s_u64.in_bytes(), 8);
783 assert_eq!(s_u64.in_bytes(), s_u64.in_bytes_aligned());
784 }
785
786 #[test]
787 fn unsigned_u256() {
789 let context = create_context();
790
791 let s_u256 = Type::get_uint256(&context).size(&context);
792
793 assert_eq!(s_u256.in_bytes(), 32);
794 assert_eq!(s_u256.in_bytes(), s_u256.in_bytes_aligned());
795 }
796
797 #[test]
798 fn pointer() {
800 let mut context = create_context();
801
802 for ty in all_sample_types(&mut context) {
803 let s_ptr = Type::new_typed_pointer(&mut context, ty).size(&context);
804
805 assert_eq!(s_ptr.in_bytes(), 8);
806 assert_eq!(s_ptr.in_bytes(), s_ptr.in_bytes_aligned());
807 }
808
809 assert_eq!(Type::get_ptr(&context).size(&context).in_bytes(), 8);
810 assert_eq!(
811 Type::get_ptr(&context).size(&context).in_bytes(),
812 Type::get_ptr(&context).size(&context).in_bytes_aligned()
813 );
814 }
815
816 #[test]
817 fn slice() {
821 let context = create_context();
822
823 let s_slice = Type::get_slice(&context).size(&context);
824
825 assert_eq!(s_slice.in_bytes(), 16);
826 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
827 }
828
829 #[test]
830 fn b256() {
832 let context = create_context();
833
834 let s_b256 = Type::get_b256(&context).size(&context);
835
836 assert_eq!(s_b256.in_bytes(), 32);
837 assert_eq!(s_b256.in_bytes(), s_b256.in_bytes_aligned());
838 }
839
840 #[test]
841 fn string_slice() {
845 let mut context = create_context();
846
847 let s_slice = Type::get_or_create_unique_type(&mut context, TypeContent::StringSlice)
848 .size(&context);
849
850 assert_eq!(s_slice.in_bytes(), 16);
851 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
852 }
853
854 #[test]
855 fn string_array() {
867 let mut context = create_context();
868
869 for (str_array_ty, len) in sample_string_arrays(&mut context) {
870 assert!(str_array_ty.is_string_array(&context)); let s_str_array = str_array_ty.size(&context);
873
874 assert_eq!(str_array_ty.get_string_len(&context).unwrap(), len);
875
876 assert_eq!(
877 s_str_array.in_bytes(),
878 size_bytes_round_up_to_word_alignment!(len)
879 );
880 assert_eq!(s_str_array.in_bytes(), s_str_array.in_bytes_aligned());
881 }
882 }
883
884 #[test]
885 fn array() {
888 let mut context = create_context();
889
890 for (array_ty, len, elem_size) in sample_arrays(&mut context) {
891 assert!(array_ty.is_array(&context)); let s_array = array_ty.size(&context);
894
895 assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
896
897 assert_eq!(s_array.in_bytes(), len * elem_size);
899
900 for elem_index in 0..len {
901 let elem_offset = array_ty
902 .get_indexed_offset(&context, &[elem_index])
903 .unwrap();
904
905 assert_eq!(elem_offset, elem_index * elem_size);
907 }
908 }
909 }
910
911 #[test]
912 fn r#struct() {
921 let mut context = create_context();
922
923 for (struct_ty, fields) in sample_structs(&mut context) {
924 assert!(struct_ty.is_struct(&context)); let s_struct = struct_ty.size(&context);
927
928 assert_eq!(
932 s_struct.in_bytes(),
933 fields
934 .iter()
935 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
936 .sum::<u64>()
937 );
938 assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
940
941 for field_index in 0..fields.len() {
942 let expected_offset = fields
944 .iter()
945 .take(field_index)
946 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
947 .sum::<u64>();
948
949 let field_offset = struct_ty
950 .get_indexed_offset(&context, &[field_index as u64])
951 .unwrap();
952 assert_eq!(field_offset, expected_offset);
953
954 let (field_offset, field_type) = struct_ty
955 .get_struct_field_offset_and_type(&context, field_index as u64)
956 .unwrap();
957 assert_eq!(field_offset, expected_offset);
958 assert_eq!(field_type, fields[field_index].0);
959 }
960 }
961 }
962
963 #[test]
964 fn union() {
976 let mut context = create_context();
977
978 for (union_ty, variants) in sample_unions(&mut context) {
979 assert!(union_ty.is_union(&context)); let s_union = union_ty.size(&context);
982
983 assert_eq!(
987 s_union.in_bytes(),
988 variants
989 .iter()
990 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
991 .max()
992 .unwrap_or_default()
993 );
994 assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
996
997 for (variant_index, variant) in variants.iter().enumerate() {
998 let expected_offset = s_union.in_bytes() - variant.1;
1001
1002 let variant_offset = union_ty
1003 .get_indexed_offset(&context, &[variant_index as u64])
1004 .unwrap();
1005 assert_eq!(variant_offset, expected_offset);
1006
1007 let (variant_offset, field_type) = union_ty
1008 .get_union_field_offset_and_type(&context, variant_index as u64)
1009 .unwrap();
1010 assert_eq!(variant_offset, expected_offset);
1011 assert_eq!(field_type, variant.0);
1012 }
1013 }
1014 }
1015
1016 static SOURCE_ENGINE: Lazy<SourceEngine> = Lazy::new(SourceEngine::default);
1021
1022 fn create_context() -> Context<'static> {
1023 Context::new(
1024 &SOURCE_ENGINE,
1025 ExperimentalFeatures::default(),
1026 Backtrace::default(),
1027 )
1028 }
1029
1030 fn sample_non_aggregate_types(context: &mut Context) -> Vec<Type> {
1034 let mut types = vec![
1035 Type::get_bool(context),
1036 Type::get_unit(context),
1037 Type::get_uint(context, 8).unwrap(),
1038 Type::get_uint(context, 16).unwrap(),
1039 Type::get_uint(context, 32).unwrap(),
1040 Type::get_uint(context, 64).unwrap(),
1041 Type::get_uint(context, 256).unwrap(),
1042 Type::get_b256(context),
1043 Type::get_slice(context),
1044 Type::get_or_create_unique_type(context, TypeContent::StringSlice),
1045 ];
1046
1047 types.extend(
1048 sample_string_arrays(context)
1049 .into_iter()
1050 .map(|(string_array, _)| string_array),
1051 );
1052
1053 types
1054 }
1055
1056 fn sample_string_arrays(context: &mut Context) -> Vec<(Type, u64)> {
1059 let mut types = vec![];
1060
1061 for len in [0, 1, 7, 8, 15] {
1062 types.push((Type::new_string_array(context, len), len));
1063 }
1064
1065 types
1066 }
1067
1068 fn sample_arrays(context: &mut Context) -> Vec<(Type, u64, u64)> {
1072 let mut types = vec![];
1073
1074 for len in [0, 1, 7, 8, 15] {
1075 for ty in sample_non_aggregate_types(context) {
1076 types.push((
1079 Type::new_array(context, ty, len),
1080 len,
1081 ty.size(context).in_bytes(),
1082 ));
1083 }
1084
1085 for (array_ty, array_len, elem_size) in sample_arrays_to_embed(context) {
1086 types.push((
1090 Type::new_array(context, array_ty, len),
1091 len,
1092 array_len * elem_size,
1093 ));
1094 }
1095
1096 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1097 types.push((Type::new_array(context, struct_ty, len), len, struct_size));
1098 }
1099 }
1100
1101 types
1102 }
1103
1104 fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1108 let mut types = vec![];
1109
1110 types.push((Type::new_struct(context, vec![]), vec![]));
1112
1113 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1115 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1117
1118 let mut fields = vec![];
1120 for ty in sample_non_aggregate_types(context) {
1121 fields.push((ty, ty.size(context).in_bytes()));
1124 }
1125 for (array_ty, len, elem_size) in sample_arrays(context) {
1126 fields.push((array_ty, len * elem_size));
1131 }
1132 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1133 fields.push((struct_ty, struct_size));
1134 }
1135
1136 types.push((
1137 Type::new_struct(
1138 context,
1139 fields.iter().map(|(field_ty, _)| *field_ty).collect(),
1140 ),
1141 fields,
1142 ));
1143
1144 return types;
1145
1146 fn add_structs_with_non_aggregate_types_of_length_in_bytes(
1147 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1148 context: &mut Context,
1149 field_type_size_in_bytes: u64,
1150 ) {
1151 for ty in sample_non_aggregate_types(context) {
1152 if ty.size(context).in_bytes() != field_type_size_in_bytes {
1153 continue;
1154 }
1155
1156 types.push((
1157 Type::new_struct(context, vec![ty]),
1158 vec![(ty, field_type_size_in_bytes)],
1159 ));
1160 }
1161 }
1162 }
1163
1164 fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1168 let mut types = vec![];
1169
1170 types.push((Type::new_union(context, vec![]), vec![]));
1172
1173 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1175 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1177
1178 let mut variants = vec![];
1183 for ty in sample_non_aggregate_types(context) {
1184 variants.push((ty, ty.size(context).in_bytes()));
1185 }
1186 for (array_ty, len, elem_size) in sample_arrays(context) {
1187 variants.push((array_ty, len * elem_size));
1188 }
1189 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1190 variants.push((struct_ty, struct_size));
1191 }
1192
1193 types.push((
1194 Type::new_union(
1195 context,
1196 variants.iter().map(|(field_ty, _)| *field_ty).collect(),
1197 ),
1198 variants,
1199 ));
1200
1201 return types;
1202
1203 fn add_unions_with_non_aggregate_types_of_length_in_bytes(
1204 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1205 context: &mut Context,
1206 variant_type_size_in_bytes: u64,
1207 ) {
1208 for ty in sample_non_aggregate_types(context) {
1209 if ty.size(context).in_bytes() != variant_type_size_in_bytes {
1210 continue;
1211 }
1212
1213 types.push((
1214 Type::new_union(context, vec![ty]),
1215 vec![(ty, variant_type_size_in_bytes)],
1216 ));
1217 }
1218 }
1219 }
1220
1221 fn sample_arrays_to_embed(context: &mut Context) -> Vec<(Type, u64, u64)> {
1225 let mut types = vec![];
1226
1227 for len in [0, 1, 7, 8, 15] {
1228 for elem_ty in sample_non_aggregate_types(context) {
1229 types.push((
1230 Type::new_array(context, elem_ty, len),
1231 len,
1232 elem_ty.size(context).in_bytes(),
1233 ));
1234 }
1235 }
1236
1237 types
1238 }
1239
1240 fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1243 let mut types = vec![];
1244
1245 for field_ty in sample_non_aggregate_types(context) {
1247 types.push((
1251 Type::new_struct(context, vec![field_ty]),
1252 field_ty.size(context).in_bytes_aligned(),
1253 ));
1254 }
1255
1256 let field_types = sample_non_aggregate_types(context);
1258 for (index, first_field_ty) in field_types.iter().enumerate() {
1259 for second_field_type in field_types.iter().skip(index) {
1260 let struct_size = first_field_ty.size(context).in_bytes_aligned()
1263 + second_field_type.size(context).in_bytes_aligned();
1264 types.push((
1265 Type::new_struct(context, vec![*first_field_ty, *second_field_type]),
1266 struct_size,
1267 ));
1268 }
1269 }
1270
1271 let field_types = sample_non_aggregate_types(context);
1273 let struct_size = field_types
1274 .iter()
1275 .map(|ty| ty.size(context).in_bytes_aligned())
1276 .sum();
1277 types.push((Type::new_struct(context, field_types), struct_size));
1278
1279 types
1280 }
1281
1282 fn all_sample_types(context: &mut Context) -> Vec<Type> {
1285 let mut types = vec![];
1286
1287 types.extend(sample_non_aggregate_types(context));
1288 types.extend(
1289 sample_arrays(context)
1290 .into_iter()
1291 .map(|(array_ty, _, _)| array_ty),
1292 );
1293 types.extend(
1294 sample_structs(context)
1295 .into_iter()
1296 .map(|(array_ty, __)| array_ty),
1297 );
1298
1299 types
1300 }
1301 }
1302}