1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::rc::Rc;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use core::fmt::Debug;
7
8use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple};
9use crate::{log, LinkingError, MemoryRef, MemoryRefMut, Result};
10use tinywasm_types::*;
11
12#[derive(Debug, Clone)]
14pub enum Function {
15 Host(Rc<HostFunction>),
17
18 Wasm(Rc<WasmFunction>),
20}
21
22impl Function {
23 pub(crate) fn ty(&self) -> &FuncType {
24 match self {
25 Self::Host(f) => &f.ty,
26 Self::Wasm(f) => &f.ty,
27 }
28 }
29}
30
31pub struct HostFunction {
33 pub(crate) ty: tinywasm_types::FuncType,
34 pub(crate) func: HostFuncInner,
35}
36
37impl HostFunction {
38 pub fn ty(&self) -> &tinywasm_types::FuncType {
40 &self.ty
41 }
42
43 pub fn call(&self, ctx: FuncContext<'_>, args: &[WasmValue]) -> Result<Vec<WasmValue>> {
45 (self.func)(ctx, args)
46 }
47}
48
49pub(crate) type HostFuncInner = Box<dyn Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>>>;
50
51#[derive(Debug)]
53pub struct FuncContext<'a> {
54 pub(crate) store: &'a mut crate::Store,
55 pub(crate) module_addr: ModuleInstanceAddr,
56}
57
58impl FuncContext<'_> {
59 pub fn store(&self) -> &crate::Store {
61 self.store
62 }
63
64 pub fn store_mut(&mut self) -> &mut crate::Store {
66 self.store
67 }
68
69 pub fn module(&self) -> crate::ModuleInstance {
71 self.store.get_module_instance_raw(self.module_addr)
72 }
73
74 pub fn exported_memory(&mut self, name: &str) -> Result<MemoryRef<'_>> {
76 self.module().exported_memory(self.store, name)
77 }
78
79 pub fn exported_memory_mut(&mut self, name: &str) -> Result<MemoryRefMut<'_>> {
81 self.module().exported_memory_mut(self.store, name)
82 }
83}
84
85impl Debug for HostFunction {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 f.debug_struct("HostFunction").field("ty", &self.ty).field("func", &"...").finish()
88 }
89}
90
91#[derive(Debug, Clone)]
92#[non_exhaustive]
93pub enum Extern {
95 Global {
97 ty: GlobalType,
99 val: WasmValue,
101 },
102
103 Table {
105 ty: TableType,
107 init: WasmValue,
109 },
110
111 Memory {
113 ty: MemoryType,
115 },
116
117 Function(Function),
119}
120
121impl Extern {
122 pub fn global(val: WasmValue, mutable: bool) -> Self {
124 Self::Global { ty: GlobalType { ty: val.val_type(), mutable }, val }
125 }
126
127 pub fn table(ty: TableType, init: WasmValue) -> Self {
129 Self::Table { ty, init }
130 }
131
132 pub fn memory(ty: MemoryType) -> Self {
134 Self::Memory { ty }
135 }
136
137 pub fn func(
139 ty: &tinywasm_types::FuncType,
140 func: impl Fn(FuncContext<'_>, &[WasmValue]) -> Result<Vec<WasmValue>> + 'static,
141 ) -> Self {
142 Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(func), ty: ty.clone() })))
143 }
144
145 pub fn typed_func<P, R>(func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Self
147 where
148 P: FromWasmValueTuple + ValTypesFromTuple,
149 R: IntoWasmValueTuple + ValTypesFromTuple + Debug,
150 {
151 let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> {
152 let args = P::from_wasm_value_tuple(args)?;
153 let result = func(ctx, args)?;
154 Ok(result.into_wasm_value_tuple().to_vec())
155 };
156
157 let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() };
158 Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty })))
159 }
160
161 pub fn kind(&self) -> ExternalKind {
163 match self {
164 Self::Global { .. } => ExternalKind::Global,
165 Self::Table { .. } => ExternalKind::Table,
166 Self::Memory { .. } => ExternalKind::Memory,
167 Self::Function { .. } => ExternalKind::Func,
168 }
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
173pub struct ExternName {
175 module: String,
176 name: String,
177}
178
179impl From<&Import> for ExternName {
180 fn from(import: &Import) -> Self {
181 Self { module: import.module.to_string(), name: import.name.to_string() }
182 }
183}
184
185#[derive(Debug, Default)]
186#[derive(Clone)]
221pub struct Imports {
222 values: BTreeMap<ExternName, Extern>,
223 modules: BTreeMap<String, ModuleInstanceAddr>,
224}
225
226pub(crate) enum ResolvedExtern<S, V> {
227 Store(S), Extern(V), }
230
231pub(crate) struct ResolvedImports {
232 pub(crate) globals: Vec<GlobalAddr>,
233 pub(crate) tables: Vec<TableAddr>,
234 pub(crate) memories: Vec<MemAddr>,
235 pub(crate) funcs: Vec<FuncAddr>,
236}
237
238impl ResolvedImports {
239 pub(crate) fn new() -> Self {
240 Self { globals: Vec::new(), tables: Vec::new(), memories: Vec::new(), funcs: Vec::new() }
241 }
242}
243
244impl Imports {
245 pub fn new() -> Self {
247 Imports { values: BTreeMap::new(), modules: BTreeMap::new() }
248 }
249
250 pub fn merge(mut self, other: Self) -> Self {
252 self.values.extend(other.values);
253 self.modules.extend(other.modules);
254 self
255 }
256
257 pub fn link_module(&mut self, name: &str, addr: ModuleInstanceAddr) -> Result<&mut Self> {
261 self.modules.insert(name.to_string(), addr);
262 Ok(self)
263 }
264
265 pub fn define(&mut self, module: &str, name: &str, value: Extern) -> Result<&mut Self> {
267 self.values.insert(ExternName { module: module.to_string(), name: name.to_string() }, value);
268 Ok(self)
269 }
270
271 pub(crate) fn take(
272 &mut self,
273 store: &mut crate::Store,
274 import: &Import,
275 ) -> Option<ResolvedExtern<ExternVal, Extern>> {
276 let name = ExternName::from(import);
277 if let Some(v) = self.values.get(&name) {
278 return Some(ResolvedExtern::Extern(v.clone()));
279 }
280 if let Some(addr) = self.modules.get(&name.module) {
281 let instance = store.get_module_instance(*addr)?;
282 return Some(ResolvedExtern::Store(instance.export_addr(&import.name)?));
283 }
284
285 None
286 }
287
288 fn compare_types<T: Debug + PartialEq>(import: &Import, actual: &T, expected: &T) -> Result<()> {
289 if expected != actual {
290 log::error!("failed to link import {}, expected {:?}, got {:?}", import.name, expected, actual);
291 return Err(LinkingError::incompatible_import_type(import).into());
292 }
293 Ok(())
294 }
295
296 fn compare_table_types(import: &Import, expected: &TableType, actual: &TableType) -> Result<()> {
297 Self::compare_types(import, &actual.element_type, &expected.element_type)?;
298
299 if actual.size_initial > expected.size_initial {
300 return Err(LinkingError::incompatible_import_type(import).into());
301 }
302
303 match (expected.size_max, actual.size_max) {
304 (None, Some(_)) => return Err(LinkingError::incompatible_import_type(import).into()),
305 (Some(expected_max), Some(actual_max)) if actual_max < expected_max => {
306 return Err(LinkingError::incompatible_import_type(import).into())
307 }
308 _ => {}
309 }
310
311 Ok(())
312 }
313
314 fn compare_memory_types(
315 import: &Import,
316 expected: &MemoryType,
317 actual: &MemoryType,
318 real_size: Option<usize>,
319 ) -> Result<()> {
320 Self::compare_types(import, &expected.arch, &actual.arch)?;
321
322 if actual.page_count_initial > expected.page_count_initial
323 && real_size.map_or(true, |size| actual.page_count_initial > size as u64)
324 {
325 return Err(LinkingError::incompatible_import_type(import).into());
326 }
327
328 if expected.page_count_max.is_none() && actual.page_count_max.is_some() {
329 return Err(LinkingError::incompatible_import_type(import).into());
330 }
331
332 if let (Some(expected_max), Some(actual_max)) = (expected.page_count_max, actual.page_count_max) {
333 if actual_max < expected_max {
334 return Err(LinkingError::incompatible_import_type(import).into());
335 }
336 }
337
338 Ok(())
339 }
340
341 pub(crate) fn link(
342 mut self,
343 store: &mut crate::Store,
344 module: &crate::Module,
345 idx: ModuleInstanceAddr,
346 ) -> Result<ResolvedImports> {
347 let mut imports = ResolvedImports::new();
348
349 for import in &module.0.imports {
350 let val = self.take(store, import).ok_or_else(|| LinkingError::unknown_import(import))?;
351
352 match val {
353 ResolvedExtern::Extern(ex) => match (ex, &import.kind) {
355 (Extern::Global { ty, val }, ImportKind::Global(import_ty)) => {
356 Self::compare_types(import, &ty, import_ty)?;
357 imports.globals.push(store.add_global(ty, val.into(), idx)?);
358 }
359 (Extern::Table { ty, .. }, ImportKind::Table(import_ty)) => {
360 Self::compare_table_types(import, &ty, import_ty)?;
361 imports.tables.push(store.add_table(ty, idx)?);
362 }
363 (Extern::Memory { ty }, ImportKind::Memory(import_ty)) => {
364 Self::compare_memory_types(import, &ty, import_ty, None)?;
365 imports.memories.push(store.add_mem(ty, idx)?);
366 }
367 (Extern::Function(extern_func), ImportKind::Function(ty)) => {
368 let import_func_type = module
369 .0
370 .func_types
371 .get(*ty as usize)
372 .ok_or_else(|| LinkingError::incompatible_import_type(import))?;
373
374 Self::compare_types(import, extern_func.ty(), import_func_type)?;
375 imports.funcs.push(store.add_func(extern_func, idx)?);
376 }
377 _ => return Err(LinkingError::incompatible_import_type(import).into()),
378 },
379
380 ResolvedExtern::Store(val) => {
382 if val.kind() != (&import.kind).into() {
384 return Err(LinkingError::incompatible_import_type(import).into());
385 }
386
387 match (val, &import.kind) {
388 (ExternVal::Global(global_addr), ImportKind::Global(ty)) => {
389 let global = store.get_global(global_addr);
390 Self::compare_types(import, &global.ty, ty)?;
391 imports.globals.push(global_addr);
392 }
393 (ExternVal::Table(table_addr), ImportKind::Table(ty)) => {
394 let table = store.get_table(table_addr);
395 let mut kind = table.kind.clone();
396 kind.size_initial = table.size() as u32;
397 Self::compare_table_types(import, &kind, ty)?;
398 imports.tables.push(table_addr);
399 }
400 (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => {
401 let mem = store.get_mem(memory_addr);
402 let (size, kind) = { (mem.page_count, mem.kind) };
403 Self::compare_memory_types(import, &kind, ty, Some(size))?;
404 imports.memories.push(memory_addr);
405 }
406 (ExternVal::Func(func_addr), ImportKind::Function(ty)) => {
407 let func = store.get_func(func_addr);
408 let import_func_type = module
409 .0
410 .func_types
411 .get(*ty as usize)
412 .ok_or_else(|| LinkingError::incompatible_import_type(import))?;
413
414 Self::compare_types(import, func.func.ty(), import_func_type)?;
415 imports.funcs.push(func_addr);
416 }
417 _ => return Err(LinkingError::incompatible_import_type(import).into()),
418 }
419 }
420 }
421 }
422
423 Ok(imports)
424 }
425}