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 ..
168 } = exported;
169 let TableType {
170 ty: imported_ty,
171 minimum: imported_minimum,
172 maximum: imported_maximum,
173 ..
174 } = imported;
175
176 is_table_element_type_compatible(*exported_ty, *imported_ty)
177 && *imported_minimum <= imported_runtime_size.unwrap_or(*exported_minimum)
178 && (imported_maximum.is_none()
179 || (!exported_maximum.is_none()
180 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
181}
182
183fn is_memory_compatible(
184 exported: &MemoryType,
185 imported: &MemoryType,
186 imported_runtime_size: Option<u32>,
187) -> bool {
188 let MemoryType {
189 minimum: exported_minimum,
190 maximum: exported_maximum,
191 shared: exported_shared,
192 } = exported;
193 let MemoryType {
194 minimum: imported_minimum,
195 maximum: imported_maximum,
196 shared: imported_shared,
197 } = imported;
198
199 imported_minimum.0 <= imported_runtime_size.unwrap_or(exported_minimum.0)
200 && (imported_maximum.is_none()
201 || (!exported_maximum.is_none()
202 && imported_maximum.unwrap() >= exported_maximum.unwrap()))
203 && exported_shared == imported_shared
204}
205
206macro_rules! accessors {
207 ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($(
208 pub fn $get(&self) -> Option<&$ty> {
211 if let Self::$variant(e) = self {
212 Some(e)
213 } else {
214 None
215 }
216 }
217
218 pub fn $unwrap(&self) -> &$ty {
225 self.$get().expect(concat!("expected ", stringify!($ty)))
226 }
227 )*)
228}
229
230impl ExternType {
231 accessors! {
232 (Function(FunctionType) func unwrap_func)
233 (Global(GlobalType) global unwrap_global)
234 (Table(TableType) table unwrap_table)
235 (Memory(MemoryType) memory unwrap_memory)
236 }
237 pub fn is_compatible_with(&self, other: &Self, runtime_size: Option<u32>) -> bool {
239 match (self, other) {
240 (Self::Function(a), Self::Function(b)) => a == b,
241 (Self::Global(a), Self::Global(b)) => is_global_compatible(*a, *b),
242 (Self::Table(a), Self::Table(b)) => is_table_compatible(a, b, runtime_size),
243 (Self::Memory(a), Self::Memory(b)) => is_memory_compatible(a, b, runtime_size),
244 (Self::Tag(a), Self::Tag(b)) => a == b,
245 _ => false,
247 }
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Hash)]
258#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
259#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
260#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
261#[rkyv(derive(Debug))]
262pub struct FunctionType {
263 params: Box<[Type]>,
265 results: Box<[Type]>,
267}
268
269impl FunctionType {
270 pub fn new<Params, Returns>(params: Params, returns: Returns) -> Self
272 where
273 Params: Into<Box<[Type]>>,
274 Returns: Into<Box<[Type]>>,
275 {
276 Self {
277 params: params.into(),
278 results: returns.into(),
279 }
280 }
281
282 pub fn params(&self) -> &[Type] {
284 &self.params
285 }
286
287 pub fn results(&self) -> &[Type] {
289 &self.results
290 }
291
292 pub fn signature_hash(&self) -> u32 {
294 let mut hasher = crc32fast::Hasher::new();
295 hasher.update(&self.results.len().to_le_bytes());
296 hasher.update(&self.params.len().to_le_bytes());
297 for ty in self.results.iter().chain(self.params.iter()) {
298 hasher.update(&[*ty as u8]);
299 }
300 hasher.finalize()
301 }
302}
303
304impl fmt::Display for FunctionType {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 let params = self
307 .params
308 .iter()
309 .map(|p| format!("{p:?}"))
310 .collect::<Vec<_>>()
311 .join(", ");
312 let results = self
313 .results
314 .iter()
315 .map(|p| format!("{p:?}"))
316 .collect::<Vec<_>>()
317 .join(", ");
318 write!(f, "[{params}] -> [{results}]")
319 }
320}
321
322macro_rules! implement_from_pair_to_functiontype {
325 ($($N:literal,$M:literal)+) => {
326 $(
327 impl From<([Type; $N], [Type; $M])> for FunctionType {
328 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
329 Self::new(pair.0, pair.1)
330 }
331 }
332 )+
333 }
334}
335
336implement_from_pair_to_functiontype! {
337 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
338 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
339 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
340 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
341 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
342 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
343 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
344 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
345 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
346 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
347}
348
349impl From<&Self> for FunctionType {
350 fn from(as_ref: &Self) -> Self {
351 as_ref.clone()
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
357#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
358#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
359#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
360#[repr(u8)]
361pub enum Mutability {
362 Const,
364 Var,
366}
367
368impl Mutability {
369 pub fn is_mutable(self) -> bool {
371 self.into()
372 }
373}
374
375impl From<bool> for Mutability {
376 fn from(value: bool) -> Self {
377 if value { Self::Var } else { Self::Const }
378 }
379}
380
381impl From<Mutability> for bool {
382 fn from(value: Mutability) -> Self {
383 match value {
384 Mutability::Var => true,
385 Mutability::Const => false,
386 }
387 }
388}
389
390#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
392#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
393#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
394#[rkyv(derive(Debug), compare(PartialEq))]
395pub struct GlobalType {
396 pub ty: Type,
398 pub mutability: Mutability,
400}
401
402impl GlobalType {
410 pub fn new(ty: Type, mutability: Mutability) -> Self {
421 Self { ty, mutability }
422 }
423}
424
425impl fmt::Display for GlobalType {
426 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427 let mutability = match self.mutability {
428 Mutability::Const => "constant",
429 Mutability::Var => "mutable",
430 };
431 write!(f, "{} ({})", self.ty, mutability)
432 }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
438#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
439#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
440#[rkyv(derive(Debug), compare(PartialEq))]
441pub struct InitExpr {
442 pub ops: Box<[InitExprOp]>,
444}
445
446impl InitExpr {
447 pub fn new<Ops>(ops: Ops) -> Self
449 where
450 Ops: Into<Box<[InitExprOp]>>,
451 {
452 Self { ops: ops.into() }
453 }
454
455 pub fn ops(&self) -> &[InitExprOp] {
457 &self.ops
458 }
459}
460
461#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
463#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
464#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
465#[rkyv(derive(Debug), compare(PartialEq))]
466#[repr(u8)]
467pub enum InitExprOp {
468 GlobalGetI32(GlobalIndex),
470 GlobalGetI64(GlobalIndex),
472 I32Const(i32),
474 I32Add,
476 I32Sub,
478 I32Mul,
480 I64Const(i64),
482 I64Add,
484 I64Sub,
486 I64Mul,
488}
489
490impl InitExprOp {
491 pub fn is_32bit_expression(&self) -> bool {
493 match self {
494 Self::GlobalGetI32(..)
495 | Self::I32Const(_)
496 | Self::I32Add
497 | Self::I32Sub
498 | Self::I32Mul => true,
499 Self::GlobalGetI64(_)
500 | Self::I64Const(_)
501 | Self::I64Add
502 | Self::I64Sub
503 | Self::I64Mul => false,
504 }
505 }
506}
507
508#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
511#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
512#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
513#[rkyv(derive(Debug), compare(PartialEq))]
514#[repr(u8)]
515pub enum GlobalInit {
516 I32Const(i32),
518 I64Const(i64),
520 F32Const(f32),
522 F64Const(f64),
524 V128Const(V128),
526 GetGlobal(GlobalIndex),
528 RefNullConst,
533 RefFunc(FunctionIndex),
535 Expr(InitExpr),
537}
538
539#[derive(Debug, Clone, PartialEq, Eq, Hash)]
545#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
546#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
547#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
548#[rkyv(derive(Debug))]
549pub enum TagKind {
550 Exception,
552}
553
554#[derive(Debug, Clone, PartialEq, Eq, Hash)]
557#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
558#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
559#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
560#[rkyv(derive(Debug))]
561pub struct TagType {
562 pub kind: TagKind,
564 pub params: Box<[Type]>,
566}
567
568impl TagType {
569 pub fn new<Params>(kind: TagKind, params: Params) -> Self
571 where
572 Params: Into<Box<[Type]>>,
573 {
574 Self {
575 kind,
576 params: params.into(),
577 }
578 }
579
580 pub fn params(&self) -> &[Type] {
582 &self.params
583 }
584
585 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
587 Self {
588 kind,
589 params: ty.params().into(),
590 }
591 }
592}
593
594impl fmt::Display for TagType {
595 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
596 write!(f, "({:?}) {:?}", self.kind, self.params(),)
597 }
598}
599
600#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
608#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
609#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
610#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
611#[rkyv(derive(Debug))]
612pub struct TableType {
613 pub ty: Type,
615 pub minimum: u32,
617 pub maximum: Option<u32>,
619 pub readonly: bool,
621}
622
623impl TableType {
624 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
627 Self {
628 ty,
629 minimum,
630 maximum,
631 readonly: false,
632 }
633 }
634
635 pub fn is_fixed_funcref_table(&self) -> bool {
637 matches!(self.ty, Type::FuncRef) && self.maximum == Some(self.minimum)
638 }
639}
640
641impl fmt::Display for TableType {
642 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643 if let Some(maximum) = self.maximum {
644 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
645 } else {
646 write!(f, "{} ({}..)", self.ty, self.minimum)
647 }
648 }
649}
650
651#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
658#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
659#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
660#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
661#[rkyv(derive(Debug))]
662pub struct MemoryType {
663 pub minimum: Pages,
665 pub maximum: Option<Pages>,
667 pub shared: bool,
669}
670
671impl MemoryType {
672 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
675 where
676 IntoPages: Into<Pages>,
677 {
678 Self {
679 minimum: minimum.into(),
680 maximum: maximum.map(Into::into),
681 shared,
682 }
683 }
684}
685
686impl fmt::Display for MemoryType {
687 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688 let shared = if self.shared { "shared" } else { "not shared" };
689 if let Some(maximum) = self.maximum {
690 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
691 } else {
692 write!(f, "{} ({:?}..)", shared, self.minimum)
693 }
694 }
695}
696
697#[derive(Debug, Clone, PartialEq, Eq, Hash)]
706#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
707pub struct ImportType<T = ExternType> {
708 module: String,
709 name: String,
710 ty: T,
711}
712
713impl<T> ImportType<T> {
714 pub fn new(module: &str, name: &str, ty: T) -> Self {
717 Self {
718 module: module.to_owned(),
719 name: name.to_owned(),
720 ty,
721 }
722 }
723
724 pub fn module(&self) -> &str {
726 &self.module
727 }
728
729 pub fn name(&self) -> &str {
732 &self.name
733 }
734
735 pub fn ty(&self) -> &T {
737 &self.ty
738 }
739}
740
741#[derive(Debug, Clone, PartialEq, Eq, Hash)]
753#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
754pub struct ExportType<T = ExternType> {
755 name: String,
756 ty: T,
757}
758
759impl<T> ExportType<T> {
760 pub fn new(name: &str, ty: T) -> Self {
763 Self {
764 name: name.to_string(),
765 ty,
766 }
767 }
768
769 pub fn name(&self) -> &str {
771 &self.name
772 }
773
774 pub fn ty(&self) -> &T {
776 &self.ty
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783
784 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
785 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
786 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
787 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
788
789 #[test]
790 fn convert_tuple_to_functiontype() {
791 let ty: FunctionType = VOID_TO_VOID.into();
792 assert_eq!(ty.params().len(), 0);
793 assert_eq!(ty.results().len(), 0);
794
795 let ty: FunctionType = I32_I32_TO_VOID.into();
796 assert_eq!(ty.params().len(), 2);
797 assert_eq!(ty.params()[0], Type::I32);
798 assert_eq!(ty.params()[1], Type::I32);
799 assert_eq!(ty.results().len(), 0);
800
801 let ty: FunctionType = V128_I64_TO_I32.into();
802 assert_eq!(ty.params().len(), 2);
803 assert_eq!(ty.params()[0], Type::V128);
804 assert_eq!(ty.params()[1], Type::I64);
805 assert_eq!(ty.results().len(), 1);
806 assert_eq!(ty.results()[0], Type::I32);
807
808 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
809 assert_eq!(ty.params().len(), 9);
810 assert_eq!(ty.results().len(), 9);
811 }
812
813 #[test]
814 fn signature_hash_is_stable() {
815 let ty: FunctionType = ([Type::I32, Type::F64], [Type::ExternRef]).into();
816 assert_eq!(ty.signature_hash(), ty.signature_hash());
817 }
818
819 #[test]
820 fn signature_hash_distinguishes() {
821 let left: FunctionType = ([Type::I32], [Type::I64]).into();
822 let right: FunctionType = ([Type::I64], [Type::I32]).into();
823 assert_ne!(left.signature_hash(), right.signature_hash());
824
825 let left: FunctionType = ([], [Type::I32, Type::I64]).into();
826 let right: FunctionType = ([Type::I32], [Type::I64]).into();
827 assert_ne!(left.signature_hash(), right.signature_hash());
828 }
829}