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_compatible(
166 exported: &TableType,
167 imported: &TableType,
168 imported_runtime_size: Option<u32>,
169) -> bool {
170 let TableType {
171 ty: exported_ty,
172 minimum: exported_minimum,
173 maximum: exported_maximum,
174 ..
175 } = exported;
176 let TableType {
177 ty: imported_ty,
178 minimum: imported_minimum,
179 maximum: imported_maximum,
180 ..
181 } = imported;
182
183 exported_ty == imported_ty
184 && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
185 && (imported_maximum.is_none()
186 || (!exported_maximum.is_none()
187 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
188}
189
190fn is_memory_compatible(
191 exported: &MemoryType,
192 imported: &MemoryType,
193 imported_runtime_size: Option<u32>,
194) -> bool {
195 let MemoryType {
196 minimum: exported_minimum,
197 maximum: exported_maximum,
198 shared: exported_shared,
199 } = exported;
200 let MemoryType {
201 minimum: imported_minimum,
202 maximum: imported_maximum,
203 shared: imported_shared,
204 } = imported;
205
206 imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
207 && (imported_maximum.is_none()
208 || (!exported_maximum.is_none()
209 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
210 && exported_shared == imported_shared
211}
212
213macro_rules! accessors {
214 ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
215 pub fn $get(&self) -> Option<&$ty> {
218 if let Self::$variant(e) = self {
219 Some(e)
220 } else {
221 None
222 }
223 }
224
225 pub fn $unwrap(&self) -> &$ty {
232 self.$get().expect(concat!("expected ", stringify!($ty)))
233 }
234 )*)
235}
236
237impl ExternType {
238 accessors! {
239 (Function(FunctionType) func unwrap_func)
240 (Global(GlobalType) global unwrap_global)
241 (Table(TableType) table unwrap_table)
242 (Memory(MemoryType) memory unwrap_memory)
243 }
244 pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
246 match (self, other) {
247 (Self::Function(a), Self::Function(b)) => a == b,
248 (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
249 (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
250 (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
251 (Self::Tag(a), Self::Tag(b)) => a == b,
252 _ => false,
254 }
255 }
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
265#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
266#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
267#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
268#[rkyv(derive(Debug))]
269pub struct FunctionType {
270 params: Box<[Type]>,
272 results: Box<[Type]>,
274}
275
276impl FunctionType {
277 pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
279 where
280 Params: Into<Box<[Type]>>,
281 Returns: Into<Box<[Type]>>,
282 {
283 Self {
284 params: params.into(),
285 results: returns.into(),
286 }
287 }
288
289 pub fn params(&self) -> &[Type] {
291 &self.params
292 }
293
294 pub fn results(&self) -> &[Type] {
296 &self.results
297 }
298
299 pub fn signature_hash(&self) -> u32 {
301 let mut hasher = crc32fast::Hasher::new();
302 hasher.update(&self.results.len().to_le_bytes());
303 hasher.update(&self.params.len().to_le_bytes());
304 for ty in self.results.iter().chain(self.params.iter()) {
305 hasher.update(&[*ty as u8]);
306 }
307 hasher.finalize()
308 }
309}
310
311impl fmt::Display for FunctionType {
312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313 let params = self
314 .params
315 .iter()
316 .map(|p| format!("{p:?}"))
317 .collect::<Vec<_>>()
318 .join(", ");
319 let results = self
320 .results
321 .iter()
322 .map(|p| format!("{p:?}"))
323 .collect::<Vec<_>>()
324 .join(", ");
325 write!(f, "[{params}] -> [{results}]")
326 }
327}
328
329macro_rules! implement_from_pair_to_functiontype {
332 ($($N:literal,$M:literal)+) => {
333 $(
334 impl From<([Type; $N], [Type; $M])> for FunctionType {
335 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
336 Self::new(pair.0, pair.1)
337 }
338 }
339 )+
340 }
341}
342
343implement_from_pair_to_functiontype! {
344 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
345 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
346 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
347 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
348 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
349 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
350 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
351 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
352 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
353 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
354}
355
356impl From<&Self> for FunctionType {
357 fn from(as_ref: &Self) -> Self {
358 as_ref.clone()
359 }
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
364#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
365#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
366#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
367#[repr(u8)]
368pub enum Mutability {
369 Const,
371 Var,
373}
374
375impl Mutability {
376 pub fn is_mutable(self) -> bool {
378 self.into()
379 }
380}
381
382impl From<bool> for Mutability {
383 fn from(value: bool) -> Self {
384 if value { Self::Var } else { Self::Const }
385 }
386}
387
388impl From<Mutability> for bool {
389 fn from(value: Mutability) -> Self {
390 match value {
391 Mutability::Var => true,
392 Mutability::Const => false,
393 }
394 }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
399#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
400#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
401#[rkyv(derive(Debug), compare(PartialEq))]
402pub struct GlobalType {
403 pub ty: Type,
405 pub mutability: Mutability,
407}
408
409impl GlobalType {
417 pub fn new(ty: Type, mutability: Mutability) -> Self {
428 Self { ty, mutability }
429 }
430}
431
432impl fmt::Display for GlobalType {
433 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434 let mutability = match self.mutability {
435 Mutability::Const => "constant",
436 Mutability::Var => "mutable",
437 };
438 write!(f, "{} ({})", self.ty, mutability)
439 }
440}
441
442#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
445#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
446#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
447#[rkyv(derive(Debug), compare(PartialEq))]
448pub struct InitExpr {
449 pub ops: Box<[InitExprOp]>,
451}
452
453impl InitExpr {
454 pub fn new<Ops>(ops: Ops) -> Self
456 where
457 Ops: Into<Box<[InitExprOp]>>,
458 {
459 Self { ops: ops.into() }
460 }
461
462 pub fn ops(&self) -> &[InitExprOp] {
464 &self.ops
465 }
466}
467
468#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
470#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
471#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
472#[rkyv(derive(Debug), compare(PartialEq))]
473#[repr(u8)]
474pub enum InitExprOp {
475 GlobalGetI32(GlobalIndex),
477 GlobalGetI64(GlobalIndex),
479 I32Const(i32),
481 I32Add,
483 I32Sub,
485 I32Mul,
487 I64Const(i64),
489 I64Add,
491 I64Sub,
493 I64Mul,
495}
496
497impl InitExprOp {
498 pub fn is_32bit_expression(&self) -> bool {
500 match self {
501 Self::GlobalGetI32(..)
502 | Self::I32Const(_)
503 | Self::I32Add
504 | Self::I32Sub
505 | Self::I32Mul => true,
506 Self::GlobalGetI64(_)
507 | Self::I64Const(_)
508 | Self::I64Add
509 | Self::I64Sub
510 | Self::I64Mul => false,
511 }
512 }
513}
514
515#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
518#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
519#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
520#[rkyv(derive(Debug), compare(PartialEq))]
521#[repr(u8)]
522pub enum GlobalInit {
523 I32Const(i32),
525 I64Const(i64),
527 F32Const(f32),
529 F64Const(f64),
531 V128Const(V128),
533 GetGlobal(GlobalIndex),
535 RefNullConst,
540 RefFunc(FunctionIndex),
542 Expr(InitExpr),
544}
545
546#[derive(Debug, Clone, PartialEq, Eq, Hash)]
552#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
553#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
554#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
555#[rkyv(derive(Debug))]
556pub enum TagKind {
557 Exception,
559}
560
561#[derive(Debug, Clone, PartialEq, Eq, Hash)]
564#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
565#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
566#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
567#[rkyv(derive(Debug))]
568pub struct TagType {
569 pub kind: TagKind,
571 pub params: Box<[Type]>,
573}
574
575impl TagType {
576 pub fn new<Params>(kind: TagKind, params: Params) -> Self
578 where
579 Params: Into<Box<[Type]>>,
580 {
581 Self {
582 kind,
583 params: params.into(),
584 }
585 }
586
587 pub fn params(&self) -> &[Type] {
589 &self.params
590 }
591
592 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
594 Self {
595 kind,
596 params: ty.params().into(),
597 }
598 }
599}
600
601impl fmt::Display for TagType {
602 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
603 write!(f, "({:?}) {:?}", self.kind, self.params(),)
604 }
605}
606
607#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
615#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
616#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
617#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
618#[rkyv(derive(Debug))]
619pub struct TableType {
620 pub ty: Type,
622 pub minimum: u32,
624 pub maximum: Option<u32>,
626 pub readonly: bool,
628}
629
630impl TableType {
631 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
634 Self {
635 ty,
636 minimum,
637 maximum,
638 readonly: false,
639 }
640 }
641
642 pub fn is_fixed_funcref_table(&self) -> bool {
644 matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
645 }
646}
647
648impl fmt::Display for TableType {
649 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
650 if let Some(maximum) = self.maximum {
651 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
652 } else {
653 write!(f, "{} ({}..)", self.ty, self.minimum)
654 }
655 }
656}
657
658#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
665#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
666#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
667#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
668#[rkyv(derive(Debug))]
669pub struct MemoryType {
670 pub minimum: Pages,
672 pub maximum: Option<Pages>,
674 pub shared: bool,
676}
677
678impl MemoryType {
679 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
682 where
683 IntoPages: Into<Pages>,
684 {
685 Self {
686 minimum: minimum.into(),
687 maximum: maximum.map(Into::into),
688 shared,
689 }
690 }
691}
692
693impl fmt::Display for MemoryType {
694 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695 let shared = if self.shared { "shared" } else { "not shared" };
696 if let Some(maximum) = self.maximum {
697 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
698 } else {
699 write!(f, "{} ({:?}..)", shared, self.minimum)
700 }
701 }
702}
703
704#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
713#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
714pub struct ImportType<T = ExternType> {
715 module: String,
716 name: String,
717 ty: T,
718}
719
720impl<T> ImportType<T> {
721 pub fn new(module: &str, name: &str, ty: T) -> Self {
724 Self {
725 module: module.to_owned(),
726 name: name.to_owned(),
727 ty,
728 }
729 }
730
731 pub fn module(&self) -> &str {
733 &self.module
734 }
735
736 pub fn name(&self) -> &str {
739 &self.name
740 }
741
742 pub fn ty(&self) -> &T {
744 &self.ty
745 }
746}
747
748#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
760#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
761pub struct ExportType<T = ExternType> {
762 name: String,
763 ty: T,
764}
765
766impl<T> ExportType<T> {
767 pub fn new(name: &str, ty: T) -> Self {
770 Self {
771 name: name.to_string(),
772 ty,
773 }
774 }
775
776 pub fn name(&self) -> &str {
778 &self.name
779 }
780
781 pub fn ty(&self) -> &T {
783 &self.ty
784 }
785}
786
787#[cfg(test)]
788mod tests {
789 use super::*;
790
791 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
792 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
793 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
794 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
795
796 #[test]
797 fn convert_tuple_to_functiontype() {
798 let ty: FunctionType = VOID_TO_VOID.into();
799 assert_eq!(ty.params().len(), 0);
800 assert_eq!(ty.results().len(), 0);
801
802 let ty: FunctionType = I32_I32_TO_VOID.into();
803 assert_eq!(ty.params().len(), 2);
804 assert_eq!(ty.params()[0], Type::I32);
805 assert_eq!(ty.params()[1], Type::I32);
806 assert_eq!(ty.results().len(), 0);
807
808 let ty: FunctionType = V128_I64_TO_I32.into();
809 assert_eq!(ty.params().len(), 2);
810 assert_eq!(ty.params()[0], Type::V128);
811 assert_eq!(ty.params()[1], Type::I64);
812 assert_eq!(ty.results().len(), 1);
813 assert_eq!(ty.results()[0], Type::I32);
814
815 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
816 assert_eq!(ty.params().len(), 9);
817 assert_eq!(ty.results().len(), 9);
818 }
819
820 #[test]
821 fn signature_hash_is_stable() {
822 let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
823 assert_eq!(ty.signature_hash(), ty.signature_hash());
824 }
825
826 #[test]
827 fn signature_hash_distinguishes() {
828 let left: FunctionType = ([Type::I32], [Type::I64]).into();
829 let right: FunctionType = ([Type::I64], [Type::I32]).into();
830 assert_ne!(left.signature_hash(), right.signature_hash());
831
832 let left: FunctionType = ([], [Type::I32, Type::I64]).into();
833 let right: FunctionType = ([Type::I32], [Type::I64]).into();
834 assert_ne!(left.signature_hash(), right.signature_hash());
835 }
836}