1use crate::indexes::{FunctionIndex, GlobalIndex};
2use crate::units::Pages;
3use std::borrow::ToOwned;
4use std::boxed::Box;
5use std::fmt;
6use std::format;
7use std::string::{String, ToString};
8use std::vec::Vec;
9
10use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
11#[cfg(feature = "enable-serde")]
12use serde::{Deserialize, Serialize};
13
14#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)]
20#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
22#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
23#[rkyv(derive(Debug), compare(PartialEq))]
24#[repr(u8)]
25pub enum Type {
26 I32,
28 I64,
30 F32,
32 F64,
34 V128,
36 ExternRef, FuncRef,
40 ExceptionRef,
42}
43
44impl Type {
45 pub fn is_num(self) -> bool {
48 matches!(
49 self,
50 Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128
51 )
52 }
53
54 pub fn is_ref(self) -> bool {
56 matches!(self, Self::ExternRef | Self::FuncRef | Self::ExceptionRef)
57 }
58
59 pub const fn bit_size(self, pointer_width: usize) -> usize {
64 match self {
65 Self::I32 | Self::F32 | Self::ExceptionRef => 32,
66 Self::I64 | Self::F64 => 64,
67 Self::ExternRef | Self::FuncRef => pointer_width,
68 Self::V128 => 128,
69 }
70 }
71}
72
73impl fmt::Display for Type {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 write!(f, "{self:?}")
76 }
77}
78
79#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
81#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
82#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
83#[rkyv(derive(Debug), compare(PartialEq))]
84pub struct V128(pub(crate) [u8; 16]);
85
86#[cfg(feature = "artifact-size")]
87impl loupe::MemoryUsage for V128 {
88 fn size_of_val(&self, _tracker: &mut dyn loupe::MemoryUsageTracker) -> usize {
89 16 * 8
90 }
91}
92
93impl V128 {
94 pub fn bytes(&self) -> &[u8; 16] {
96 &self.0
97 }
98 pub fn iter(&self) -> impl Iterator<Item = &u8> {
100 self.0.iter()
101 }
102
103 pub fn to_vec(self) -> Vec<u8> {
105 self.0.to_vec()
106 }
107
108 pub fn as_slice(&self) -> &[u8] {
110 &self.0[..]
111 }
112}
113
114impl From<[u8; 16]> for V128 {
115 fn from(array: [u8; 16]) -> Self {
116 Self(array)
117 }
118}
119
120impl From<&[u8]> for V128 {
121 fn from(slice: &[u8]) -> Self {
122 assert_eq!(slice.len(), 16);
123 let mut buffer = [0; 16];
124 buffer.copy_from_slice(slice);
125 Self(buffer)
126 }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
137#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
138#[rkyv(derive(Debug))]
139pub enum ExternType {
140 Function(FunctionType),
142 Global(GlobalType),
144 Table(TableType),
146 Memory(MemoryType),
148 Tag(TagType),
150}
151
152fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
153 let GlobalType {
154 ty: exported_ty,
155 mutability: exported_mutability,
156 } = exported;
157 let GlobalType {
158 ty: imported_ty,
159 mutability: imported_mutability,
160 } = imported;
161
162 exported_ty == imported_ty && imported_mutability == exported_mutability
163}
164
165fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool {
166 match exported_type {
167 Type::FuncRef => true,
168 _ => imported_type == exported_type,
169 }
170}
171
172fn is_table_compatible(
173 exported: &TableType,
174 imported: &TableType,
175 imported_runtime_size: Option<u32>,
176) -> bool {
177 let TableType {
178 ty: exported_ty,
179 minimum: exported_minimum,
180 maximum: exported_maximum,
181 ..
182 } = exported;
183 let TableType {
184 ty: imported_ty,
185 minimum: imported_minimum,
186 maximum: imported_maximum,
187 ..
188 } = imported;
189
190 is_table_element_type_compatible(*exported_ty, *imported_ty)
191 && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
192 && (imported_maximum.is_none()
193 || (!exported_maximum.is_none()
194 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
195}
196
197fn is_memory_compatible(
198 exported: &MemoryType,
199 imported: &MemoryType,
200 imported_runtime_size: Option<u32>,
201) -> bool {
202 let MemoryType {
203 minimum: exported_minimum,
204 maximum: exported_maximum,
205 shared: exported_shared,
206 } = exported;
207 let MemoryType {
208 minimum: imported_minimum,
209 maximum: imported_maximum,
210 shared: imported_shared,
211 } = imported;
212
213 imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
214 && (imported_maximum.is_none()
215 || (!exported_maximum.is_none()
216 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
217 && exported_shared == imported_shared
218}
219
220macro_rules! accessors {
221 ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
222 pub fn $get(&self) -> Option<&$ty> {
225 if let Self::$variant(e) = self {
226 Some(e)
227 } else {
228 None
229 }
230 }
231
232 pub fn $unwrap(&self) -> &$ty {
239 self.$get().expect(concat!("expected ", stringify!($ty)))
240 }
241 )*)
242}
243
244impl ExternType {
245 accessors! {
246 (Function(FunctionType) func unwrap_func)
247 (Global(GlobalType) global unwrap_global)
248 (Table(TableType) table unwrap_table)
249 (Memory(MemoryType) memory unwrap_memory)
250 }
251 pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
253 match (self, other) {
254 (Self::Function(a), Self::Function(b)) => a == b,
255 (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
256 (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
257 (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
258 (Self::Tag(a), Self::Tag(b)) => a == b,
259 _ => false,
261 }
262 }
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
272#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
273#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
274#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
275#[rkyv(derive(Debug))]
276pub struct FunctionType {
277 params: Box<[Type]>,
279 results: Box<[Type]>,
281}
282
283impl FunctionType {
284 pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
286 where
287 Params: Into<Box<[Type]>>,
288 Returns: Into<Box<[Type]>>,
289 {
290 Self {
291 params: params.into(),
292 results: returns.into(),
293 }
294 }
295
296 pub fn params(&self) -> &[Type] {
298 &self.params
299 }
300
301 pub fn results(&self) -> &[Type] {
303 &self.results
304 }
305
306 pub fn signature_hash(&self) -> u32 {
308 let mut hasher = crc32fast::Hasher::new();
309 hasher.update(&self.results.len().to_le_bytes());
310 hasher.update(&self.params.len().to_le_bytes());
311 for ty in self.results.iter().chain(self.params.iter()) {
312 hasher.update(&[*ty as u8]);
313 }
314 hasher.finalize()
315 }
316}
317
318impl fmt::Display for FunctionType {
319 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
320 let params = self
321 .params
322 .iter()
323 .map(|p| format!("{p:?}"))
324 .collect::<Vec<_>>()
325 .join(", ");
326 let results = self
327 .results
328 .iter()
329 .map(|p| format!("{p:?}"))
330 .collect::<Vec<_>>()
331 .join(", ");
332 write!(f, "[{params}] -> [{results}]")
333 }
334}
335
336macro_rules! implement_from_pair_to_functiontype {
339 ($($N:literal,$M:literal)+) => {
340 $(
341 impl From<([Type; $N], [Type; $M])> for FunctionType {
342 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
343 Self::new(pair.0, pair.1)
344 }
345 }
346 )+
347 }
348}
349
350implement_from_pair_to_functiontype! {
351 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
352 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
353 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
354 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
355 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
356 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
357 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
358 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
359 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
360 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
361}
362
363impl From<&Self> for FunctionType {
364 fn from(as_ref: &Self) -> Self {
365 as_ref.clone()
366 }
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
371#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
372#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
373#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
374#[repr(u8)]
375pub enum Mutability {
376 Const,
378 Var,
380}
381
382impl Mutability {
383 pub fn is_mutable(self) -> bool {
385 self.into()
386 }
387}
388
389impl From<bool> for Mutability {
390 fn from(value: bool) -> Self {
391 if value { Self::Var } else { Self::Const }
392 }
393}
394
395impl From<Mutability> for bool {
396 fn from(value: Mutability) -> Self {
397 match value {
398 Mutability::Var => true,
399 Mutability::Const => false,
400 }
401 }
402}
403
404#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
406#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
407#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
408#[rkyv(derive(Debug), compare(PartialEq))]
409pub struct GlobalType {
410 pub ty: Type,
412 pub mutability: Mutability,
414}
415
416impl GlobalType {
424 pub fn new(ty: Type, mutability: Mutability) -> Self {
435 Self { ty, mutability }
436 }
437}
438
439impl fmt::Display for GlobalType {
440 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441 let mutability = match self.mutability {
442 Mutability::Const => "constant",
443 Mutability::Var => "mutable",
444 };
445 write!(f, "{} ({})", self.ty, mutability)
446 }
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
452#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
453#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
454#[rkyv(derive(Debug), compare(PartialEq))]
455pub struct InitExpr {
456 pub ops: Box<[InitExprOp]>,
458}
459
460impl InitExpr {
461 pub fn new<Ops>(ops: Ops) -> Self
463 where
464 Ops: Into<Box<[InitExprOp]>>,
465 {
466 Self { ops: ops.into() }
467 }
468
469 pub fn ops(&self) -> &[InitExprOp] {
471 &self.ops
472 }
473}
474
475#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
477#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
478#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
479#[rkyv(derive(Debug), compare(PartialEq))]
480#[repr(u8)]
481pub enum InitExprOp {
482 GlobalGetI32(GlobalIndex),
484 GlobalGetI64(GlobalIndex),
486 I32Const(i32),
488 I32Add,
490 I32Sub,
492 I32Mul,
494 I64Const(i64),
496 I64Add,
498 I64Sub,
500 I64Mul,
502}
503
504impl InitExprOp {
505 pub fn is_32bit_expression(&self) -> bool {
507 match self {
508 Self::GlobalGetI32(..)
509 | Self::I32Const(_)
510 | Self::I32Add
511 | Self::I32Sub
512 | Self::I32Mul => true,
513 Self::GlobalGetI64(_)
514 | Self::I64Const(_)
515 | Self::I64Add
516 | Self::I64Sub
517 | Self::I64Mul => false,
518 }
519 }
520}
521
522#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
525#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
526#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
527#[rkyv(derive(Debug), compare(PartialEq))]
528#[repr(u8)]
529pub enum GlobalInit {
530 I32Const(i32),
532 I64Const(i64),
534 F32Const(f32),
536 F64Const(f64),
538 V128Const(V128),
540 GetGlobal(GlobalIndex),
542 RefNullConst,
547 RefFunc(FunctionIndex),
549 Expr(InitExpr),
551}
552
553#[derive(Debug, Clone, PartialEq, Eq, Hash)]
559#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
560#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
561#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
562#[rkyv(derive(Debug))]
563pub enum TagKind {
564 Exception,
566}
567
568#[derive(Debug, Clone, PartialEq, Eq, Hash)]
571#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
572#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
573#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
574#[rkyv(derive(Debug))]
575pub struct TagType {
576 pub kind: TagKind,
578 pub params: Box<[Type]>,
580}
581
582impl TagType {
583 pub fn new<Params>(kind: TagKind, params: Params) -> Self
585 where
586 Params: Into<Box<[Type]>>,
587 {
588 Self {
589 kind,
590 params: params.into(),
591 }
592 }
593
594 pub fn params(&self) -> &[Type] {
596 &self.params
597 }
598
599 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
601 Self {
602 kind,
603 params: ty.params().into(),
604 }
605 }
606}
607
608impl fmt::Display for TagType {
609 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610 write!(f, "({:?}) {:?}", self.kind, self.params(),)
611 }
612}
613
614#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
622#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
623#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
624#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
625#[rkyv(derive(Debug))]
626pub struct TableType {
627 pub ty: Type,
629 pub minimum: u32,
631 pub maximum: Option<u32>,
633 pub readonly: bool,
635}
636
637impl TableType {
638 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
641 Self {
642 ty,
643 minimum,
644 maximum,
645 readonly: false,
646 }
647 }
648
649 pub fn is_fixed_funcref_table(&self) -> bool {
651 matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
652 }
653}
654
655impl fmt::Display for TableType {
656 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657 if let Some(maximum) = self.maximum {
658 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
659 } else {
660 write!(f, "{} ({}..)", self.ty, self.minimum)
661 }
662 }
663}
664
665#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
672#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
673#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
674#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
675#[rkyv(derive(Debug))]
676pub struct MemoryType {
677 pub minimum: Pages,
679 pub maximum: Option<Pages>,
681 pub shared: bool,
683}
684
685impl MemoryType {
686 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
689 where
690 IntoPages: Into<Pages>,
691 {
692 Self {
693 minimum: minimum.into(),
694 maximum: maximum.map(Into::into),
695 shared,
696 }
697 }
698}
699
700impl fmt::Display for MemoryType {
701 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702 let shared = if self.shared { "shared" } else { "not shared" };
703 if let Some(maximum) = self.maximum {
704 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
705 } else {
706 write!(f, "{} ({:?}..)", shared, self.minimum)
707 }
708 }
709}
710
711#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
720#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
721pub struct ImportType<T = ExternType> {
722 module: String,
723 name: String,
724 ty: T,
725}
726
727impl<T> ImportType<T> {
728 pub fn new(module: &str, name: &str, ty: T) -> Self {
731 Self {
732 module: module.to_owned(),
733 name: name.to_owned(),
734 ty,
735 }
736 }
737
738 pub fn module(&self) -> &str {
740 &self.module
741 }
742
743 pub fn name(&self) -> &str {
746 &self.name
747 }
748
749 pub fn ty(&self) -> &T {
751 &self.ty
752 }
753}
754
755#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
767#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
768pub struct ExportType<T = ExternType> {
769 name: String,
770 ty: T,
771}
772
773impl<T> ExportType<T> {
774 pub fn new(name: &str, ty: T) -> Self {
777 Self {
778 name: name.to_string(),
779 ty,
780 }
781 }
782
783 pub fn name(&self) -> &str {
785 &self.name
786 }
787
788 pub fn ty(&self) -> &T {
790 &self.ty
791 }
792}
793
794#[cfg(test)]
795mod tests {
796 use super::*;
797
798 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
799 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
800 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
801 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
802
803 #[test]
804 fn convert_tuple_to_functiontype() {
805 let ty: FunctionType = VOID_TO_VOID.into();
806 assert_eq!(ty.params().len(), 0);
807 assert_eq!(ty.results().len(), 0);
808
809 let ty: FunctionType = I32_I32_TO_VOID.into();
810 assert_eq!(ty.params().len(), 2);
811 assert_eq!(ty.params()[0], Type::I32);
812 assert_eq!(ty.params()[1], Type::I32);
813 assert_eq!(ty.results().len(), 0);
814
815 let ty: FunctionType = V128_I64_TO_I32.into();
816 assert_eq!(ty.params().len(), 2);
817 assert_eq!(ty.params()[0], Type::V128);
818 assert_eq!(ty.params()[1], Type::I64);
819 assert_eq!(ty.results().len(), 1);
820 assert_eq!(ty.results()[0], Type::I32);
821
822 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
823 assert_eq!(ty.params().len(), 9);
824 assert_eq!(ty.results().len(), 9);
825 }
826
827 #[test]
828 fn signature_hash_is_stable() {
829 let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
830 assert_eq!(ty.signature_hash(), ty.signature_hash());
831 }
832
833 #[test]
834 fn signature_hash_distinguishes() {
835 let left: FunctionType = ([Type::I32], [Type::I64]).into();
836 let right: FunctionType = ([Type::I64], [Type::I32]).into();
837 assert_ne!(left.signature_hash(), right.signature_hash());
838
839 let left: FunctionType = ([], [Type::I32, Type::I64]).into();
840 let right: FunctionType = ([Type::I32], [Type::I64]).into();
841 assert_ne!(left.signature_hash(), right.signature_hash());
842 }
843}