1use alloc::borrow::ToOwned;
2use core::convert::TryFrom;
3use core::fmt;
4
5mod convert;
6mod wasi;
7
8use self::convert::WasmPrimitive;
9
10macro_rules! option_helper {
11 (Some $rt:expr) => {
12 Some($rt)
13 };
14 (Some) => {
15 None
16 };
17}
18
19#[derive(Debug)]
20pub struct Host {
21 pub module: wasmi::ModuleRef,
22 pub memory: wasmi::MemoryRef,
32}
33
34impl Host {
35 pub fn new(instance: &wasmi::ModuleRef) -> Result<Self, wasmi::Error> {
38 let memory = match instance.export_by_name("memory") {
39 Some(wasmi::ExternVal::Memory(memory)) => memory,
40 _ => {
41 return Err(wasmi::Error::Instantiation(
42 "required memory export".to_owned(),
43 ))
44 }
45 };
46
47 Ok(Host {
48 module: instance.clone(),
49 memory,
50 })
51 }
52}
53
54macro_rules! host_funcs {
55 ($(
56 fn $module:literal :: $name:literal ($($p:ident : $t:ident),*) $( -> $rt:ident)?
57 as $variant:ident impl $method:path;
58 )*) => {
59 #[repr(usize)]
60 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
61 enum HostFunc {
62 $($variant),*
63 }
64
65 impl HostFunc {
66 fn resolve_func(
67 module_name: &str,
68 field_name: &str,
69 _signature: &wasmi::Signature,
70 ) -> Result<HostFunc, wasmi::Error> {
71 match (module_name, field_name) {
72 $(($module, $name) => Ok(HostFunc::$variant),)*
73 _ => Err(wasmi::Error::Instantiation("unresolved func import".to_owned()))
74 }
75 }
76
77 fn signature(self) -> wasmi::Signature {
78 match self {
79 $(
80 HostFunc::$variant => wasmi::Signature::new(
81 &[$(<$t as WasmPrimitive>::TYPE),*][..],
82 option_helper!(Some $(<$rt as WasmPrimitive>::TYPE)?),
83 )
84 ),*
85 }
86 }
87
88 fn func_ref(self) -> wasmi::FuncRef {
89 wasmi::FuncInstance::alloc_host(self.signature(), self as usize)
90 }
91
92 fn module_name(self) -> &'static str {
93 match self {
94 $(HostFunc::$variant => $module),*
95 }
96 }
97
98 fn field_name(self) -> &'static str {
99 match self {
100 $(HostFunc::$variant => $name),*
101 }
102 }
103 }
104
105 impl TryFrom<usize> for HostFunc {
106 type Error = wasmi::Trap;
107 fn try_from(x: usize) -> Result<Self, Self::Error> {
108 $(
109 if x == (HostFunc::$variant as usize) {
110 return Ok(HostFunc::$variant);
111 }
112 )*
113 Err(wasmi::TrapKind::UnexpectedSignature.into())
114 }
115 }
116
117 impl fmt::Display for HostFunc {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 write!(f, "{}::{}", self.module_name(), self.field_name())
120 }
121 }
122
123 impl wasmi::Externals for Host {
124 fn invoke_index(
125 &mut self,
126 index: usize,
127 args: wasmi::RuntimeArgs,
128 ) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
129 let span = tracing::trace_span!("invoke_index", index, ?args);
130 let _enter = span.enter();
131
132 match HostFunc::try_from(index)? {
133 $(
134 HostFunc::$variant => match args.as_ref() {
135 [$($p),*] => {
136 let _result = $method(
137 self,
138 $(<$t as WasmPrimitive>::from_wasm_value(*$p)?),*
139 )?;
140 Ok(option_helper!(
141 Some $(<$rt as WasmPrimitive>::into_wasm_value(_result))?
142 ))
143 }
144 _ => Err(wasmi::TrapKind::UnexpectedSignature.into()),
145 }
146 ),*
147 }
148 }
149 }
150 }
151}
152
153host_funcs! {
154 fn "wasi_unstable"::"fd_write"(fd: u32, iovs: u32, iovs_len: u32, nwritten: u32) -> u16
155 as FdWrite impl wasi::fd_write;
156}
157
158struct HostResolver;
159impl wasmi::ImportResolver for HostResolver {
160 fn resolve_func(
161 &self,
162 module_name: &str,
163 field_name: &str,
164 signature: &wasmi::Signature,
165 ) -> Result<wasmi::FuncRef, wasmi::Error> {
166 let host_fn = HostFunc::resolve_func(module_name, field_name, signature)?;
167 Ok(host_fn.func_ref())
168 }
169
170 fn resolve_global(
171 &self,
172 module_name: &str,
173 field_name: &str,
174 descriptor: &wasmi::GlobalDescriptor,
175 ) -> Result<wasmi::GlobalRef, wasmi::Error> {
176 tracing::error!(
177 module_name,
178 field_name,
179 ?descriptor,
180 "unresolved global import"
181 );
182 Err(wasmi::Error::Instantiation(
183 "unresolved global import".to_owned(),
184 ))
185 }
186
187 fn resolve_memory(
188 &self,
189 module_name: &str,
190 field_name: &str,
191 descriptor: &wasmi::MemoryDescriptor,
192 ) -> Result<wasmi::MemoryRef, wasmi::Error> {
193 tracing::error!(
194 module_name,
195 field_name,
196 ?descriptor,
197 "unresolved memory import"
198 );
199 Err(wasmi::Error::Instantiation(
200 "unresolved memory import".to_owned(),
201 ))
202 }
203
204 fn resolve_table(
205 &self,
206 module_name: &str,
207 field_name: &str,
208 descriptor: &wasmi::TableDescriptor,
209 ) -> Result<wasmi::TableRef, wasmi::Error> {
210 tracing::error!(
211 module_name,
212 field_name,
213 ?descriptor,
214 "unresolved table import"
215 );
216 Err(wasmi::Error::Instantiation(
217 "unresolved table import".to_owned(),
218 ))
219 }
220}
221
222pub fn run_wasm(binary: &[u8]) -> Result<(), wasmi::Error> {
223 let module = wasmi::Module::from_buffer(binary)?;
224 let instance = wasmi::ModuleInstance::new(&module, &HostResolver)?;
226 let mut host = Host::new(instance.not_started_instance())?;
227 let instance = instance.run_start(&mut host)?;
228
229 instance.invoke_export("_start", &[], &mut host)?;
231 Ok(())
232}