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
80 fn deref(&self) -> &ModuleInner {
81 &self.0
82 }
83}
84
85#[doc(hidden)]
86#[derive(Clone, Default, PartialEq)]
87#[cfg_attr(feature = "debug", derive(Debug))]
88#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
89pub struct ModuleInner {
90 pub start_func: Option<FuncAddr>,
94
95 pub funcs: Box<[Arc<WasmFunction>]>,
99
100 pub func_types: Arc<[Arc<FuncType>]>,
104
105 pub func_type_idxs: Arc<[u32]>,
107
108 pub exports: Arc<[Export]>,
112
113 pub globals: Box<[Global]>,
117
118 pub table_types: Box<[TableType]>,
122
123 pub memory_types: Box<[MemoryType]>,
127
128 pub imports: Box<[Import]>,
132
133 pub data: Box<[Data]>,
137
138 pub elements: Box<[Element]>,
142
143 pub local_memory_allocation: LocalMemoryAllocation,
145}
146
147impl Module {
148 pub fn imports(&self) -> impl Iterator<Item = ModuleImport<'_>> {
152 self.0.imports.iter().filter_map(|import| {
153 let ty = match &import.kind {
154 ImportKind::Function(type_idx) => Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)),
155 ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)),
156 ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)),
157 ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)),
158 }?;
159
160 Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty })
161 })
162 }
163
164 pub fn exports(&self) -> impl Iterator<Item = ModuleExport<'_>> {
168 fn imported_count(module: &ModuleInner, kind: ExternalKind) -> usize {
169 module
170 .imports
171 .iter()
172 .filter(|import| {
173 matches!(
174 (kind, &import.kind),
175 (ExternalKind::Func, ImportKind::Function(_))
176 | (ExternalKind::Table, ImportKind::Table(_))
177 | (ExternalKind::Memory, ImportKind::Memory(_))
178 | (ExternalKind::Global, ImportKind::Global(_))
179 )
180 })
181 .count()
182 }
183
184 fn imported_func_type(module: &ModuleInner, function_index: usize) -> Option<&FuncType> {
185 let mut seen = 0usize;
186 for import in module.imports.iter() {
187 if let ImportKind::Function(type_idx) = import.kind {
188 if seen == function_index {
189 return module.func_types.get(type_idx as usize).map(|ty| &**ty);
190 }
191 seen += 1;
192 }
193 }
194 None
195 }
196
197 fn imported_table_type(module: &ModuleInner, table_index: usize) -> Option<&TableType> {
198 let mut seen = 0usize;
199 for import in module.imports.iter() {
200 if let ImportKind::Table(table_ty) = &import.kind {
201 if seen == table_index {
202 return Some(table_ty);
203 }
204 seen += 1;
205 }
206 }
207 None
208 }
209
210 fn imported_memory_type(module: &ModuleInner, memory_index: usize) -> Option<&MemoryType> {
211 let mut seen = 0usize;
212 for import in module.imports.iter() {
213 if let ImportKind::Memory(memory_ty) = &import.kind {
214 if seen == memory_index {
215 return Some(memory_ty);
216 }
217 seen += 1;
218 }
219 }
220 None
221 }
222
223 fn imported_global_type(module: &Module, global_index: usize) -> Option<&GlobalType> {
224 let mut seen = 0usize;
225 for import in module.imports.iter() {
226 if let ImportKind::Global(global_ty) = &import.kind {
227 if seen == global_index {
228 return Some(global_ty);
229 }
230 seen += 1;
231 }
232 }
233 None
234 }
235
236 self.0.exports.iter().filter_map(move |export| {
237 let idx = export.index as usize;
238 let ty = match export.kind {
239 ExternalKind::Func => {
240 let imported_funcs = imported_count(&self.0, ExternalKind::Func);
241 if idx < imported_funcs {
242 ExportType::Func(imported_func_type(&self.0, idx)?)
243 } else {
244 let local_idx = idx - imported_funcs;
245 ExportType::Func(&self.0.funcs.get(local_idx)?.ty)
246 }
247 }
248 ExternalKind::Table => {
249 let imported_tables = imported_count(&self.0, ExternalKind::Table);
250 if idx < imported_tables {
251 ExportType::Table(imported_table_type(&self.0, idx)?)
252 } else {
253 let local_idx = idx - imported_tables;
254 ExportType::Table(self.0.table_types.get(local_idx)?)
255 }
256 }
257 ExternalKind::Memory => {
258 let imported_memories = imported_count(&self.0, ExternalKind::Memory);
259 if idx < imported_memories {
260 ExportType::Memory(imported_memory_type(&self.0, idx)?)
261 } else {
262 let local_idx = idx - imported_memories;
263 ExportType::Memory(self.0.memory_types.get(local_idx)?)
264 }
265 }
266 ExternalKind::Global => {
267 let imported_globals = imported_count(&self.0, ExternalKind::Global);
268 if idx < imported_globals {
269 ExportType::Global(imported_global_type(self, idx)?)
270 } else {
271 let local_idx = idx - imported_globals;
272 ExportType::Global(&self.0.globals.get(local_idx)?.ty)
273 }
274 }
275 };
276
277 Some(ModuleExport { name: export.name.as_ref(), ty })
278 })
279 }
280}
281
282pub struct ModuleExport<'a> {
284 pub name: &'a str,
286 pub ty: ExportType<'a>,
288}
289
290pub struct ModuleImport<'a> {
292 pub module: &'a str,
294 pub name: &'a str,
296 pub ty: ImportType<'a>,
298}
299
300pub enum ImportType<'a> {
302 Func(&'a FuncType),
304 Table(&'a TableType),
306 Memory(&'a MemoryType),
308 Global(&'a GlobalType),
310}
311
312pub enum ExportType<'a> {
314 Func(&'a FuncType),
316 Table(&'a TableType),
318 Memory(&'a MemoryType),
320 Global(&'a GlobalType),
322}
323
324#[derive(Clone, Copy, PartialEq, Eq, Default)]
326#[cfg_attr(feature = "debug", derive(Debug))]
327#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
328pub enum LocalMemoryAllocation {
329 #[default]
331 Skip,
332 Lazy,
334 Eager,
336}
337
338#[derive(Clone, Copy, PartialEq, Eq)]
342#[cfg_attr(feature = "debug", derive(Debug))]
343#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
344pub enum ExternalKind {
345 Func,
347 Table,
349 Memory,
351 Global,
353}
354
355pub type Addr = u32;
361
362pub type FuncAddr = Addr;
364pub type TableAddr = Addr;
365pub type MemAddr = Addr;
366pub type GlobalAddr = Addr;
367pub type ElemAddr = Addr;
368pub type DataAddr = Addr;
369pub type ExternAddr = Addr;
370pub type ConstIdx = Addr;
371
372pub type TypeAddr = Addr;
374pub type LocalAddr = u16; pub type ModuleInstanceAddr = Addr;
376
377#[derive(Clone)]
381#[cfg_attr(feature = "debug", derive(Debug))]
382pub enum ExternVal {
383 Func(FuncAddr),
384 Table(TableAddr),
385 Memory(MemAddr),
386 Global(GlobalAddr),
387}
388
389impl ExternVal {
390 #[inline]
391 pub const fn kind(&self) -> ExternalKind {
392 match self {
393 Self::Func(_) => ExternalKind::Func,
394 Self::Table(_) => ExternalKind::Table,
395 Self::Memory(_) => ExternalKind::Memory,
396 Self::Global(_) => ExternalKind::Global,
397 }
398 }
399
400 #[inline]
401 pub const fn new(kind: ExternalKind, addr: Addr) -> Self {
402 match kind {
403 ExternalKind::Func => Self::Func(addr),
404 ExternalKind::Table => Self::Table(addr),
405 ExternalKind::Memory => Self::Memory(addr),
406 ExternalKind::Global => Self::Global(addr),
407 }
408 }
409}
410
411#[derive(Clone, PartialEq, Eq, Default)]
415#[cfg_attr(feature = "debug", derive(Debug))]
416#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
417pub struct FuncType {
418 data: Box<[WasmType]>,
419 param_count: u16,
420}
421
422impl FuncType {
423 pub fn new(params: &[WasmType], results: &[WasmType]) -> Self {
425 let param_count = params.len() as u16;
426 let data: Box<[WasmType]> = params.iter().cloned().chain(results.iter().cloned()).collect();
427 Self { data, param_count }
428 }
429
430 pub fn params(&self) -> &[WasmType] {
432 &self.data[..self.param_count as usize]
433 }
434
435 pub fn results(&self) -> &[WasmType] {
437 &self.data[self.param_count as usize..]
438 }
439}
440
441#[derive(Default, Clone, Copy, PartialEq, Eq)]
442#[cfg_attr(feature = "debug", derive(Debug))]
443#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
444pub struct ValueCounts {
445 pub c32: u16,
446 pub c64: u16,
447 pub c128: u16,
448}
449
450impl ValueCounts {
451 #[inline]
452 pub fn is_empty(&self) -> bool {
453 self.c32 == 0 && self.c64 == 0 && self.c128 == 0
454 }
455}
456
457impl<'a> FromIterator<&'a WasmType> for ValueCounts {
458 #[inline]
459 fn from_iter<I: IntoIterator<Item = &'a WasmType>>(iter: I) -> Self {
460 let mut counts = Self::default();
461
462 for ty in iter {
463 match ty {
464 WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
465 WasmType::I64 | WasmType::F64 => counts.c64 += 1,
466 WasmType::V128 => counts.c128 += 1,
467 }
468 }
469 counts
470 }
471}
472
473#[derive(Clone, PartialEq, Default)]
474#[cfg_attr(feature = "debug", derive(Debug))]
475#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
476pub struct WasmFunction {
477 pub instructions: Box<[Instruction]>,
478 pub data: WasmFunctionData,
479 pub locals: ValueCounts,
480 pub params: ValueCounts,
481 pub results: ValueCounts,
482 pub ty: Arc<FuncType>,
483}
484
485#[derive(Clone, PartialEq, Eq, Default)]
486#[cfg_attr(feature = "debug", derive(Debug))]
487#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
488pub struct WasmFunctionData {
489 pub v128_constants: Box<[[u8; 16]]>,
490 pub branch_table_targets: Box<[u32]>,
491}
492
493impl WasmFunctionData {
494 #[inline(always)]
496 pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] {
497 let Some(val) = self.v128_constants.get(idx as usize) else {
498 cold_path();
499 unreachable!("invalid v128 constant index");
500 };
501 *val
502 }
503}
504
505#[derive(Clone, PartialEq, Eq)]
507#[cfg_attr(feature = "debug", derive(Debug))]
508#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
509pub struct Export {
510 pub name: Box<str>,
512 pub kind: ExternalKind,
514 pub index: u32,
516}
517
518#[derive(Clone, PartialEq)]
519#[cfg_attr(feature = "debug", derive(Debug))]
520#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
521pub struct Global {
522 pub ty: GlobalType,
523 pub init: Box<[ConstInstruction]>,
524}
525
526#[derive(Clone, Copy, PartialEq, Eq)]
527#[cfg_attr(feature = "debug", derive(Debug))]
528#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
529pub struct GlobalType {
530 pub mutable: bool,
531 pub ty: WasmType,
532}
533
534impl GlobalType {
535 pub const fn new(ty: WasmType, mutable: bool) -> Self {
537 Self { mutable, ty }
538 }
539
540 pub const fn with_ty(mut self, ty: WasmType) -> Self {
542 self.ty = ty;
543 self
544 }
545
546 pub const fn with_mutable(mut self, mutable: bool) -> Self {
548 self.mutable = mutable;
549 self
550 }
551}
552
553impl Default for GlobalType {
554 fn default() -> Self {
555 Self::new(WasmType::I32, false)
556 }
557}
558
559#[derive(Clone, PartialEq, Eq)]
560#[cfg_attr(feature = "debug", derive(Debug))]
561#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
562pub struct TableType {
563 pub element_type: WasmType,
564 pub size_initial: u32,
565 pub size_max: Option<u32>,
566}
567
568impl TableType {
569 pub fn empty() -> Self {
570 Self { element_type: WasmType::RefFunc, size_initial: 0, size_max: None }
571 }
572
573 pub fn new(element_type: WasmType, size_initial: u32, size_max: Option<u32>) -> Self {
574 Self { element_type, size_initial, size_max }
575 }
576}
577
578#[derive(Copy, Clone, PartialEq, Eq)]
580#[cfg_attr(feature = "debug", derive(Debug))]
581#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
582pub struct MemoryType {
583 arch: MemoryArch,
584 page_count_initial: u64,
585 page_count_max: Option<u64>,
586 page_size: Option<u64>,
587}
588
589impl MemoryType {
590 pub const fn new(
592 arch: MemoryArch,
593 page_count_initial: u64,
594 page_count_max: Option<u64>,
595 page_size: Option<u64>,
596 ) -> Self {
597 Self { arch, page_count_initial, page_count_max, page_size }
598 }
599
600 #[inline]
601 pub const fn arch(&self) -> MemoryArch {
602 self.arch
603 }
604
605 #[inline]
606 pub const fn page_count_initial(&self) -> u64 {
607 self.page_count_initial
608 }
609
610 #[inline]
611 pub const fn page_count_max(&self) -> u64 {
612 if let Some(page_count_max) = self.page_count_max { page_count_max } else { max_page_count(self.page_size()) }
613 }
614
615 #[inline]
616 pub const fn page_size(&self) -> u64 {
617 if let Some(page_size) = self.page_size { page_size } else { MEM_PAGE_SIZE }
618 }
619
620 #[inline]
621 pub const fn initial_size(&self) -> u64 {
622 self.page_count_initial * self.page_size()
623 }
624
625 #[inline]
626 pub const fn max_size(&self) -> u64 {
627 self.page_count_max() * self.page_size()
628 }
629
630 pub const fn with_arch(mut self, arch: MemoryArch) -> Self {
632 self.arch = arch;
633 self
634 }
635
636 pub const fn with_page_count_initial(mut self, page_count_initial: u64) -> Self {
638 self.page_count_initial = page_count_initial;
639 self
640 }
641
642 pub const fn with_page_count_max(mut self, page_count_max: Option<u64>) -> Self {
644 self.page_count_max = page_count_max;
645 self
646 }
647
648 pub const fn with_page_size(mut self, page_size: Option<u64>) -> Self {
650 self.page_size = page_size;
651 self
652 }
653}
654
655impl Default for MemoryType {
656 fn default() -> Self {
657 Self::new(MemoryArch::I32, 0, None, None)
658 }
659}
660
661#[derive(Copy, Clone, PartialEq, Eq, Hash)]
662#[cfg_attr(feature = "debug", derive(Debug))]
663#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
664pub enum MemoryArch {
665 I32,
666 I64,
667}
668
669#[derive(Clone, PartialEq, Eq)]
670#[cfg_attr(feature = "debug", derive(Debug))]
671#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
672pub struct Import {
673 pub module: Box<str>,
674 pub name: Box<str>,
675 pub kind: ImportKind,
676}
677
678#[derive(Clone, PartialEq, Eq)]
679#[cfg_attr(feature = "debug", derive(Debug))]
680#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
681pub enum ImportKind {
682 Function(TypeAddr),
683 Table(TableType),
684 Memory(MemoryType),
685 Global(GlobalType),
686}
687
688impl From<&ImportKind> for ExternalKind {
689 fn from(kind: &ImportKind) -> Self {
690 match kind {
691 ImportKind::Function(_) => Self::Func,
692 ImportKind::Table(_) => Self::Table,
693 ImportKind::Memory(_) => Self::Memory,
694 ImportKind::Global(_) => Self::Global,
695 }
696 }
697}
698
699#[derive(Clone, PartialEq)]
700#[cfg_attr(feature = "debug", derive(Debug))]
701#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
702pub struct Data {
703 pub data: Box<[u8]>,
704 pub range: Range<usize>,
705 pub kind: DataKind,
706}
707
708#[derive(Clone, PartialEq)]
709#[cfg_attr(feature = "debug", derive(Debug))]
710#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
711pub enum DataKind {
712 Active { mem: MemAddr, offset: Box<[ConstInstruction]> },
713 Passive,
714}
715
716#[derive(Clone, PartialEq)]
717#[cfg_attr(feature = "debug", derive(Debug))]
718#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
719pub struct Element {
720 pub kind: ElementKind,
721 pub items: Box<[ElementItem]>,
722 pub range: Range<usize>,
723 pub ty: WasmType,
724}
725
726#[derive(Clone, PartialEq)]
727#[cfg_attr(feature = "debug", derive(Debug))]
728#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
729pub enum ElementKind {
730 Passive,
731 Active { table: TableAddr, offset: Box<[ConstInstruction]> },
732 Declared,
733}
734
735#[derive(Clone, PartialEq)]
736#[cfg_attr(feature = "debug", derive(Debug))]
737#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
738pub enum ElementItem {
739 Func(FuncAddr),
740 Expr(Box<[ConstInstruction]>),
741}