1#![doc(test(
2 no_crate_inject,
3 attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables))
4))]
5#![warn(rust_2018_idioms, unreachable_pub)]
6#![no_std]
7#![deny(unsafe_code)]
8
9extern crate alloc;
12use alloc::{boxed::Box, sync::Arc};
13use core::ops::{Deref, Range};
14
15const MEM_PAGE_SIZE: u64 = 65536;
17const MAX_MEMORY_SIZE: u64 = 4294967296;
18
19const fn max_page_count(page_size: u64) -> u64 {
20 MAX_MEMORY_SIZE / page_size
21}
22
23mod instructions;
24mod reference;
25mod value;
26pub use instructions::*;
27pub use reference::*;
28pub use value::*;
29
30#[cfg(feature = "archive")]
31pub mod archive;
32
33#[cfg(not(feature = "archive"))]
34pub mod archive {
35 #[derive(Debug, PartialEq, Eq)]
36 pub enum TwasmError {}
37 impl core::fmt::Display for TwasmError {
38 fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 Err(core::fmt::Error)
40 }
41 }
42 impl core::error::Error for TwasmError {}
43}
44
45#[derive(Clone, Default, PartialEq)]
51#[cfg_attr(feature = "debug", derive(Debug))]
52#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
53pub struct Module(Arc<ModuleInner>);
54
55impl From<ModuleInner> for Module {
56 fn from(inner: ModuleInner) -> Self {
57 Self(Arc::new(inner))
58 }
59}
60
61impl Deref for Module {
62 type Target = ModuleInner;
63
64 fn deref(&self) -> &ModuleInner {
65 &self.0
66 }
67}
68
69#[doc(hidden)]
70#[derive(Clone, Default, PartialEq)]
71#[cfg_attr(feature = "debug", derive(Debug))]
72#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
73pub struct ModuleInner {
74 pub start_func: Option<FuncAddr>,
78
79 pub funcs: Box<[Arc<WasmFunction>]>,
83
84 pub func_types: Arc<[Arc<FuncType>]>,
88
89 pub func_type_idxs: Arc<[u32]>,
91
92 pub exports: Arc<[Export]>,
96
97 pub globals: Box<[Global]>,
101
102 pub table_types: Box<[TableType]>,
106
107 pub memory_types: Box<[MemoryType]>,
111
112 pub imports: Box<[Import]>,
116
117 pub data: Box<[Data]>,
121
122 pub elements: Box<[Element]>,
126
127 pub local_memory_allocation: LocalMemoryAllocation,
129}
130
131impl Module {
132 pub fn imports(&self) -> impl Iterator<Item = ModuleImport<'_>> {
136 self.0.imports.iter().filter_map(|import| {
137 let ty = match &import.kind {
138 ImportKind::Function(type_idx) => Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)),
139 ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)),
140 ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)),
141 ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)),
142 }?;
143
144 Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty })
145 })
146 }
147
148 pub fn exports(&self) -> impl Iterator<Item = ModuleExport<'_>> {
152 fn imported_count(module: &ModuleInner, kind: ExternalKind) -> usize {
153 module
154 .imports
155 .iter()
156 .filter(|import| {
157 matches!(
158 (kind, &import.kind),
159 (ExternalKind::Func, ImportKind::Function(_))
160 | (ExternalKind::Table, ImportKind::Table(_))
161 | (ExternalKind::Memory, ImportKind::Memory(_))
162 | (ExternalKind::Global, ImportKind::Global(_))
163 )
164 })
165 .count()
166 }
167
168 fn imported_type(module: &ModuleInner, kind: ExternalKind, index: usize) -> Option<ExportType<'_>> {
169 let mut imports = module.imports.iter().filter(|import| {
170 matches!(
171 (kind, &import.kind),
172 (ExternalKind::Func, ImportKind::Function(_))
173 | (ExternalKind::Table, ImportKind::Table(_))
174 | (ExternalKind::Memory, ImportKind::Memory(_))
175 | (ExternalKind::Global, ImportKind::Global(_))
176 )
177 });
178 let import = imports.nth(index)?;
179
180 match &import.kind {
181 ImportKind::Function(type_idx) => Some(ExportType::Func(module.func_types.get(*type_idx as usize)?)),
182 ImportKind::Table(table_ty) => Some(ExportType::Table(table_ty)),
183 ImportKind::Memory(memory_ty) => Some(ExportType::Memory(memory_ty)),
184 ImportKind::Global(global_ty) => Some(ExportType::Global(global_ty)),
185 }
186 }
187
188 self.0.exports.iter().filter_map(move |export| {
189 let idx = export.index as usize;
190 let ty = match export.kind {
191 ExternalKind::Func => {
192 let imported_funcs = imported_count(&self.0, ExternalKind::Func);
193 if idx < imported_funcs {
194 imported_type(&self.0, ExternalKind::Func, idx)?
195 } else {
196 ExportType::Func(&self.0.funcs.get(idx - imported_funcs)?.ty)
197 }
198 }
199 ExternalKind::Table => {
200 let imported_tables = imported_count(&self.0, ExternalKind::Table);
201 if idx < imported_tables {
202 imported_type(&self.0, ExternalKind::Table, idx)?
203 } else {
204 ExportType::Table(self.0.table_types.get(idx - imported_tables)?)
205 }
206 }
207 ExternalKind::Memory => {
208 let imported_memories = imported_count(&self.0, ExternalKind::Memory);
209 if idx < imported_memories {
210 imported_type(&self.0, ExternalKind::Memory, idx)?
211 } else {
212 ExportType::Memory(self.0.memory_types.get(idx - imported_memories)?)
213 }
214 }
215 ExternalKind::Global => {
216 let imported_globals = imported_count(&self.0, ExternalKind::Global);
217 if idx < imported_globals {
218 imported_type(&self.0, ExternalKind::Global, idx)?
219 } else {
220 ExportType::Global(&self.0.globals.get(idx - imported_globals)?.ty)
221 }
222 }
223 };
224
225 Some(ModuleExport { name: export.name.as_ref(), ty })
226 })
227 }
228}
229
230pub struct ModuleExport<'a> {
232 pub name: &'a str,
234 pub ty: ExportType<'a>,
236}
237
238pub struct ModuleImport<'a> {
240 pub module: &'a str,
242 pub name: &'a str,
244 pub ty: ImportType<'a>,
246}
247
248pub enum ImportType<'a> {
250 Func(&'a FuncType),
252 Table(&'a TableType),
254 Memory(&'a MemoryType),
256 Global(&'a GlobalType),
258}
259
260pub enum ExportType<'a> {
262 Func(&'a FuncType),
264 Table(&'a TableType),
266 Memory(&'a MemoryType),
268 Global(&'a GlobalType),
270}
271
272#[derive(Clone, Copy, PartialEq, Eq, Default)]
274#[cfg_attr(feature = "debug", derive(Debug))]
275#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
276pub enum LocalMemoryAllocation {
277 #[default]
279 Skip,
280 Lazy,
282 Eager,
284}
285
286#[derive(Clone, Copy, PartialEq, Eq)]
290#[cfg_attr(feature = "debug", derive(Debug))]
291#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
292pub enum ExternalKind {
293 Func,
295 Table,
297 Memory,
299 Global,
301}
302
303pub type Addr = u32;
309
310pub type FuncAddr = Addr;
312pub type TableAddr = Addr;
313pub type MemAddr = Addr;
314pub type GlobalAddr = Addr;
315pub type ElemAddr = Addr;
316pub type DataAddr = Addr;
317pub type ExternAddr = Addr;
318pub type ConstIdx = Addr;
319
320pub type TypeAddr = Addr;
322pub type LocalAddr = u16; pub type ModuleInstanceAddr = Addr;
324
325#[derive(Clone)]
329#[cfg_attr(feature = "debug", derive(Debug))]
330pub enum ExternVal {
331 Func(FuncAddr),
332 Table(TableAddr),
333 Memory(MemAddr),
334 Global(GlobalAddr),
335}
336
337impl ExternVal {
338 #[inline]
339 pub const fn kind(&self) -> ExternalKind {
340 match self {
341 Self::Func(_) => ExternalKind::Func,
342 Self::Table(_) => ExternalKind::Table,
343 Self::Memory(_) => ExternalKind::Memory,
344 Self::Global(_) => ExternalKind::Global,
345 }
346 }
347
348 #[inline]
349 pub const fn new(kind: ExternalKind, addr: Addr) -> Self {
350 match kind {
351 ExternalKind::Func => Self::Func(addr),
352 ExternalKind::Table => Self::Table(addr),
353 ExternalKind::Memory => Self::Memory(addr),
354 ExternalKind::Global => Self::Global(addr),
355 }
356 }
357}
358
359#[derive(Clone, PartialEq, Eq, Default)]
363#[cfg_attr(feature = "debug", derive(Debug))]
364#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
365pub struct FuncType {
366 data: Box<[WasmType]>,
367 param_count: u16,
368}
369
370impl FuncType {
371 pub fn new(params: &[WasmType], results: &[WasmType]) -> Self {
373 let data: Box<[WasmType]> = params.iter().cloned().chain(results.iter().cloned()).collect();
374 Self { data, param_count: params.len() as u16 }
375 }
376
377 pub fn params(&self) -> &[WasmType] {
379 &self.data[..self.param_count as usize]
380 }
381
382 pub fn results(&self) -> &[WasmType] {
384 &self.data[self.param_count as usize..]
385 }
386}
387
388#[derive(Default, Clone, Copy, PartialEq, Eq)]
389#[cfg_attr(feature = "debug", derive(Debug))]
390#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
391pub struct ValueCounts {
392 pub c32: u16,
393 pub c64: u16,
394 pub c128: u16,
395}
396
397impl ValueCounts {
398 #[inline]
399 pub fn is_empty(&self) -> bool {
400 self.c32 == 0 && self.c64 == 0 && self.c128 == 0
401 }
402}
403
404impl<'a> FromIterator<&'a WasmType> for ValueCounts {
405 #[inline]
406 fn from_iter<I: IntoIterator<Item = &'a WasmType>>(iter: I) -> Self {
407 let mut counts = Self::default();
408
409 for ty in iter {
410 match ty {
411 WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
412 WasmType::I64 | WasmType::F64 => counts.c64 += 1,
413 WasmType::V128 => counts.c128 += 1,
414 }
415 }
416 counts
417 }
418}
419
420#[derive(Clone, PartialEq, Default)]
421#[cfg_attr(feature = "debug", derive(Debug))]
422#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
423pub struct WasmFunction {
424 pub instructions: Box<[Instruction]>,
425 pub data: WasmFunctionData,
426 pub locals: ValueCounts,
427 pub params: ValueCounts,
428 pub results: ValueCounts,
429 pub ty: Arc<FuncType>,
430}
431
432#[derive(Clone, PartialEq, Eq, Default)]
433#[cfg_attr(feature = "debug", derive(Debug))]
434#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
435pub struct WasmFunctionData {
436 pub v128_constants: Box<[[u8; 16]]>,
437 pub branch_table_targets: Box<[u32]>,
438}
439
440impl WasmFunctionData {
441 #[inline(always)]
443 pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] {
444 *self.v128_constants.get(idx as usize).unwrap_or_else(|| unreachable!("invalid v128 constant index: {idx}"))
445 }
446}
447
448#[derive(Clone, PartialEq, Eq)]
450#[cfg_attr(feature = "debug", derive(Debug))]
451#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
452pub struct Export {
453 pub name: Box<str>,
455 pub kind: ExternalKind,
457 pub index: u32,
459}
460
461#[derive(Clone, PartialEq)]
462#[cfg_attr(feature = "debug", derive(Debug))]
463#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
464pub struct Global {
465 pub ty: GlobalType,
466 pub init: Box<[ConstInstruction]>,
467}
468
469#[derive(Clone, Copy, PartialEq, Eq)]
470#[cfg_attr(feature = "debug", derive(Debug))]
471#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
472pub struct GlobalType {
473 pub mutable: bool,
474 pub ty: WasmType,
475}
476
477impl GlobalType {
478 pub const fn new(ty: WasmType, mutable: bool) -> Self {
480 Self { mutable, ty }
481 }
482
483 pub const fn with_ty(mut self, ty: WasmType) -> Self {
485 self.ty = ty;
486 self
487 }
488
489 pub const fn with_mutable(mut self, mutable: bool) -> Self {
491 self.mutable = mutable;
492 self
493 }
494}
495
496impl Default for GlobalType {
497 fn default() -> Self {
498 Self::new(WasmType::I32, false)
499 }
500}
501
502#[derive(Copy, Clone, PartialEq, Eq)]
503#[cfg_attr(feature = "debug", derive(Debug))]
504#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
505pub struct TableType {
506 arch: MemoryArch,
507 pub element_type: WasmType,
508 pub size_initial: u64,
509 pub size_max: Option<u64>,
510}
511
512impl TableType {
513 pub const fn empty() -> Self {
514 Self::new(WasmType::RefFunc, 0, None)
515 }
516
517 pub const fn new(element_type: WasmType, size_initial: u64, size_max: Option<u64>) -> Self {
519 Self { arch: MemoryArch::I32, element_type, size_initial, size_max }
520 }
521
522 pub const fn new64(element_type: WasmType, size_initial: u64, size_max: Option<u64>) -> Self {
524 Self { arch: MemoryArch::I64, element_type, size_initial, size_max }
525 }
526
527 #[inline]
528 pub const fn arch(&self) -> MemoryArch {
529 self.arch
530 }
531}
532
533#[derive(Copy, Clone, PartialEq, Eq)]
535#[cfg_attr(feature = "debug", derive(Debug))]
536#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
537pub struct MemoryType {
538 arch: MemoryArch,
539 page_count_initial: u64,
540 page_count_max: Option<u64>,
541 page_size: Option<u64>,
542}
543
544impl MemoryType {
545 pub const fn new(
547 arch: MemoryArch,
548 page_count_initial: u64,
549 page_count_max: Option<u64>,
550 page_size: Option<u64>,
551 ) -> Self {
552 Self { arch, page_count_initial, page_count_max, page_size }
553 }
554
555 #[inline]
556 pub const fn arch(&self) -> MemoryArch {
557 self.arch
558 }
559
560 #[inline]
561 pub const fn page_count_initial(&self) -> u64 {
562 self.page_count_initial
563 }
564
565 #[inline]
566 pub const fn page_count_max(&self) -> u64 {
567 if let Some(page_count_max) = self.page_count_max { page_count_max } else { max_page_count(self.page_size()) }
568 }
569
570 #[inline]
571 pub const fn page_size(&self) -> u64 {
572 if let Some(page_size) = self.page_size { page_size } else { MEM_PAGE_SIZE }
573 }
574
575 #[inline]
576 pub const fn initial_size(&self) -> u64 {
577 self.page_count_initial * self.page_size()
578 }
579
580 #[inline]
581 pub const fn max_size(&self) -> u64 {
582 self.page_count_max() * self.page_size()
583 }
584
585 pub const fn with_arch(mut self, arch: MemoryArch) -> Self {
587 self.arch = arch;
588 self
589 }
590
591 pub const fn with_page_count_initial(mut self, page_count_initial: u64) -> Self {
593 self.page_count_initial = page_count_initial;
594 self
595 }
596
597 pub const fn with_page_count_max(mut self, page_count_max: Option<u64>) -> Self {
599 self.page_count_max = page_count_max;
600 self
601 }
602
603 pub const fn with_page_size(mut self, page_size: Option<u64>) -> Self {
605 self.page_size = page_size;
606 self
607 }
608}
609
610impl Default for MemoryType {
611 fn default() -> Self {
612 Self::new(MemoryArch::I32, 0, None, None)
613 }
614}
615
616#[derive(Copy, Clone, PartialEq, Eq, Hash)]
617#[cfg_attr(feature = "debug", derive(Debug))]
618#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
619pub enum MemoryArch {
620 I32,
621 I64,
622}
623
624#[derive(Clone, PartialEq, Eq)]
625#[cfg_attr(feature = "debug", derive(Debug))]
626#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
627pub struct Import {
628 pub module: Box<str>,
629 pub name: Box<str>,
630 pub kind: ImportKind,
631}
632
633#[derive(Clone, PartialEq, Eq)]
634#[cfg_attr(feature = "debug", derive(Debug))]
635#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
636pub enum ImportKind {
637 Function(TypeAddr),
638 Table(TableType),
639 Memory(MemoryType),
640 Global(GlobalType),
641}
642
643impl From<&ImportKind> for ExternalKind {
644 fn from(kind: &ImportKind) -> Self {
645 match kind {
646 ImportKind::Function(_) => Self::Func,
647 ImportKind::Table(_) => Self::Table,
648 ImportKind::Memory(_) => Self::Memory,
649 ImportKind::Global(_) => Self::Global,
650 }
651 }
652}
653
654#[derive(Clone, PartialEq)]
655#[cfg_attr(feature = "debug", derive(Debug))]
656#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
657pub struct Data {
658 pub data: Box<[u8]>,
659 pub range: Range<usize>,
660 pub kind: DataKind,
661}
662
663#[derive(Clone, PartialEq)]
664#[cfg_attr(feature = "debug", derive(Debug))]
665#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
666pub enum DataKind {
667 Active { mem: MemAddr, offset: Box<[ConstInstruction]> },
668 Passive,
669}
670
671#[derive(Clone, PartialEq)]
672#[cfg_attr(feature = "debug", derive(Debug))]
673#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
674pub struct Element {
675 pub kind: ElementKind,
676 pub items: Box<[ElementItem]>,
677 pub range: Range<usize>,
678 pub ty: WasmType,
679}
680
681#[derive(Clone, PartialEq)]
682#[cfg_attr(feature = "debug", derive(Debug))]
683#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
684pub enum ElementKind {
685 Passive,
686 Active { table: TableAddr, offset: Box<[ConstInstruction]> },
687 Declared,
688}
689
690#[derive(Clone, PartialEq)]
691#[cfg_attr(feature = "debug", derive(Debug))]
692#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
693pub enum ElementItem {
694 Func(FuncAddr),
695 Expr(Box<[ConstInstruction]>),
696}