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
291impl fmt::Display for FunctionType {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 let params = self
294 .params
295 .iter()
296 .map(|p| format!("{p:?}"))
297 .collect::<Vec<_>>()
298 .join(", ");
299 let results = self
300 .results
301 .iter()
302 .map(|p| format!("{p:?}"))
303 .collect::<Vec<_>>()
304 .join(", ");
305 write!(f, "[{params}] -> [{results}]")
306 }
307}
308
309macro_rules! implement_from_pair_to_functiontype {
312 ($($N:literal,$M:literal)+) => {
313 $(
314 impl From<([Type; $N], [Type; $M])> for FunctionType {
315 fn from(pair: ([Type; $N], [Type; $M])) -> Self {
316 Self::new(pair.0, pair.1)
317 }
318 }
319 )+
320 }
321}
322
323implement_from_pair_to_functiontype! {
324 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9
325 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9
326 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9
327 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9
328 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9
329 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9
330 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9
331 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9
332 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9
333 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9
334}
335
336impl From<&Self> for FunctionType {
337 fn from(as_ref: &Self) -> Self {
338 as_ref.clone()
339 }
340}
341
342#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
344#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
345#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
346#[rkyv(derive(Debug), compare(PartialOrd, PartialEq))]
347#[repr(u8)]
348pub enum Mutability {
349 Const,
351 Var,
353}
354
355impl Mutability {
356 pub fn is_mutable(self) -> bool {
358 self.into()
359 }
360}
361
362impl From<bool> for Mutability {
363 fn from(value: bool) -> Self {
364 if value { Self::Var } else { Self::Const }
365 }
366}
367
368impl From<Mutability> for bool {
369 fn from(value: Mutability) -> Self {
370 match value {
371 Mutability::Var => true,
372 Mutability::Const => false,
373 }
374 }
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
379#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
380#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
381#[rkyv(derive(Debug), compare(PartialEq))]
382pub struct GlobalType {
383 pub ty: Type,
385 pub mutability: Mutability,
387}
388
389impl GlobalType {
397 pub fn new(ty: Type, mutability: Mutability) -> Self {
408 Self { ty, mutability }
409 }
410}
411
412impl fmt::Display for GlobalType {
413 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414 let mutability = match self.mutability {
415 Mutability::Const => "constant",
416 Mutability::Var => "mutable",
417 };
418 write!(f, "{} ({})", self.ty, mutability)
419 }
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
425#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
426#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
427#[rkyv(derive(Debug), compare(PartialEq))]
428pub struct InitExpr {
429 pub ops: Box<[InitExprOp]>,
431}
432
433impl InitExpr {
434 pub fn new<Ops>(ops: Ops) -> Self
436 where
437 Ops: Into<Box<[InitExprOp]>>,
438 {
439 Self { ops: ops.into() }
440 }
441
442 pub fn ops(&self) -> &[InitExprOp] {
444 &self.ops
445 }
446}
447
448#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)]
450#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
451#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
452#[rkyv(derive(Debug), compare(PartialEq))]
453#[repr(u8)]
454pub enum InitExprOp {
455 GlobalGetI32(GlobalIndex),
457 GlobalGetI64(GlobalIndex),
459 I32Const(i32),
461 I32Add,
463 I32Sub,
465 I32Mul,
467 I64Const(i64),
469 I64Add,
471 I64Sub,
473 I64Mul,
475}
476
477impl InitExprOp {
478 pub fn is_32bit_expression(&self) -> bool {
480 match self {
481 Self::GlobalGetI32(..)
482 | Self::I32Const(_)
483 | Self::I32Add
484 | Self::I32Sub
485 | Self::I32Mul => true,
486 Self::GlobalGetI64(_)
487 | Self::I64Const(_)
488 | Self::I64Add
489 | Self::I64Sub
490 | Self::I64Mul => false,
491 }
492 }
493}
494
495#[derive(Debug, Clone, PartialEq, RkyvSerialize, RkyvDeserialize, Archive)]
498#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
499#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
500#[rkyv(derive(Debug), compare(PartialEq))]
501#[repr(u8)]
502pub enum GlobalInit {
503 I32Const(i32),
505 I64Const(i64),
507 F32Const(f32),
509 F64Const(f64),
511 V128Const(V128),
513 GetGlobal(GlobalIndex),
515 RefNullConst,
520 RefFunc(FunctionIndex),
522 Expr(InitExpr),
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Hash)]
532#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
533#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
534#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
535#[rkyv(derive(Debug))]
536pub enum TagKind {
537 Exception,
539}
540
541#[derive(Debug, Clone, PartialEq, Eq, Hash)]
544#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
545#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
546#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
547#[rkyv(derive(Debug))]
548pub struct TagType {
549 pub kind: TagKind,
551 pub params: Box<[Type]>,
553}
554
555impl TagType {
556 pub fn new<Params>(kind: TagKind, params: Params) -> Self
558 where
559 Params: Into<Box<[Type]>>,
560 {
561 Self {
562 kind,
563 params: params.into(),
564 }
565 }
566
567 pub fn params(&self) -> &[Type] {
569 &self.params
570 }
571
572 pub fn from_fn_type(kind: TagKind, ty: FunctionType) -> Self {
574 Self {
575 kind,
576 params: ty.params().into(),
577 }
578 }
579}
580
581impl fmt::Display for TagType {
582 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
583 write!(f, "({:?}) {:?}", self.kind, self.params(),)
584 }
585}
586
587#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
595#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
596#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
597#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
598#[rkyv(derive(Debug))]
599pub struct TableType {
600 pub ty: Type,
602 pub minimum: u32,
604 pub maximum: Option<u32>,
606}
607
608impl TableType {
609 pub fn new(ty: Type, minimum: u32, maximum: Option<u32>) -> Self {
612 Self {
613 ty,
614 minimum,
615 maximum,
616 }
617 }
618}
619
620impl fmt::Display for TableType {
621 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622 if let Some(maximum) = self.maximum {
623 write!(f, "{} ({}..{})", self.ty, self.minimum, maximum)
624 } else {
625 write!(f, "{} ({}..)", self.ty, self.minimum)
626 }
627 }
628}
629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
637#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
638#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
639#[derive(RkyvSerialize, RkyvDeserialize, Archive)]
640#[rkyv(derive(Debug))]
641pub struct MemoryType {
642 pub minimum: Pages,
644 pub maximum: Option<Pages>,
646 pub shared: bool,
648}
649
650impl MemoryType {
651 pub fn new<IntoPages>(minimum: IntoPages, maximum: Option<IntoPages>, shared: bool) -> Self
654 where
655 IntoPages: Into<Pages>,
656 {
657 Self {
658 minimum: minimum.into(),
659 maximum: maximum.map(Into::into),
660 shared,
661 }
662 }
663}
664
665impl fmt::Display for MemoryType {
666 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
667 let shared = if self.shared { "shared" } else { "not shared" };
668 if let Some(maximum) = self.maximum {
669 write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum)
670 } else {
671 write!(f, "{} ({:?}..)", shared, self.minimum)
672 }
673 }
674}
675
676#[derive(Debug, Clone, PartialEq, Eq, Hash)]
685#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
686pub struct ImportType<T = ExternType> {
687 module: String,
688 name: String,
689 ty: T,
690}
691
692impl<T> ImportType<T> {
693 pub fn new(module: &str, name: &str, ty: T) -> Self {
696 Self {
697 module: module.to_owned(),
698 name: name.to_owned(),
699 ty,
700 }
701 }
702
703 pub fn module(&self) -> &str {
705 &self.module
706 }
707
708 pub fn name(&self) -> &str {
711 &self.name
712 }
713
714 pub fn ty(&self) -> &T {
716 &self.ty
717 }
718}
719
720#[derive(Debug, Clone, PartialEq, Eq, Hash)]
732#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
733pub struct ExportType<T = ExternType> {
734 name: String,
735 ty: T,
736}
737
738impl<T> ExportType<T> {
739 pub fn new(name: &str, ty: T) -> Self {
742 Self {
743 name: name.to_string(),
744 ty,
745 }
746 }
747
748 pub fn name(&self) -> &str {
750 &self.name
751 }
752
753 pub fn ty(&self) -> &T {
755 &self.ty
756 }
757}
758
759#[cfg(test)]
760mod tests {
761 use super::*;
762
763 const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []);
764 const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []);
765 const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]);
766 const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]);
767
768 #[test]
769 fn convert_tuple_to_functiontype() {
770 let ty: FunctionType = VOID_TO_VOID.into();
771 assert_eq!(ty.params().len(), 0);
772 assert_eq!(ty.results().len(), 0);
773
774 let ty: FunctionType = I32_I32_TO_VOID.into();
775 assert_eq!(ty.params().len(), 2);
776 assert_eq!(ty.params()[0], Type::I32);
777 assert_eq!(ty.params()[1], Type::I32);
778 assert_eq!(ty.results().len(), 0);
779
780 let ty: FunctionType = V128_I64_TO_I32.into();
781 assert_eq!(ty.params().len(), 2);
782 assert_eq!(ty.params()[0], Type::V128);
783 assert_eq!(ty.params()[1], Type::I64);
784 assert_eq!(ty.results().len(), 1);
785 assert_eq!(ty.results()[0], Type::I32);
786
787 let ty: FunctionType = NINE_V128_TO_NINE_I32.into();
788 assert_eq!(ty.params().len(), 9);
789 assert_eq!(ty.results().len(), 9);
790 }
791}