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, RkyvSerialize, RkyvDeserialize, Archive)]
124#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
125#[rkyv(derive(Debug))]
126pub enum ExternType {
127 Function(FunctionType),
129 Global(GlobalType),
131 Table(TableType),
133 Memory(MemoryType),
135 Tag(TagType),
137}
138
139fn is_global_compatible(exported: GlobalType, imported: GlobalType) -> bool {
140 let GlobalType {
141 ty: exported_ty,
142 mutability: exported_mutability,
143 } = exported;
144 let GlobalType {
145 ty: imported_ty,
146 mutability: imported_mutability,
147 } = imported;
148
149 exported_ty == imported_ty && imported_mutability == exported_mutability
150}
151
152fn is_table_element_type_compatible(exported_type: Type, imported_type: Type) -> bool {
153 match exported_type {
154 Type::FuncRef => true,
155 _ => imported_type == exported_type,
156 }
157}
158
159fn is_table_compatible(
160 exported: &TableType,
161 imported: &TableType,
162 imported_runtime_size: Option<u32>,
163) -> bool {
164 let TableType {
165 ty: exported_ty,
166 minimum: exported_minimum,
167 maximum: exported_maximum,
168 ..
169 } = exported;
170 let TableType {
171 ty: imported_ty,
172 minimum: imported_minimum,
173 maximum: imported_maximum,
174 ..
175 } = imported;
176
177 is_table_element_type_compatible(*exported_ty, *imported_ty)
178 && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
179 && (imported_maximum.is_none()
180 || (!exported_maximum.is_none()
181 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
182}
183
184fn is_memory_compatible(
185 exported: &MemoryType,
186 imported: &MemoryType,
187 imported_runtime_size: Option<u32>,
188) -> bool {
189 let MemoryType {
190 minimum: exported_minimum,
191 maximum: exported_maximum,
192 shared: exported_shared,
193 } = exported;
194 let MemoryType {
195 minimum: imported_minimum,
196 maximum: imported_maximum,
197 shared: imported_shared,
198 } = imported;
199
200 imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
201 && (imported_maximum.is_none()
202 || (!exported_maximum.is_none()
203 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
204 && exported_shared == imported_shared
205}
206
207macro_rules! accessors {
208 ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
209 pub fn $get(&self) -> Option<&$ty> {
212 if let Self::$variant(e) = self {
213 Some(e)
214 } else {
215 None
216 }
217 }
218
219 pub fn $unwrap(&self) -> &$ty {
226 self.$get().expect(concat!("expected ", stringify!($ty)))
227 }
228 )*)
229}
230
231impl ExternType {
232 accessors! {
233 (Function(FunctionType) func unwrap_func)
234 (Global(GlobalType) global unwrap_global)
235 (Table(TableType) table unwrap_table)
236 (Memory(MemoryType) memory unwrap_memory)
237 }
238 pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
240 match (self, other) {
241 (Self::Function(a), Self::Function(b)) => a == b,
242 (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
243 (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
244 (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
245 (Self::Tag(a), Self::Tag(b)) => a == b,
246 _ => false,
248 }
249 }
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Hash)]
259#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
260#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
261#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
262#[rkyv(derive(Debug))]
263pub struct FunctionType {
264 params: Box<[Type]>,
266 results: Box<[Type]>,
268}
269
270impl FunctionType {
271 pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
273 where
274 Params: Into<Box<[Type]>>,
275 Returns: Into<Box<[Type]>>,
276 {
277 Self {
278 params: params.into(),
279 results: returns.into(),
280 }
281 }
282
283 pub fn params(&self) -> &[Type] {
285 &self.params
286 }
287
288 pub fn results(&self) -> &[Type] {
290 &self.results
291 }
292
293 pub fn signature_hash(&self) -> u32 {
295 let mut hasher = crc32fast::Hasher::new();
296 hasher.update(&self.results.len().to_le_bytes());
297 hasher.update(&self.params.len().to_le_bytes());
298 for ty in self.results.iter().chain(self.params.iter()) {
299 hasher.update(&[*ty as u8]);
300 }
301 hasher.finalize()
302 }
303}
304
305impl fmt::Display for FunctionType {
306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307 let params = self
308 .params
309 .iter()
310 .map(|p| format!("{p:?}"))
311 .collect::<Vec<_>>()
312 .join(", ");
313 let results = self
314 .results
315 .iter()
316 .map(|p| format!("{p:?}"))
317 .collect::<Vec<_>>()
318 .join(", ");
319 write!(f, "[{params}] -> [{results}]")
320 }
321}
322
323macro_rules! implement_from_pair_to_functiontype {
326 ($($N:literal,$M:literal)+) => {
327 $(
328 impl From<([Type; $N], [Type; $M])> for FunctionType {
329 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
330 Self::new(pair.0, pair.1)
331 }
332 }
333 )+
334 }
335}
336
337implement_from_pair_to_functiontype! {
338 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
339 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
340 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
341 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
342 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
343 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
344 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
345 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
346 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
347 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
348}
349
350impl From<&Self> for FunctionType {
351 fn from(as_ref: &Self) -> Self {
352 as_ref.clone()
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
358#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
359#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
360#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
361#[repr(u8)]
362pub enum Mutability {
363 Const,
365 Var,
367}
368
369impl Mutability {
370 pub fn is_mutable(self) -> bool {
372 self.into()
373 }
374}
375
376impl From<bool> for Mutability {
377 fn from(value: bool) -> Self {
378 if value { Self::Var } else { Self::Const }
379 }
380}
381
382impl From<Mutability> for bool {
383 fn from(value: Mutability) -> Self {
384 match value {
385 Mutability::Var => true,
386 Mutability::Const => false,
387 }
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
393#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
394#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
395#[rkyv(derive(Debug), compare(PartialEq))]
396pub struct GlobalType {
397 pub ty: Type,
399 pub mutability: Mutability,
401}
402
403impl GlobalType {
411 pub fn new(ty: Type, mutability: Mutability) -> Self {
422 Self { ty, mutability }
423 }
424}
425
426impl fmt::Display for GlobalType {
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 let mutability = match self.mutability {
429 Mutability::Const => "constant",
430 Mutability::Var => "mutable",
431 };
432 write!(f, "{} ({})", self.ty, mutability)
433 }
434}
435
436#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
439#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
440#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
441#[rkyv(derive(Debug), compare(PartialEq))]
442pub struct InitExpr {
443 pub ops: Box<[InitExprOp]>,
445}
446
447impl InitExpr {
448 pub fn new<Ops>(ops: Ops) -> Self
450 where
451 Ops: Into<Box<[InitExprOp]>>,
452 {
453 Self { ops: ops.into() }
454 }
455
456 pub fn ops(&self) -> &[InitExprOp] {
458 &self.ops
459 }
460}
461
462#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
464#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
465#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
466#[rkyv(derive(Debug), compare(PartialEq))]
467#[repr(u8)]
468pub enum InitExprOp {
469 GlobalGetI32(GlobalIndex),
471 GlobalGetI64(GlobalIndex),
473 I32Const(i32),
475 I32Add,
477 I32Sub,
479 I32Mul,
481 I64Const(i64),
483 I64Add,
485 I64Sub,
487 I64Mul,
489}
490
491impl InitExprOp {
492 pub fn is_32bit_expression(&self) -> bool {
494 match self {
495 Self::GlobalGetI32(..)
496 | Self::I32Const(_)
497 | Self::I32Add
498 | Self::I32Sub
499 | Self::I32Mul => true,
500 Self::GlobalGetI64(_)
501 | Self::I64Const(_)
502 | Self::I64Add
503 | Self::I64Sub
504 | Self::I64Mul => false,
505 }
506 }
507}
508
509#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
512#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
513#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
514#[rkyv(derive(Debug), compare(PartialEq))]
515#[repr(u8)]
516pub enum GlobalInit {
517 I32Const(i32),
519 I64Const(i64),
521 F32Const(f32),
523 F64Const(f64),
525 V128Const(V128),
527 GetGlobal(GlobalIndex),
529 RefNullConst,
534 RefFunc(FunctionIndex),
536 Expr(InitExpr),
538}
539
540#[derive(Debug, Clone, PartialEq, Eq, Hash)]
546#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
547#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
548#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
549#[rkyv(derive(Debug))]
550pub enum TagKind {
551 Exception,
553}
554
555#[derive(Debug, Clone, PartialEq, Eq, Hash)]
558#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
559#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
560#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
561#[rkyv(derive(Debug))]
562pub struct TagType {
563 pub kind: TagKind,
565 pub params: Box<[Type]>,
567}
568
569impl TagType {
570 pub fn new<Params>(kind: TagKind, params: Params) -> Self
572 where
573 Params: Into<Box<[Type]>>,
574 {
575 Self {
576 kind,
577 params: params.into(),
578 }
579 }
580
581 pub fn params(&self) -> &[Type] {
583 &self.params
584 }
585
586 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
588 Self {
589 kind,
590 params: ty.params().into(),
591 }
592 }
593}
594
595impl fmt::Display for TagType {
596 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597 write!(f, "({:?}) {:?}", self.kind, self.params(),)
598 }
599}
600
601#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
609#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
610#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
611#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
612#[rkyv(derive(Debug))]
613pub struct TableType {
614 pub ty: Type,
616 pub minimum: u32,
618 pub maximum: Option<u32>,
620 pub readonly: bool,
622}
623
624impl TableType {
625 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
628 Self {
629 ty,
630 minimum,
631 maximum,
632 readonly: false,
633 }
634 }
635
636 pub fn is_fixed_funcref_table(&self) -> bool {
638 matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
639 }
640}
641
642impl fmt::Display for TableType {
643 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
644 if let Some(maximum) = self.maximum {
645 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
646 } else {
647 write!(f, "{} ({}..)", self.ty, self.minimum)
648 }
649 }
650}
651
652#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
659#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
660#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
661#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
662#[rkyv(derive(Debug))]
663pub struct MemoryType {
664 pub minimum: Pages,
666 pub maximum: Option<Pages>,
668 pub shared: bool,
670}
671
672impl MemoryType {
673 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
676 where
677 IntoPages: Into<Pages>,
678 {
679 Self {
680 minimum: minimum.into(),
681 maximum: maximum.map(Into::into),
682 shared,
683 }
684 }
685}
686
687impl fmt::Display for MemoryType {
688 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
689 let shared = if self.shared { "shared" } else { "not shared" };
690 if let Some(maximum) = self.maximum {
691 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
692 } else {
693 write!(f, "{} ({:?}..)", shared, self.minimum)
694 }
695 }
696}
697
698#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
707#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
708pub struct ImportType<T = ExternType> {
709 module: String,
710 name: String,
711 ty: T,
712}
713
714impl<T> ImportType<T> {
715 pub fn new(module: &str, name: &str, ty: T) -> Self {
718 Self {
719 module: module.to_owned(),
720 name: name.to_owned(),
721 ty,
722 }
723 }
724
725 pub fn module(&self) -> &str {
727 &self.module
728 }
729
730 pub fn name(&self) -> &str {
733 &self.name
734 }
735
736 pub fn ty(&self) -> &T {
738 &self.ty
739 }
740}
741
742#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
754#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
755pub struct ExportType<T = ExternType> {
756 name: String,
757 ty: T,
758}
759
760impl<T> ExportType<T> {
761 pub fn new(name: &str, ty: T) -> Self {
764 Self {
765 name: name.to_string(),
766 ty,
767 }
768 }
769
770 pub fn name(&self) -> &str {
772 &self.name
773 }
774
775 pub fn ty(&self) -> &T {
777 &self.ty
778 }
779}
780
781#[cfg(test)]
782mod tests {
783 use super::*;
784
785 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
786 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
787 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
788 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
789
790 #[test]
791 fn convert_tuple_to_functiontype() {
792 let ty: FunctionType = VOID_TO_VOID.into();
793 assert_eq!(ty.params().len(), 0);
794 assert_eq!(ty.results().len(), 0);
795
796 let ty: FunctionType = I32_I32_TO_VOID.into();
797 assert_eq!(ty.params().len(), 2);
798 assert_eq!(ty.params()[0], Type::I32);
799 assert_eq!(ty.params()[1], Type::I32);
800 assert_eq!(ty.results().len(), 0);
801
802 let ty: FunctionType = V128_I64_TO_I32.into();
803 assert_eq!(ty.params().len(), 2);
804 assert_eq!(ty.params()[0], Type::V128);
805 assert_eq!(ty.params()[1], Type::I64);
806 assert_eq!(ty.results().len(), 1);
807 assert_eq!(ty.results()[0], Type::I32);
808
809 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
810 assert_eq!(ty.params().len(), 9);
811 assert_eq!(ty.results().len(), 9);
812 }
813
814 #[test]
815 fn signature_hash_is_stable() {
816 let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
817 assert_eq!(ty.signature_hash(), ty.signature_hash());
818 }
819
820 #[test]
821 fn signature_hash_distinguishes() {
822 let left: FunctionType = ([Type::I32], [Type::I64]).into();
823 let right: FunctionType = ([Type::I64], [Type::I32]).into();
824 assert_ne!(left.signature_hash(), right.signature_hash());
825
826 let left: FunctionType = ([], [Type::I32, Type::I64]).into();
827 let right: FunctionType = ([Type::I32], [Type::I64]).into();
828 assert_ne!(left.signature_hash(), right.signature_hash());
829 }
830}