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::hint::cold_path;
14use core::ops::{Deref, Range};
15
16const MEM_PAGE_SIZE: u64 = 65536;
18const MAX_MEMORY_SIZE: u64 = 4294967296;
19
20const fn max_page_count(page_size: u64) -> u64 {
21 MAX_MEMORY_SIZE / page_size
22}
23
24#[cfg(feature = "log")]
26#[allow(clippy::single_component_path_imports, unused_imports)]
27use log;
28
29#[cfg(not(feature = "log"))]
31#[allow(unused_imports, unused_macros)]
32pub(crate) mod log {
33 macro_rules! debug ( ($($tt:tt)*) => {{}} );
34 macro_rules! info ( ($($tt:tt)*) => {{}} );
35 macro_rules! error ( ($($tt:tt)*) => {{}} );
36 pub(crate) use debug;
37 pub(crate) use error;
38 pub(crate) use info;
39}
40
41mod instructions;
42mod value;
43pub use instructions::*;
44pub use value::*;
45
46#[cfg(feature = "archive")]
47pub mod archive;
48
49#[cfg(not(feature = "archive"))]
50pub mod archive {
51 #[derive(Debug)]
52 pub enum TwasmError {}
53 impl core::fmt::Display for TwasmError {
54 fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55 Err(core::fmt::Error)
56 }
57 }
58 impl core::error::Error for TwasmError {}
59}
60
61#[derive(Clone, Default, PartialEq)]
67#[cfg_attr(feature = "debug", derive(Debug))]
68#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
69pub struct Module(Arc<ModuleInner>);
70
71impl From<ModuleInner> for Module {
72 fn from(inner: ModuleInner) -> Self {
73 Self(Arc::new(inner))
74 }
75}
76
77impl Deref for Module {
78 type Target = ModuleInner;
79 fn deref(&self) -> &ModuleInner {
80 &self.0
81 }
82}
83
84#[doc(hidden)]
85#[derive(Clone, Default, PartialEq)]
86#[cfg_attr(feature = "debug", derive(Debug))]
87#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
88pub struct ModuleInner {
89 pub start_func: Option<FuncAddr>,
93
94 pub funcs: Box<[Arc<WasmFunction>]>,
98
99 pub func_types: Arc<[Arc<FuncType>]>,
103
104 pub func_type_idxs: Arc<[u32]>,
106
107 pub exports: Arc<[Export]>,
111
112 pub globals: Box<[Global]>,
116
117 pub table_types: Box<[TableType]>,
121
122 pub memory_types: Box<[MemoryType]>,
126
127 pub imports: Box<[Import]>,
131
132 pub data: Box<[Data]>,
136
137 pub elements: Box<[Element]>,
141
142 pub local_memory_allocation: LocalMemoryAllocation,
144}
145
146impl Module {
147 pub fn imports(&self) -> impl Iterator<Item = ModuleImport<'_>> {
151 self.0.imports.iter().filter_map(|import| {
152 let ty = match &import.kind {
153 ImportKind::Function(type_idx) => Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)),
154 ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)),
155 ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)),
156 ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)),
157 }?;
158
159 Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty })
160 })
161 }
162
163 pub fn exports(&self) -> impl Iterator<Item = ModuleExport<'_>> {
167 fn imported_count(module: &ModuleInner, kind: ExternalKind) -> usize {
168 module
169 .imports
170 .iter()
171 .filter(|import| {
172 matches!(
173 (kind, &import.kind),
174 (ExternalKind::Func, ImportKind::Function(_))
175 | (ExternalKind::Table, ImportKind::Table(_))
176 | (ExternalKind::Memory, ImportKind::Memory(_))
177 | (ExternalKind::Global, ImportKind::Global(_))
178 )
179 })
180 .count()
181 }
182
183 fn imported_func_type(module: &ModuleInner, function_index: usize) -> Option<&FuncType> {
184 let mut seen = 0usize;
185 for import in module.imports.iter() {
186 if let ImportKind::Function(type_idx) = import.kind {
187 if seen == function_index {
188 return module.func_types.get(type_idx as usize).map(|ty| &**ty);
189 }
190 seen += 1;
191 }
192 }
193 None
194 }
195
196 fn imported_table_type(module: &ModuleInner, table_index: usize) -> Option<&TableType> {
197 let mut seen = 0usize;
198 for import in module.imports.iter() {
199 if let ImportKind::Table(table_ty) = &import.kind {
200 if seen == table_index {
201 return Some(table_ty);
202 }
203 seen += 1;
204 }
205 }
206 None
207 }
208
209 fn imported_memory_type(module: &ModuleInner, memory_index: usize) -> Option<&MemoryType> {
210 let mut seen = 0usize;
211 for import in module.imports.iter() {
212 if let ImportKind::Memory(memory_ty) = &import.kind {
213 if seen == memory_index {
214 return Some(memory_ty);
215 }
216 seen += 1;
217 }
218 }
219 None
220 }
221
222 fn imported_global_type(module: &Module, global_index: usize) -> Option<&GlobalType> {
223 let mut seen = 0usize;
224 for import in module.imports.iter() {
225 if let ImportKind::Global(global_ty) = &import.kind {
226 if seen == global_index {
227 return Some(global_ty);
228 }
229 seen += 1;
230 }
231 }
232 None
233 }
234
235 self.0.exports.iter().filter_map(move |export| {
236 let idx = export.index as usize;
237 let ty = match export.kind {
238 ExternalKind::Func => {
239 let imported_funcs = imported_count(&self.0, ExternalKind::Func);
240 if idx < imported_funcs {
241 ExportType::Func(imported_func_type(&self.0, idx)?)
242 } else {
243 let local_idx = idx - imported_funcs;
244 ExportType::Func(&self.0.funcs.get(local_idx)?.ty)
245 }
246 }
247 ExternalKind::Table => {
248 let imported_tables = imported_count(&self.0, ExternalKind::Table);
249 if idx < imported_tables {
250 ExportType::Table(imported_table_type(&self.0, idx)?)
251 } else {
252 let local_idx = idx - imported_tables;
253 ExportType::Table(self.0.table_types.get(local_idx)?)
254 }
255 }
256 ExternalKind::Memory => {
257 let imported_memories = imported_count(&self.0, ExternalKind::Memory);
258 if idx < imported_memories {
259 ExportType::Memory(imported_memory_type(&self.0, idx)?)
260 } else {
261 let local_idx = idx - imported_memories;
262 ExportType::Memory(self.0.memory_types.get(local_idx)?)
263 }
264 }
265 ExternalKind::Global => {
266 let imported_globals = imported_count(&self.0, ExternalKind::Global);
267 if idx < imported_globals {
268 ExportType::Global(imported_global_type(self, idx)?)
269 } else {
270 let local_idx = idx - imported_globals;
271 ExportType::Global(&self.0.globals.get(local_idx)?.ty)
272 }
273 }
274 };
275
276 Some(ModuleExport { name: export.name.as_ref(), ty })
277 })
278 }
279}
280
281pub struct ModuleExport<'a> {
283 pub name: &'a str,
285 pub ty: ExportType<'a>,
287}
288
289pub struct ModuleImport<'a> {
291 pub module: &'a str,
293 pub name: &'a str,
295 pub ty: ImportType<'a>,
297}
298
299pub enum ImportType<'a> {
301 Func(&'a FuncType),
303 Table(&'a TableType),
305 Memory(&'a MemoryType),
307 Global(&'a GlobalType),
309}
310
311pub enum ExportType<'a> {
313 Func(&'a FuncType),
315 Table(&'a TableType),
317 Memory(&'a MemoryType),
319 Global(&'a GlobalType),
321}
322
323#[derive(Clone, Copy, PartialEq, Eq, Default)]
325#[cfg_attr(feature = "debug", derive(Debug))]
326#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
327pub enum LocalMemoryAllocation {
328 #[default]
330 Skip,
331 Lazy,
333 Eager,
335}
336
337#[derive(Clone, Copy, PartialEq, Eq)]
341#[cfg_attr(feature = "debug", derive(Debug))]
342#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
343pub enum ExternalKind {
344 Func,
346 Table,
348 Memory,
350 Global,
352}
353
354pub type Addr = u32;
360
361pub type FuncAddr = Addr;
363pub type TableAddr = Addr;
364pub type MemAddr = Addr;
365pub type GlobalAddr = Addr;
366pub type ElemAddr = Addr;
367pub type DataAddr = Addr;
368pub type ExternAddr = Addr;
369pub type ConstIdx = Addr;
370
371pub type TypeAddr = Addr;
373pub type LocalAddr = u16; pub type ModuleInstanceAddr = Addr;
375
376#[derive(Clone)]
380#[cfg_attr(feature = "debug", derive(Debug))]
381pub enum ExternVal {
382 Func(FuncAddr),
383 Table(TableAddr),
384 Memory(MemAddr),
385 Global(GlobalAddr),
386}
387
388impl ExternVal {
389 #[inline]
390 pub const fn kind(&self) -> ExternalKind {
391 match self {
392 Self::Func(_) => ExternalKind::Func,
393 Self::Table(_) => ExternalKind::Table,
394 Self::Memory(_) => ExternalKind::Memory,
395 Self::Global(_) => ExternalKind::Global,
396 }
397 }
398
399 #[inline]
400 pub const fn new(kind: ExternalKind, addr: Addr) -> Self {
401 match kind {
402 ExternalKind::Func => Self::Func(addr),
403 ExternalKind::Table => Self::Table(addr),
404 ExternalKind::Memory => Self::Memory(addr),
405 ExternalKind::Global => Self::Global(addr),
406 }
407 }
408}
409
410#[derive(Clone, PartialEq, Eq, Default)]
414#[cfg_attr(feature = "debug", derive(Debug))]
415#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
416pub struct FuncType {
417 data: Box<[WasmType]>,
418 param_count: u16,
419}
420
421impl FuncType {
422 pub fn new(params: &[WasmType], results: &[WasmType]) -> Self {
424 let param_count = params.len() as u16;
425 let data: Box<[WasmType]> = params.iter().cloned().chain(results.iter().cloned()).collect();
426 Self { data, param_count }
427 }
428
429 pub fn params(&self) -> &[WasmType] {
431 &self.data[..self.param_count as usize]
432 }
433
434 pub fn results(&self) -> &[WasmType] {
436 &self.data[self.param_count as usize..]
437 }
438}
439
440#[derive(Default, Clone, Copy, PartialEq, Eq)]
441#[cfg_attr(feature = "debug", derive(Debug))]
442#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
443pub struct ValueCounts {
444 pub c32: u16,
445 pub c64: u16,
446 pub c128: u16,
447}
448
449impl ValueCounts {
450 #[inline]
451 pub fn is_empty(&self) -> bool {
452 self.c32 == 0 && self.c64 == 0 && self.c128 == 0
453 }
454}
455
456impl<'a> FromIterator<&'a WasmType> for ValueCounts {
457 #[inline]
458 fn from_iter<I: IntoIterator<Item = &'a WasmType>>(iter: I) -> Self {
459 let mut counts = Self::default();
460
461 for ty in iter {
462 match ty {
463 WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
464 WasmType::I64 | WasmType::F64 => counts.c64 += 1,
465 WasmType::V128 => counts.c128 += 1,
466 }
467 }
468 counts
469 }
470}
471
472#[derive(Clone, PartialEq, Default)]
473#[cfg_attr(feature = "debug", derive(Debug))]
474#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
475pub struct WasmFunction {
476 pub instructions: Box<[Instruction]>,
477 pub data: WasmFunctionData,
478 pub locals: ValueCounts,
479 pub params: ValueCounts,
480 pub results: ValueCounts,
481 pub ty: Arc<FuncType>,
482}
483
484#[derive(Clone, PartialEq, Eq, Default)]
485#[cfg_attr(feature = "debug", derive(Debug))]
486#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
487pub struct WasmFunctionData {
488 pub v128_constants: Box<[[u8; 16]]>,
489 pub branch_table_targets: Box<[u32]>,
490}
491
492impl WasmFunctionData {
493 #[inline(always)]
495 pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] {
496 let Some(val) = self.v128_constants.get(idx as usize) else {
497 cold_path();
498 unreachable!("invalid v128 constant index");
499 };
500 *val
501 }
502}
503
504#[derive(Clone, PartialEq, Eq)]
506#[cfg_attr(feature = "debug", derive(Debug))]
507#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
508pub struct Export {
509 pub name: Box<str>,
511 pub kind: ExternalKind,
513 pub index: u32,
515}
516
517#[derive(Clone, PartialEq)]
518#[cfg_attr(feature = "debug", derive(Debug))]
519#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
520pub struct Global {
521 pub ty: GlobalType,
522 pub init: Box<[ConstInstruction]>,
523}
524
525#[derive(Clone, Copy, PartialEq, Eq)]
526#[cfg_attr(feature = "debug", derive(Debug))]
527#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
528pub struct GlobalType {
529 pub mutable: bool,
530 pub ty: WasmType,
531}
532
533impl GlobalType {
534 pub const fn new(ty: WasmType, mutable: bool) -> Self {
536 Self { mutable, ty }
537 }
538
539 pub const fn with_ty(mut self, ty: WasmType) -> Self {
541 self.ty = ty;
542 self
543 }
544
545 pub const fn with_mutable(mut self, mutable: bool) -> Self {
547 self.mutable = mutable;
548 self
549 }
550}
551
552impl Default for GlobalType {
553 fn default() -> Self {
554 Self::new(WasmType::I32, false)
555 }
556}
557
558#[derive(Clone, PartialEq, Eq)]
559#[cfg_attr(feature = "debug", derive(Debug))]
560#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
561pub struct TableType {
562 pub element_type: WasmType,
563 pub size_initial: u32,
564 pub size_max: Option<u32>,
565}
566
567impl TableType {
568 pub fn empty() -> Self {
569 Self { element_type: WasmType::RefFunc, size_initial: 0, size_max: None }
570 }
571
572 pub fn new(element_type: WasmType, size_initial: u32, size_max: Option<u32>) -> Self {
573 Self { element_type, size_initial, size_max }
574 }
575}
576
577#[derive(Copy, Clone, PartialEq, Eq)]
579#[cfg_attr(feature = "debug", derive(Debug))]
580#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
581pub struct MemoryType {
582 arch: MemoryArch,
583 page_count_initial: u64,
584 page_count_max: Option<u64>,
585 page_size: Option<u64>,
586}
587
588impl MemoryType {
589 pub const fn new(
591 arch: MemoryArch,
592 page_count_initial: u64,
593 page_count_max: Option<u64>,
594 page_size: Option<u64>,
595 ) -> Self {
596 Self { arch, page_count_initial, page_count_max, page_size }
597 }
598
599 #[inline]
600 pub const fn arch(&self) -> MemoryArch {
601 self.arch
602 }
603
604 #[inline]
605 pub const fn page_count_initial(&self) -> u64 {
606 self.page_count_initial
607 }
608
609 #[inline]
610 pub const fn page_count_max(&self) -> u64 {
611 if let Some(page_count_max) = self.page_count_max { page_count_max } else { max_page_count(self.page_size()) }
612 }
613
614 #[inline]
615 pub const fn page_size(&self) -> u64 {
616 if let Some(page_size) = self.page_size { page_size } else { MEM_PAGE_SIZE }
617 }
618
619 #[inline]
620 pub const fn initial_size(&self) -> u64 {
621 self.page_count_initial * self.page_size()
622 }
623
624 #[inline]
625 pub const fn max_size(&self) -> u64 {
626 self.page_count_max() * self.page_size()
627 }
628
629 pub const fn with_arch(mut self, arch: MemoryArch) -> Self {
631 self.arch = arch;
632 self
633 }
634
635 pub const fn with_page_count_initial(mut self, page_count_initial: u64) -> Self {
637 self.page_count_initial = page_count_initial;
638 self
639 }
640
641 pub const fn with_page_count_max(mut self, page_count_max: Option<u64>) -> Self {
643 self.page_count_max = page_count_max;
644 self
645 }
646
647 pub const fn with_page_size(mut self, page_size: Option<u64>) -> Self {
649 self.page_size = page_size;
650 self
651 }
652}
653
654impl Default for MemoryType {
655 fn default() -> Self {
656 Self::new(MemoryArch::I32, 0, None, None)
657 }
658}
659
660#[derive(Copy, Clone, PartialEq, Eq, Hash)]
661#[cfg_attr(feature = "debug", derive(Debug))]
662#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
663pub enum MemoryArch {
664 I32,
665 I64,
666}
667
668#[derive(Clone, PartialEq, Eq)]
669#[cfg_attr(feature = "debug", derive(Debug))]
670#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
671pub struct Import {
672 pub module: Box<str>,
673 pub name: Box<str>,
674 pub kind: ImportKind,
675}
676
677#[derive(Clone, PartialEq, Eq)]
678#[cfg_attr(feature = "debug", derive(Debug))]
679#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
680pub enum ImportKind {
681 Function(TypeAddr),
682 Table(TableType),
683 Memory(MemoryType),
684 Global(GlobalType),
685}
686
687impl From<&ImportKind> for ExternalKind {
688 fn from(kind: &ImportKind) -> Self {
689 match kind {
690 ImportKind::Function(_) => Self::Func,
691 ImportKind::Table(_) => Self::Table,
692 ImportKind::Memory(_) => Self::Memory,
693 ImportKind::Global(_) => Self::Global,
694 }
695 }
696}
697
698#[derive(Clone, PartialEq)]
699#[cfg_attr(feature = "debug", derive(Debug))]
700#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
701pub struct Data {
702 pub data: Box<[u8]>,
703 pub range: Range<usize>,
704 pub kind: DataKind,
705}
706
707#[derive(Clone, PartialEq)]
708#[cfg_attr(feature = "debug", derive(Debug))]
709#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
710pub enum DataKind {
711 Active { mem: MemAddr, offset: Box<[ConstInstruction]> },
712 Passive,
713}
714
715#[derive(Clone, PartialEq)]
716#[cfg_attr(feature = "debug", derive(Debug))]
717#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
718pub struct Element {
719 pub kind: ElementKind,
720 pub items: Box<[ElementItem]>,
721 pub range: Range<usize>,
722 pub ty: WasmType,
723}
724
725#[derive(Clone, PartialEq)]
726#[cfg_attr(feature = "debug", derive(Debug))]
727#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
728pub enum ElementKind {
729 Passive,
730 Active { table: TableAddr, offset: Box<[ConstInstruction]> },
731 Declared,
732}
733
734#[derive(Clone, PartialEq)]
735#[cfg_attr(feature = "debug", derive(Debug))]
736#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
737pub enum ElementItem {
738 Func(FuncAddr),
739 Expr(Box<[ConstInstruction]>),
740}