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 exports: Arc<[Export]>,
108
109 pub globals: Box<[Global]>,
113
114 pub table_types: Box<[TableType]>,
118
119 pub memory_types: Box<[MemoryType]>,
123
124 pub imports: Box<[Import]>,
128
129 pub data: Box<[Data]>,
133
134 pub elements: Box<[Element]>,
138
139 pub local_memory_allocation: LocalMemoryAllocation,
141}
142
143impl Module {
144 pub fn imports(&self) -> impl Iterator<Item = ModuleImport<'_>> {
148 self.0.imports.iter().filter_map(|import| {
149 let ty = match &import.kind {
150 ImportKind::Function(type_idx) => Some(ImportType::Func(self.0.func_types.get(*type_idx as usize)?)),
151 ImportKind::Table(table_ty) => Some(ImportType::Table(table_ty)),
152 ImportKind::Memory(memory_ty) => Some(ImportType::Memory(memory_ty)),
153 ImportKind::Global(global_ty) => Some(ImportType::Global(global_ty)),
154 }?;
155
156 Some(ModuleImport { module: import.module.as_ref(), name: import.name.as_ref(), ty })
157 })
158 }
159
160 pub fn exports(&self) -> impl Iterator<Item = ModuleExport<'_>> {
164 fn imported_func_type(module: &ModuleInner, function_index: usize) -> Option<&FuncType> {
165 let mut seen = 0usize;
166 for import in module.imports.iter() {
167 if let ImportKind::Function(type_idx) = import.kind {
168 if seen == function_index {
169 return module.func_types.get(type_idx as usize).map(|ty| &**ty);
170 }
171 seen += 1;
172 }
173 }
174 None
175 }
176
177 fn imported_global_type(module: &Module, global_index: usize) -> Option<&GlobalType> {
178 let mut seen = 0usize;
179 for import in module.imports.iter() {
180 if let ImportKind::Global(global_ty) = &import.kind {
181 if seen == global_index {
182 return Some(global_ty);
183 }
184 seen += 1;
185 }
186 }
187 None
188 }
189
190 self.0.exports.iter().filter_map(move |export| {
191 let imports = self.0.imports.iter();
192 let idx = export.index as usize;
193 let ty = match export.kind {
194 ExternalKind::Func => {
195 let imported_funcs =
196 imports.filter(|import| matches!(import.kind, ImportKind::Function(_))).count();
197 if idx < imported_funcs {
198 ExportType::Func(imported_func_type(&self.0, idx)?)
199 } else {
200 let local_idx = idx - imported_funcs;
201 ExportType::Func(&self.0.funcs.get(local_idx)?.ty)
202 }
203 }
204 ExternalKind::Table => ExportType::Table(self.0.table_types.get(idx)?),
205 ExternalKind::Memory => ExportType::Memory(self.0.memory_types.get(idx)?),
206 ExternalKind::Global => {
207 let imported_globals =
208 imports.filter(|import| matches!(import.kind, ImportKind::Global(_))).count();
209 if idx < imported_globals {
210 ExportType::Global(imported_global_type(self, idx)?)
211 } else {
212 let local_idx = idx - imported_globals;
213 ExportType::Global(&self.0.globals.get(local_idx)?.ty)
214 }
215 }
216 };
217
218 Some(ModuleExport { name: export.name.as_ref(), ty })
219 })
220 }
221}
222
223pub struct ModuleExport<'a> {
225 pub name: &'a str,
227 pub ty: ExportType<'a>,
229}
230
231pub struct ModuleImport<'a> {
233 pub module: &'a str,
235 pub name: &'a str,
237 pub ty: ImportType<'a>,
239}
240
241pub enum ImportType<'a> {
243 Func(&'a FuncType),
245 Table(&'a TableType),
247 Memory(&'a MemoryType),
249 Global(&'a GlobalType),
251}
252
253pub enum ExportType<'a> {
255 Func(&'a FuncType),
257 Table(&'a TableType),
259 Memory(&'a MemoryType),
261 Global(&'a GlobalType),
263}
264
265#[derive(Clone, Copy, PartialEq, Eq, Default)]
267#[cfg_attr(feature = "debug", derive(Debug))]
268#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
269pub enum LocalMemoryAllocation {
270 #[default]
272 Skip,
273 Lazy,
275 Eager,
277}
278
279#[derive(Clone, Copy, PartialEq, Eq)]
283#[cfg_attr(feature = "debug", derive(Debug))]
284#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
285pub enum ExternalKind {
286 Func,
288 Table,
290 Memory,
292 Global,
294}
295
296pub type Addr = u32;
302
303pub type FuncAddr = Addr;
305pub type TableAddr = Addr;
306pub type MemAddr = Addr;
307pub type GlobalAddr = Addr;
308pub type ElemAddr = Addr;
309pub type DataAddr = Addr;
310pub type ExternAddr = Addr;
311pub type ConstIdx = Addr;
312
313pub type TypeAddr = Addr;
315pub type LocalAddr = u16; pub type ModuleInstanceAddr = Addr;
317
318#[derive(Clone)]
322#[cfg_attr(feature = "debug", derive(Debug))]
323pub enum ExternVal {
324 Func(FuncAddr),
325 Table(TableAddr),
326 Memory(MemAddr),
327 Global(GlobalAddr),
328}
329
330impl ExternVal {
331 #[inline]
332 pub const fn kind(&self) -> ExternalKind {
333 match self {
334 Self::Func(_) => ExternalKind::Func,
335 Self::Table(_) => ExternalKind::Table,
336 Self::Memory(_) => ExternalKind::Memory,
337 Self::Global(_) => ExternalKind::Global,
338 }
339 }
340
341 #[inline]
342 pub const fn new(kind: ExternalKind, addr: Addr) -> Self {
343 match kind {
344 ExternalKind::Func => Self::Func(addr),
345 ExternalKind::Table => Self::Table(addr),
346 ExternalKind::Memory => Self::Memory(addr),
347 ExternalKind::Global => Self::Global(addr),
348 }
349 }
350}
351
352#[derive(Clone, PartialEq, Eq, Default)]
356#[cfg_attr(feature = "debug", derive(Debug))]
357#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
358pub struct FuncType {
359 data: Box<[WasmType]>,
360 param_count: u16,
361}
362
363impl FuncType {
364 pub fn new(params: &[WasmType], results: &[WasmType]) -> Self {
366 let param_count = params.len() as u16;
367 let data: Box<[WasmType]> = params.iter().cloned().chain(results.iter().cloned()).collect();
368 Self { data, param_count }
369 }
370
371 pub fn params(&self) -> &[WasmType] {
373 &self.data[..self.param_count as usize]
374 }
375
376 pub fn results(&self) -> &[WasmType] {
378 &self.data[self.param_count as usize..]
379 }
380}
381
382#[derive(Default, Clone, Copy, PartialEq, Eq)]
383#[cfg_attr(feature = "debug", derive(Debug))]
384#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
385pub struct ValueCounts {
386 pub c32: u16,
387 pub c64: u16,
388 pub c128: u16,
389}
390
391impl ValueCounts {
392 #[inline]
393 pub fn is_empty(&self) -> bool {
394 self.c32 == 0 && self.c64 == 0 && self.c128 == 0
395 }
396}
397
398impl<'a> FromIterator<&'a WasmType> for ValueCounts {
399 #[inline]
400 fn from_iter<I: IntoIterator<Item = &'a WasmType>>(iter: I) -> Self {
401 let mut counts = Self::default();
402
403 for ty in iter {
404 match ty {
405 WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
406 WasmType::I64 | WasmType::F64 => counts.c64 += 1,
407 WasmType::V128 => counts.c128 += 1,
408 }
409 }
410 counts
411 }
412}
413
414#[derive(Clone, PartialEq, Default)]
415#[cfg_attr(feature = "debug", derive(Debug))]
416#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
417pub struct WasmFunction {
418 pub instructions: Box<[Instruction]>,
419 pub data: WasmFunctionData,
420 pub locals: ValueCounts,
421 pub params: ValueCounts,
422 pub results: ValueCounts,
423 pub ty: Arc<FuncType>,
424}
425
426#[derive(Clone, PartialEq, Eq, Default)]
427#[cfg_attr(feature = "debug", derive(Debug))]
428#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
429pub struct WasmFunctionData {
430 pub v128_constants: Box<[[u8; 16]]>,
431 pub branch_table_targets: Box<[u32]>,
432}
433
434impl WasmFunctionData {
435 #[inline(always)]
437 pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] {
438 let Some(val) = self.v128_constants.get(idx as usize) else {
439 cold_path();
440 unreachable!("invalid v128 constant index");
441 };
442 *val
443 }
444}
445
446#[derive(Clone, PartialEq, Eq)]
448#[cfg_attr(feature = "debug", derive(Debug))]
449#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
450pub struct Export {
451 pub name: Box<str>,
453 pub kind: ExternalKind,
455 pub index: u32,
457}
458
459#[derive(Clone, PartialEq)]
460#[cfg_attr(feature = "debug", derive(Debug))]
461#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
462pub struct Global {
463 pub ty: GlobalType,
464 pub init: Box<[ConstInstruction]>,
465}
466
467#[derive(Clone, Copy, PartialEq, Eq)]
468#[cfg_attr(feature = "debug", derive(Debug))]
469#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
470pub struct GlobalType {
471 pub mutable: bool,
472 pub ty: WasmType,
473}
474
475impl GlobalType {
476 pub const fn new(ty: WasmType, mutable: bool) -> Self {
478 Self { mutable, ty }
479 }
480
481 pub const fn with_ty(mut self, ty: WasmType) -> Self {
483 self.ty = ty;
484 self
485 }
486
487 pub const fn with_mutable(mut self, mutable: bool) -> Self {
489 self.mutable = mutable;
490 self
491 }
492}
493
494impl Default for GlobalType {
495 fn default() -> Self {
496 Self::new(WasmType::I32, false)
497 }
498}
499
500#[derive(Clone, PartialEq, Eq)]
501#[cfg_attr(feature = "debug", derive(Debug))]
502#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
503pub struct TableType {
504 pub element_type: WasmType,
505 pub size_initial: u32,
506 pub size_max: Option<u32>,
507}
508
509impl TableType {
510 pub fn empty() -> Self {
511 Self { element_type: WasmType::RefFunc, size_initial: 0, size_max: None }
512 }
513
514 pub fn new(element_type: WasmType, size_initial: u32, size_max: Option<u32>) -> Self {
515 Self { element_type, size_initial, size_max }
516 }
517}
518
519#[derive(Copy, Clone, PartialEq, Eq)]
521#[cfg_attr(feature = "debug", derive(Debug))]
522#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
523pub struct MemoryType {
524 arch: MemoryArch,
525 page_count_initial: u64,
526 page_count_max: Option<u64>,
527 page_size: Option<u64>,
528}
529
530impl MemoryType {
531 pub const fn new(
533 arch: MemoryArch,
534 page_count_initial: u64,
535 page_count_max: Option<u64>,
536 page_size: Option<u64>,
537 ) -> Self {
538 Self { arch, page_count_initial, page_count_max, page_size }
539 }
540
541 #[inline]
542 pub const fn arch(&self) -> MemoryArch {
543 self.arch
544 }
545
546 #[inline]
547 pub const fn page_count_initial(&self) -> u64 {
548 self.page_count_initial
549 }
550
551 #[inline]
552 pub const fn page_count_max(&self) -> u64 {
553 if let Some(page_count_max) = self.page_count_max { page_count_max } else { max_page_count(self.page_size()) }
554 }
555
556 #[inline]
557 pub const fn page_size(&self) -> u64 {
558 if let Some(page_size) = self.page_size { page_size } else { MEM_PAGE_SIZE }
559 }
560
561 #[inline]
562 pub const fn initial_size(&self) -> u64 {
563 self.page_count_initial * self.page_size()
564 }
565
566 #[inline]
567 pub const fn max_size(&self) -> u64 {
568 self.page_count_max() * self.page_size()
569 }
570
571 pub const fn with_arch(mut self, arch: MemoryArch) -> Self {
573 self.arch = arch;
574 self
575 }
576
577 pub const fn with_page_count_initial(mut self, page_count_initial: u64) -> Self {
579 self.page_count_initial = page_count_initial;
580 self
581 }
582
583 pub const fn with_page_count_max(mut self, page_count_max: Option<u64>) -> Self {
585 self.page_count_max = page_count_max;
586 self
587 }
588
589 pub const fn with_page_size(mut self, page_size: Option<u64>) -> Self {
591 self.page_size = page_size;
592 self
593 }
594}
595
596impl Default for MemoryType {
597 fn default() -> Self {
598 Self::new(MemoryArch::I32, 0, None, None)
599 }
600}
601
602#[derive(Copy, Clone, PartialEq, Eq, Hash)]
603#[cfg_attr(feature = "debug", derive(Debug))]
604#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
605pub enum MemoryArch {
606 I32,
607 I64,
608}
609
610#[derive(Clone, PartialEq, Eq)]
611#[cfg_attr(feature = "debug", derive(Debug))]
612#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
613pub struct Import {
614 pub module: Box<str>,
615 pub name: Box<str>,
616 pub kind: ImportKind,
617}
618
619#[derive(Clone, PartialEq, Eq)]
620#[cfg_attr(feature = "debug", derive(Debug))]
621#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
622pub enum ImportKind {
623 Function(TypeAddr),
624 Table(TableType),
625 Memory(MemoryType),
626 Global(GlobalType),
627}
628
629impl From<&ImportKind> for ExternalKind {
630 fn from(kind: &ImportKind) -> Self {
631 match kind {
632 ImportKind::Function(_) => Self::Func,
633 ImportKind::Table(_) => Self::Table,
634 ImportKind::Memory(_) => Self::Memory,
635 ImportKind::Global(_) => Self::Global,
636 }
637 }
638}
639
640#[derive(Clone, PartialEq)]
641#[cfg_attr(feature = "debug", derive(Debug))]
642#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
643pub struct Data {
644 pub data: Box<[u8]>,
645 pub range: Range<usize>,
646 pub kind: DataKind,
647}
648
649#[derive(Clone, PartialEq)]
650#[cfg_attr(feature = "debug", derive(Debug))]
651#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
652pub enum DataKind {
653 Active { mem: MemAddr, offset: Box<[ConstInstruction]> },
654 Passive,
655}
656
657#[derive(Clone, PartialEq)]
658#[cfg_attr(feature = "debug", derive(Debug))]
659#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
660pub struct Element {
661 pub kind: ElementKind,
662 pub items: Box<[ElementItem]>,
663 pub range: Range<usize>,
664 pub ty: WasmType,
665}
666
667#[derive(Clone, PartialEq)]
668#[cfg_attr(feature = "debug", derive(Debug))]
669#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
670pub enum ElementKind {
671 Passive,
672 Active { table: TableAddr, offset: Box<[ConstInstruction]> },
673 Declared,
674}
675
676#[derive(Clone, PartialEq)]
677#[cfg_attr(feature = "debug", derive(Debug))]
678#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
679pub enum ElementItem {
680 Func(FuncAddr),
681 Expr(Box<[ConstInstruction]>),
682}