1use crate::indexes::{FunctionIndex, GlobalIndex};
2use crate::lib::std::borrow::ToOwned;
3use crate::lib::std::boxed::Box;
4use crate::lib::std::fmt;
5use crate::lib::std::format;
6use crate::lib::std::string::{String, ToString};
7use crate::lib::std::vec::Vec;
8use crate::units::Pages;
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
60impl fmt::Display for Type {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "{self:?}")
63 }
64}
65
66#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
68#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
69#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
70#[rkyv(derive(Debug), compare(PartialEq))]
71pub struct V128(pub(crate) [u8; 16]);
72
73#[cfg(feature = "artifact-size")]
74impl loupe::MemoryUsage for V128 {
75 fn size_of_val(&self, _tracker: &mut dyn loupe::MemoryUsageTracker) -> usize {
76 16 * 8
77 }
78}
79
80impl V128 {
81 pub fn bytes(&self) -> &[u8; 16] {
83 &self.0
84 }
85 pub fn iter(&self) -> impl Iterator<Item = &u8> {
87 self.0.iter()
88 }
89
90 pub fn to_vec(self) -> Vec<u8> {
92 self.0.to_vec()
93 }
94
95 pub fn as_slice(&self) -> &[u8] {
97 &self.0[..]
98 }
99}
100
101impl From<[u8; 16]> for V128 {
102 fn from(array: [u8; 16]) -> Self {
103 Self(array)
104 }
105}
106
107impl From<&[u8]> for V128 {
108 fn from(slice: &[u8]) -> Self {
109 assert_eq!(slice.len(), 16);
110 let mut buffer = [0; 16];
111 buffer.copy_from_slice(slice);
112 Self(buffer)
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
124#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
125pub enum ExternType {
126 Function(FunctionType),
128 Global(GlobalType),
130 Table(TableType),
132 Memory(MemoryType),
134 Tag(TagType),
136}
137
138fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
139 let GlobalType {
140 ty: exported_ty,
141 mutability: exported_mutability,
142 } = exported;
143 let GlobalType {
144 ty: imported_ty,
145 mutability: imported_mutability,
146 } = imported;
147
148 exported_ty == imported_ty && imported_mutability == exported_mutability
149}
150
151fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool {
152 match exported_type {
153 Type::FuncRef => true,
154 _ => imported_type == exported_type,
155 }
156}
157
158fn is_table_compatible(
159 exported: &TableType,
160 imported: &TableType,
161 imported_runtime_size: Option<u32>,
162) -> bool {
163 let TableType {
164 ty: exported_ty,
165 minimum: exported_minimum,
166 maximum: exported_maximum,
167 } = exported;
168 let TableType {
169 ty: imported_ty,
170 minimum: imported_minimum,
171 maximum: imported_maximum,
172 } = imported;
173
174 is_table_element_type_compatible(*exported_ty, *imported_ty)
175 && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
176 && (imported_maximum.is_none()
177 || (!exported_maximum.is_none()
178 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
179}
180
181fn is_memory_compatible(
182 exported: &MemoryType,
183 imported: &MemoryType,
184 imported_runtime_size: Option<u32>,
185) -> bool {
186 let MemoryType {
187 minimum: exported_minimum,
188 maximum: exported_maximum,
189 shared: exported_shared,
190 } = exported;
191 let MemoryType {
192 minimum: imported_minimum,
193 maximum: imported_maximum,
194 shared: imported_shared,
195 } = imported;
196
197 imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
198 && (imported_maximum.is_none()
199 || (!exported_maximum.is_none()
200 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
201 && exported_shared == imported_shared
202}
203
204macro_rules! accessors {
205 ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
206 pub fn $get(&self) -> Option<&$ty> {
209 if let Self::$variant(e) = self {
210 Some(e)
211 } else {
212 None
213 }
214 }
215
216 pub fn $unwrap(&self) -> &$ty {
223 self.$get().expect(concat!("expected ", stringify!($ty)))
224 }
225 )*)
226}
227
228impl ExternType {
229 accessors! {
230 (Function(FunctionType) func unwrap_func)
231 (Global(GlobalType) global unwrap_global)
232 (Table(TableType) table unwrap_table)
233 (Memory(MemoryType) memory unwrap_memory)
234 }
235 pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
237 match (self, other) {
238 (Self::Function(a), Self::Function(b)) => a == b,
239 (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
240 (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
241 (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
242 (Self::Tag(a), Self::Tag(b)) => a == b,
243 _ => false,
245 }
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq, Hash)]
256#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
257#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
258#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
259#[rkyv(derive(Debug))]
260pub struct FunctionType {
261 params: Box<[Type]>,
263 results: Box<[Type]>,
265}
266
267impl FunctionType {
268 pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
270 where
271 Params: Into<Box<[Type]>>,
272 Returns: Into<Box<[Type]>>,
273 {
274 Self {
275 params: params.into(),
276 results: returns.into(),
277 }
278 }
279
280 pub fn params(&self) -> &[Type] {
282 &self.params
283 }
284
285 pub fn results(&self) -> &[Type] {
287 &self.results
288 }
289
290 pub fn signature_hash(&self) -> u32 {
292 let mut hasher = crc32fast::Hasher::new();
293 hasher.update(&self.results.len().to_le_bytes());
294 hasher.update(&self.params.len().to_le_bytes());
295 for ty in self.results.iter().chain(self.params.iter()) {
296 hasher.update(&[*ty as u8]);
297 }
298 hasher.finalize()
299 }
300}
301
302impl fmt::Display for FunctionType {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 let params = self
305 .params
306 .iter()
307 .map(|p| format!("{p:?}"))
308 .collect::<Vec<_>>()
309 .join(", ");
310 let results = self
311 .results
312 .iter()
313 .map(|p| format!("{p:?}"))
314 .collect::<Vec<_>>()
315 .join(", ");
316 write!(f, "[{params}] -> [{results}]")
317 }
318}
319
320macro_rules! implement_from_pair_to_functiontype {
323 ($($N:literal,$M:literal)+) => {
324 $(
325 impl From<([Type; $N], [Type; $M])> for FunctionType {
326 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
327 Self::new(pair.0, pair.1)
328 }
329 }
330 )+
331 }
332}
333
334implement_from_pair_to_functiontype! {
335 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
336 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
337 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
338 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
339 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
340 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
341 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
342 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
343 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
344 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
345}
346
347impl From<&Self> for FunctionType {
348 fn from(as_ref: &Self) -> Self {
349 as_ref.clone()
350 }
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
355#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
356#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
357#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
358#[repr(u8)]
359pub enum Mutability {
360 Const,
362 Var,
364}
365
366impl Mutability {
367 pub fn is_mutable(self) -> bool {
369 self.into()
370 }
371}
372
373impl From<bool> for Mutability {
374 fn from(value: bool) -> Self {
375 if value { Self::Var } else { Self::Const }
376 }
377}
378
379impl From<Mutability> for bool {
380 fn from(value: Mutability) -> Self {
381 match value {
382 Mutability::Var => true,
383 Mutability::Const => false,
384 }
385 }
386}
387
388#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
390#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
391#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
392#[rkyv(derive(Debug), compare(PartialEq))]
393pub struct GlobalType {
394 pub ty: Type,
396 pub mutability: Mutability,
398}
399
400impl GlobalType {
408 pub fn new(ty: Type, mutability: Mutability) -> Self {
419 Self { ty, mutability }
420 }
421}
422
423impl fmt::Display for GlobalType {
424 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425 let mutability = match self.mutability {
426 Mutability::Const => "constant",
427 Mutability::Var => "mutable",
428 };
429 write!(f, "{} ({})", self.ty, mutability)
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
436#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
437#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
438#[rkyv(derive(Debug), compare(PartialEq))]
439pub struct InitExpr {
440 pub ops: Box<[InitExprOp]>,
442}
443
444impl InitExpr {
445 pub fn new<Ops>(ops: Ops) -> Self
447 where
448 Ops: Into<Box<[InitExprOp]>>,
449 {
450 Self { ops: ops.into() }
451 }
452
453 pub fn ops(&self) -> &[InitExprOp] {
455 &self.ops
456 }
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
461#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
462#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
463#[rkyv(derive(Debug), compare(PartialEq))]
464#[repr(u8)]
465pub enum InitExprOp {
466 GlobalGetI32(GlobalIndex),
468 GlobalGetI64(GlobalIndex),
470 I32Const(i32),
472 I32Add,
474 I32Sub,
476 I32Mul,
478 I64Const(i64),
480 I64Add,
482 I64Sub,
484 I64Mul,
486}
487
488impl InitExprOp {
489 pub fn is_32bit_expression(&self) -> bool {
491 match self {
492 Self::GlobalGetI32(..)
493 | Self::I32Const(_)
494 | Self::I32Add
495 | Self::I32Sub
496 | Self::I32Mul => true,
497 Self::GlobalGetI64(_)
498 | Self::I64Const(_)
499 | Self::I64Add
500 | Self::I64Sub
501 | Self::I64Mul => false,
502 }
503 }
504}
505
506#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
509#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
510#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
511#[rkyv(derive(Debug), compare(PartialEq))]
512#[repr(u8)]
513pub enum GlobalInit {
514 I32Const(i32),
516 I64Const(i64),
518 F32Const(f32),
520 F64Const(f64),
522 V128Const(V128),
524 GetGlobal(GlobalIndex),
526 RefNullConst,
531 RefFunc(FunctionIndex),
533 Expr(InitExpr),
535}
536
537#[derive(Debug, Clone, PartialEq, Eq, Hash)]
543#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
544#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
545#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
546#[rkyv(derive(Debug))]
547pub enum TagKind {
548 Exception,
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Hash)]
555#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
556#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
557#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
558#[rkyv(derive(Debug))]
559pub struct TagType {
560 pub kind: TagKind,
562 pub params: Box<[Type]>,
564}
565
566impl TagType {
567 pub fn new<Params>(kind: TagKind, params: Params) -> Self
569 where
570 Params: Into<Box<[Type]>>,
571 {
572 Self {
573 kind,
574 params: params.into(),
575 }
576 }
577
578 pub fn params(&self) -> &[Type] {
580 &self.params
581 }
582
583 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
585 Self {
586 kind,
587 params: ty.params().into(),
588 }
589 }
590}
591
592impl fmt::Display for TagType {
593 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
594 write!(f, "({:?}) {:?}", self.kind, self.params(),)
595 }
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
606#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
607#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
608#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
609#[rkyv(derive(Debug))]
610pub struct TableType {
611 pub ty: Type,
613 pub minimum: u32,
615 pub maximum: Option<u32>,
617}
618
619impl TableType {
620 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
623 Self {
624 ty,
625 minimum,
626 maximum,
627 }
628 }
629
630 pub fn is_fixed_funcref_table(&self) -> bool {
632 matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
633 }
634}
635
636impl fmt::Display for TableType {
637 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638 if let Some(maximum) = self.maximum {
639 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
640 } else {
641 write!(f, "{} ({}..)", self.ty, self.minimum)
642 }
643 }
644}
645
646#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
653#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
654#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
655#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
656#[rkyv(derive(Debug))]
657pub struct MemoryType {
658 pub minimum: Pages,
660 pub maximum: Option<Pages>,
662 pub shared: bool,
664}
665
666impl MemoryType {
667 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
670 where
671 IntoPages: Into<Pages>,
672 {
673 Self {
674 minimum: minimum.into(),
675 maximum: maximum.map(Into::into),
676 shared,
677 }
678 }
679}
680
681impl fmt::Display for MemoryType {
682 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
683 let shared = if self.shared { "shared" } else { "not shared" };
684 if let Some(maximum) = self.maximum {
685 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
686 } else {
687 write!(f, "{} ({:?}..)", shared, self.minimum)
688 }
689 }
690}
691
692#[derive(Debug, Clone, PartialEq, Eq, Hash)]
701#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
702pub struct ImportType<T = ExternType> {
703 module: String,
704 name: String,
705 ty: T,
706}
707
708impl<T> ImportType<T> {
709 pub fn new(module: &str, name: &str, ty: T) -> Self {
712 Self {
713 module: module.to_owned(),
714 name: name.to_owned(),
715 ty,
716 }
717 }
718
719 pub fn module(&self) -> &str {
721 &self.module
722 }
723
724 pub fn name(&self) -> &str {
727 &self.name
728 }
729
730 pub fn ty(&self) -> &T {
732 &self.ty
733 }
734}
735
736#[derive(Debug, Clone, PartialEq, Eq, Hash)]
748#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
749pub struct ExportType<T = ExternType> {
750 name: String,
751 ty: T,
752}
753
754impl<T> ExportType<T> {
755 pub fn new(name: &str, ty: T) -> Self {
758 Self {
759 name: name.to_string(),
760 ty,
761 }
762 }
763
764 pub fn name(&self) -> &str {
766 &self.name
767 }
768
769 pub fn ty(&self) -> &T {
771 &self.ty
772 }
773}
774
775#[cfg(test)]
776mod tests {
777 use super::*;
778
779 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
780 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
781 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
782 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
783
784 #[test]
785 fn convert_tuple_to_functiontype() {
786 let ty: FunctionType = VOID_TO_VOID.into();
787 assert_eq!(ty.params().len(), 0);
788 assert_eq!(ty.results().len(), 0);
789
790 let ty: FunctionType = I32_I32_TO_VOID.into();
791 assert_eq!(ty.params().len(), 2);
792 assert_eq!(ty.params()[0], Type::I32);
793 assert_eq!(ty.params()[1], Type::I32);
794 assert_eq!(ty.results().len(), 0);
795
796 let ty: FunctionType = V128_I64_TO_I32.into();
797 assert_eq!(ty.params().len(), 2);
798 assert_eq!(ty.params()[0], Type::V128);
799 assert_eq!(ty.params()[1], Type::I64);
800 assert_eq!(ty.results().len(), 1);
801 assert_eq!(ty.results()[0], Type::I32);
802
803 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
804 assert_eq!(ty.params().len(), 9);
805 assert_eq!(ty.results().len(), 9);
806 }
807
808 #[test]
809 fn signature_hash_is_stable() {
810 let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
811 assert_eq!(ty.signature_hash(), ty.signature_hash());
812 }
813
814 #[test]
815 fn signature_hash_distinguishes() {
816 let left: FunctionType = ([Type::I32], [Type::I64]).into();
817 let right: FunctionType = ([Type::I64], [Type::I32]).into();
818 assert_ne!(left.signature_hash(), right.signature_hash());
819
820 let left: FunctionType = ([], [Type::I32, Type::I64]).into();
821 let right: FunctionType = ([Type::I32], [Type::I64]).into();
822 assert_ne!(left.signature_hash(), right.signature_hash());
823 }
824}