1use crate::{ImportError, LinkError};
5use more_asserts::assert_ge;
6use unc_vm_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
7use unc_vm_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType};
8
9use unc_vm_vm::{
10 Export, ExportFunctionMetadata, FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle,
11 Resolver, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind,
12 VMGlobalImport, VMImport, VMImportType, VMMemoryImport, VMTableImport,
13};
14
15fn is_compatible_table(ex: &TableType, im: &TableType) -> bool {
16 (ex.ty == unc_vm_types::Type::FuncRef || ex.ty == im.ty)
17 && im.minimum <= ex.minimum
18 && (im.maximum.is_none()
19 || (ex.maximum.is_some() && im.maximum.unwrap() >= ex.maximum.unwrap()))
20}
21
22fn is_compatible_memory(ex: &MemoryType, im: &MemoryType) -> bool {
23 im.minimum <= ex.minimum
24 && (im.maximum.is_none()
25 || (ex.maximum.is_some() && im.maximum.unwrap() >= ex.maximum.unwrap()))
26 && ex.shared == im.shared
27}
28
29pub fn resolve_imports(
34 engine: &crate::universal::UniversalEngine,
35 resolver: &dyn Resolver,
36 import_counts: &ImportCounts,
37 imports: &[VMImport],
38 finished_dynamic_function_trampolines: &BoxedSlice<FunctionIndex, FunctionBodyPtr>,
39) -> Result<Imports, LinkError> {
40 let mut function_imports = PrimaryMap::with_capacity(import_counts.functions as _);
41 let mut host_function_env_initializers =
42 PrimaryMap::with_capacity(import_counts.functions as _);
43 let mut table_imports = PrimaryMap::with_capacity(import_counts.tables as _);
44 let mut memory_imports = PrimaryMap::with_capacity(import_counts.memories as _);
45 let mut global_imports = PrimaryMap::with_capacity(import_counts.globals as _);
46 for VMImport { import_no, module, field, ty } in imports {
47 let resolved = resolver.resolve(*import_no, module, field);
48 let import_extern = || match ty {
49 &VMImportType::Table(t) => ExternType::Table(t),
50 &VMImportType::Memory(t, _) => ExternType::Memory(t),
51 &VMImportType::Global(t) => ExternType::Global(t),
52 &VMImportType::Function { sig, static_trampoline: _ } => ExternType::Function(
53 engine.lookup_signature(sig).expect("VMSharedSignatureIndex is not valid?"),
54 ),
55 };
56 let resolved = match resolved {
57 Some(r) => r,
58 None => {
59 return Err(LinkError::Import(
60 module.to_string(),
61 field.to_string(),
62 ImportError::UnknownImport(import_extern()).into(),
63 ));
64 }
65 };
66 let export_extern = || match resolved {
67 Export::Function(ref f) => ExternType::Function(
68 engine
69 .lookup_signature(f.vm_function.signature)
70 .expect("VMSharedSignatureIndex not registered with engine (wrong engine?)"),
71 ),
72 Export::Table(ref t) => ExternType::Table(*t.ty()),
73 Export::Memory(ref m) => ExternType::Memory(m.ty()),
74 Export::Global(ref g) => {
75 let global = g.from.ty();
76 ExternType::Global(*global)
77 }
78 };
79 match (&resolved, ty) {
80 (Export::Function(ex), VMImportType::Function { sig, static_trampoline })
81 if ex.vm_function.signature == *sig =>
82 {
83 let address = match ex.vm_function.kind {
84 VMFunctionKind::Dynamic => {
85 let index = FunctionIndex::new(function_imports.len());
89 finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _
90
91 }
94 VMFunctionKind::Static => ex.vm_function.address,
95 };
96
97 let env = if let Some(ExportFunctionMetadata { host_env_clone_fn: clone, .. }) =
99 ex.metadata.as_deref()
100 {
101 unsafe {
105 assert!(!ex.vm_function.vmctx.host_env.is_null());
106 (clone)(ex.vm_function.vmctx.host_env)
107 }
108 } else {
109 unsafe { ex.vm_function.vmctx.host_env }
113 };
114
115 let trampoline = if let Some(t) = ex.vm_function.call_trampoline {
116 Some(t)
117 } else if let VMFunctionKind::Static = ex.vm_function.kind {
118 Some(*static_trampoline)
120 } else {
121 None
123 };
124
125 function_imports.push(VMFunctionImport {
126 body: FunctionBodyPtr(address),
127 signature: *sig,
128 environment: VMFunctionEnvironment { host_env: env },
129 trampoline,
130 });
131
132 let initializer = ex.metadata.as_ref().and_then(|m| m.import_init_function_ptr);
133 let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn);
134 let destructor = ex.metadata.as_ref().map(|m| m.host_env_drop_fn);
135 let import_function_env =
136 if let (Some(clone), Some(destructor)) = (clone, destructor) {
137 ImportFunctionEnv::Env { env, clone, initializer, destructor }
138 } else {
139 ImportFunctionEnv::NoEnv
140 };
141
142 host_function_env_initializers.push(import_function_env);
143 }
144 (Export::Table(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => {
145 let import_table_ty = ex.from.ty();
146 if import_table_ty.ty != im.ty {
147 return Err(LinkError::Import(
148 module.to_string(),
149 field.to_string(),
150 ImportError::IncompatibleType(import_extern(), export_extern()).into(),
151 ));
152 }
153 table_imports
154 .push(VMTableImport { definition: ex.from.vmtable(), from: ex.from.clone() });
155 }
156 (Export::Memory(ex), VMImportType::Memory(im, import_memory_style))
157 if is_compatible_memory(&ex.ty(), im) =>
158 {
159 let export_memory_style = ex.style();
162 if let (
163 MemoryStyle::Static { bound, .. },
164 MemoryStyle::Static { bound: import_bound, .. },
165 ) = (export_memory_style.clone(), &import_memory_style)
166 {
167 assert_ge!(bound, *import_bound);
168 }
169 assert_ge!(
170 export_memory_style.offset_guard_size(),
171 import_memory_style.offset_guard_size()
172 );
173 memory_imports
174 .push(VMMemoryImport { definition: ex.from.vmmemory(), from: ex.from.clone() });
175 }
176
177 (Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => {
178 global_imports
179 .push(VMGlobalImport { definition: ex.from.vmglobal(), from: ex.from.clone() });
180 }
181 _ => {
182 return Err(LinkError::Import(
183 module.to_string(),
184 field.to_string(),
185 ImportError::IncompatibleType(import_extern(), export_extern()).into(),
186 ));
187 }
188 }
189 }
190 Ok(Imports::new(
191 function_imports,
192 host_function_env_initializers,
193 table_imports,
194 memory_imports,
195 global_imports,
196 ))
197}