1use std::{ffi::CString, marker::PhantomData};
10use wamr_sys::{
11 wasm_exec_env_t, wasm_func_get_param_count, wasm_func_get_result_count,
12 wasm_func_get_result_types, wasm_function_inst_t, wasm_runtime_call_wasm,
13 wasm_runtime_get_exception, wasm_runtime_get_exec_env_singleton,
14 wasm_runtime_get_wasi_exit_code, wasm_runtime_lookup_function,
15 wasm_valkind_enum_WASM_EXTERNREF, wasm_valkind_enum_WASM_F32, wasm_valkind_enum_WASM_F64,
16 wasm_valkind_enum_WASM_FUNCREF, wasm_valkind_enum_WASM_I32, wasm_valkind_enum_WASM_I64,
17 wasm_valkind_enum_WASM_V128,
18};
19
20use crate::{
21 helper::exception_to_string, instance::Instance, value::WasmValue, ExecError, RuntimeError,
22};
23
24pub struct Function<'instance> {
25 function: wasm_function_inst_t,
26 _phantom: PhantomData<Instance<'instance>>,
27}
28
29impl<'instance> Function<'instance> {
30 pub fn find_export_func(
36 instance: &'instance Instance<'instance>,
37 name: &str,
38 ) -> Result<Self, RuntimeError> {
39 let name = CString::new(name).expect("CString::new failed");
40 let function =
41 unsafe { wasm_runtime_lookup_function(instance.get_inner_instance(), name.as_ptr()) };
42 match function.is_null() {
43 true => Err(RuntimeError::FunctionNotFound),
44 false => Ok(Function {
45 function,
46 _phantom: PhantomData,
47 }),
48 }
49 }
50
51 #[allow(non_upper_case_globals)]
52 #[allow(non_snake_case)]
53 fn parse_result(
54 &self,
55 instance: &Instance<'instance>,
56 result: Vec<u32>,
57 ) -> Result<Vec<WasmValue>, RuntimeError> {
58 let result_count =
59 unsafe { wasm_func_get_result_count(self.function, instance.get_inner_instance()) };
60 if result_count == 0 {
61 return Ok(vec![WasmValue::Void]);
62 }
63
64 let mut result_types = vec![0u8; result_count as usize];
65 unsafe {
66 wasm_func_get_result_types(
67 self.function,
68 instance.get_inner_instance(),
69 result_types.as_mut_ptr(),
70 );
71 }
72
73 let mut results = Vec::with_capacity(result_types.len());
74 let mut index: usize = 0;
75
76 for result_type in result_types.iter() {
77 match *result_type as u32 {
78 wasm_valkind_enum_WASM_I32
79 | wasm_valkind_enum_WASM_FUNCREF
80 | wasm_valkind_enum_WASM_EXTERNREF => {
81 results.push(WasmValue::decode_to_i32(&result[index..index + 1]));
82 index += 1;
83 }
84 wasm_valkind_enum_WASM_I64 => {
85 results.push(WasmValue::decode_to_i64(&result[index..index + 2]));
86 index += 2;
87 }
88 wasm_valkind_enum_WASM_F32 => {
89 results.push(WasmValue::decode_to_f32(&result[index..index + 1]));
90 index += 1;
91 }
92 wasm_valkind_enum_WASM_F64 => {
93 results.push(WasmValue::decode_to_f64(&result[index..index + 2]));
94 index += 2;
95 }
96 wasm_valkind_enum_WASM_V128 => {
97 results.push(WasmValue::decode_to_v128(&result[index..index + 4]));
98 index += 4;
99 }
100 _ => return Err(RuntimeError::NotImplemented),
101 }
102 }
103
104 Ok(results)
105 }
106
107 #[allow(non_upper_case_globals)]
114 pub fn call(
115 &self,
116 instance: &'instance Instance<'instance>,
117 params: &Vec<WasmValue>,
118 ) -> Result<Vec<WasmValue>, RuntimeError> {
119 let param_count =
120 unsafe { wasm_func_get_param_count(self.function, instance.get_inner_instance()) };
121 if param_count > params.len() as u32 {
122 return Err(RuntimeError::ExecutionError(ExecError {
123 message: "invalid parameters".to_string(),
124 exit_code: 0xff,
125 }));
126 }
127
128 let result_count =
130 unsafe { wasm_func_get_result_count(self.function, instance.get_inner_instance()) };
131 let capacity = std::cmp::max(param_count, result_count) as usize * 4;
132
133 let mut argv = Vec::with_capacity(capacity);
135 for p in params {
136 argv.append(&mut p.encode());
137 }
138 argv.resize(capacity, 0);
139
140 let call_result: bool;
141 unsafe {
142 let exec_env: wasm_exec_env_t =
143 wasm_runtime_get_exec_env_singleton(instance.get_inner_instance());
144 call_result =
145 wasm_runtime_call_wasm(exec_env, self.function, param_count, argv.as_mut_ptr());
146 };
147
148 if !call_result {
149 unsafe {
150 let exception_c = wasm_runtime_get_exception(instance.get_inner_instance());
151 let error_info = ExecError {
152 message: exception_to_string(exception_c),
153 exit_code: wasm_runtime_get_wasi_exit_code(instance.get_inner_instance()),
154 };
155 return Err(RuntimeError::ExecutionError(error_info));
156 }
157 }
158
159 self.parse_result(instance, argv)
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::{module::Module, runtime::Runtime, wasi_context::WasiCtxBuilder};
168 use std::{
169 process::{Command, Stdio}, path::Path, path::PathBuf, env, fs,
170 };
171
172 #[test]
173 fn test_func_in_wasm32_unknown() {
174 let runtime = Runtime::new().unwrap();
175
176 let binary = vec![
193 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0E, 0x02, 0x60, 0x02, 0x7E,
194 0x7F, 0x02, 0x7F, 0x7E, 0x60, 0x00, 0x03, 0x7F, 0x7E, 0x7F, 0x03, 0x03, 0x02, 0x00,
195 0x01, 0x07, 0x16, 0x02, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0C, 0x6D, 0x75, 0x6C,
196 0x74, 0x69, 0x2D, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x00, 0x01, 0x0A, 0x18, 0x02,
197 0x0D, 0x00, 0x20, 0x01, 0x41, 0x20, 0x6A, 0x20, 0x00, 0x42, 0xC0, 0x00, 0x7C, 0x0B,
198 0x08, 0x00, 0x41, 0x01, 0x42, 0x02, 0x41, 0x03, 0x0B,
199 ];
200 let binary = binary.into_iter().map(|c| c as u8).collect::<Vec<u8>>();
201
202 let module = Module::from_vec(&runtime, binary, "");
203 assert!(module.is_ok());
204 let module = module.unwrap();
205
206 let instance = Instance::new(&runtime, &module, 1024);
207 assert!(instance.is_ok());
208 let instance: &Instance = &instance.unwrap();
209
210 let function = Function::find_export_func(instance, "add");
215 assert!(function.is_ok());
216 let function = function.unwrap();
217
218 let params: Vec<WasmValue> = vec![WasmValue::I64(10), WasmValue::I32(20)];
219 let call_result = function.call(instance, ¶ms);
220 assert!(call_result.is_ok());
221 assert_eq!(
222 call_result.unwrap(),
223 vec![WasmValue::I32(52), WasmValue::I64(74)]
224 );
225
226 let function = Function::find_export_func(instance, "multi-result");
231 assert!(function.is_ok());
232 let function = function.unwrap();
233
234 let params: Vec<WasmValue> = Vec::new();
235 let call_result = function.call(instance, ¶ms);
236 assert!(call_result.is_ok());
237 assert_eq!(
238 call_result.unwrap(),
239 vec![WasmValue::I32(1), WasmValue::I64(2), WasmValue::I32(3)]
240 );
241 }
242
243 #[test]
244 fn test_func_in_wasm32_wasi() {
245 let runtime = Runtime::new().unwrap();
246
247 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
248 d.push("resources/test");
249 d.push("gcd_wasm32_wasi.wasm");
250 let module = Module::from_file(&runtime, d.as_path());
251 assert!(module.is_ok());
252 let mut module = module.unwrap();
253
254 let wasi_ctx = WasiCtxBuilder::new()
255 .set_pre_open_path(vec!["."], vec![])
256 .build();
257 module.set_wasi_context(wasi_ctx);
258
259 let instance = Instance::new(&runtime, &module, 1024 * 64);
260 assert!(instance.is_ok());
261 let instance: &Instance = &instance.unwrap();
262
263 let function = Function::find_export_func(instance, "gcd");
264 assert!(function.is_ok());
265 let function = function.unwrap();
266
267 let params: Vec<WasmValue> = vec![WasmValue::I32(9), WasmValue::I32(27)];
268 let result = function.call(instance, ¶ms);
269 assert_eq!(result.unwrap(), vec![WasmValue::I32(9)]);
270
271 let params: Vec<WasmValue> = vec![WasmValue::I32(0), WasmValue::I32(27)];
272 let result = function.call(instance, ¶ms);
273 assert_eq!(result.unwrap(), vec![WasmValue::I32(27)]);
274 }
275
276 #[test]
277 fn test_func_in_wasm32_wasi_w_args() {
278 let runtime = Runtime::new().unwrap();
279
280 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
281 d.push("resources/test");
282 d.push("wasi-demo-app.wasm");
283 let module = Module::from_file(&runtime, d.as_path());
284 assert!(module.is_ok());
285 let mut module = module.unwrap();
286
287 let wasi_ctx = WasiCtxBuilder::new()
288 .set_pre_open_path(vec!["."], vec![])
289 .set_arguments(vec!["wasi-demo-app.wasm", "echo", "hi"])
290 .build();
291 module.set_wasi_context(wasi_ctx);
292
293 let instance = Instance::new(&runtime, &module, 1024 * 64);
294 assert!(instance.is_ok());
295 let instance: &Instance = &instance.unwrap();
296
297 let function = Function::find_export_func(instance, "_start");
298 assert!(function.is_ok());
299 let function = function.unwrap();
300
301 let result = function.call(instance, &vec![]);
302 assert!(result.is_ok());
303 println!("{:?}", result.unwrap());
304 }
305
306 #[test]
307 fn test_func_in_multi_v128_return() {
308 let runtime = Runtime::new().unwrap();
309
310 let mut wasm_src = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
326 wasm_src.push("resources/test");
327 wasm_src.push("multiret.wasm");
328
329 let mut aot_dest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
331 aot_dest.push("resources/test");
332 aot_dest.push("multiret.aot");
333
334 let base = match Path::new("target/release").exists() {
336 true => "target/release/build",
337 false => "target/debug/build",
338 };
339 let base_entries = fs::read_dir(base);
340 assert!(base_entries.is_ok());
341 let found = base_entries.unwrap()
342 .filter_map(|entry| entry.ok())
343 .map(|entry| {
344 let path = entry.path();
345 let name = path
346 .file_name()
347 .and_then(|s| s.to_str())
348 .unwrap_or("")
349 .to_string();
350 (path, name)
351 })
352 .filter_map(|(path, name)| {
353 if name.starts_with("wamr-sys") && path.join("out").join("wamrcbuild").join("bin").join("wamrc").exists() {
354 Some(path.join("out").join("wamrcbuild").join("bin").join("wamrc"))
355 } else {
356 None
357 }
358 })
359 .next();
360 assert!(found.is_some());
361 let wamrc_path = found.unwrap();
362
363 let wamrc_output = Command::new(wamrc_path)
364 .arg("--bounds-checks=1")
365 .arg("-o")
366 .arg(aot_dest.clone())
367 .arg(wasm_src.clone())
368 .stderr(Stdio::piped())
369 .stdout(Stdio::piped())
370 .output()
371 .unwrap();
372 assert!(String::from_utf8_lossy(&wamrc_output.stdout).contains("Compile success"));
373
374 let module = Module::from_file(&runtime, aot_dest.as_path());
375 assert!(module.is_ok());
376 let module = module.unwrap();
377
378 let instance = Instance::new(&runtime, &module, 1024 * 64);
379 assert!(instance.is_ok());
380 let instance: &Instance = &instance.unwrap();
381
382 let function = Function::find_export_func(instance, "multi");
383 assert!(function.is_ok());
384 let function = function.unwrap();
385
386 let wrapped_result = function.call(instance, &vec![]);
387 let unwrapped_result = wrapped_result.unwrap();
388
389 assert_eq!(unwrapped_result.len(), 12);
390 assert_eq!(
391 unwrapped_result,
392 vec![
393 WasmValue::F64(22.2222),
394 WasmValue::F32(1.57),
395 WasmValue::I32(42),
396 WasmValue::I64(3523),
397 WasmValue::F64(22.2222),
398 WasmValue::F32(1.57),
399 WasmValue::I32(42),
400 WasmValue::I64(3523),
401 WasmValue::V128(316912650112397582603894390785),
402 WasmValue::V128(85735205748011485687928662073142149120),
403 WasmValue::V128(36893488147419103233),
404 WasmValue::V128(85070591730234615870450834276742070272)
405 ]
406 );
407 }
408}