1use crate::{ffi, utils, utils::check, Config, WasmEdgeResult};
4use std::path::Path;
5use wasmedge_types::error::WasmEdgeError;
6
7#[derive(Debug)]
9pub struct Compiler {
10 pub(crate) inner: InnerCompiler,
11}
12impl Drop for Compiler {
13 fn drop(&mut self) {
14 if !self.inner.0.is_null() {
15 unsafe { ffi::WasmEdge_CompilerDelete(self.inner.0) }
16 }
17 }
18}
19impl Compiler {
20 pub fn create(config: Option<&Config>) -> WasmEdgeResult<Self> {
26 let ctx = match config {
27 Some(config) => unsafe { ffi::WasmEdge_CompilerCreate(config.inner.0) },
28 None => unsafe { ffi::WasmEdge_CompilerCreate(std::ptr::null_mut()) },
29 };
30
31 match ctx.is_null() {
32 true => Err(Box::new(WasmEdgeError::CompilerCreate)),
33 false => Ok(Self {
34 inner: InnerCompiler(ctx),
35 }),
36 }
37 }
38
39 pub fn compile_from_file(
51 &self,
52 wasm_file: impl AsRef<Path>,
53 aot_file: impl AsRef<Path>,
54 ) -> WasmEdgeResult<()> {
55 match wasm_file.as_ref().extension() {
56 Some(extension) => match extension.to_str() {
57 Some("wasm") => self.compile_from_wasm_file(wasm_file, aot_file),
58 Some("wat") => {
59 let bytes = wat::parse_file(wasm_file.as_ref())
60 .map_err(|_| WasmEdgeError::Operation("Failed to parse wat file".into()))?;
61 self.compile_from_bytes(bytes, aot_file)
62 }
63 _ => Err(Box::new(WasmEdgeError::Operation(
64 "The wasm file's extension should be `wasm` or `wat`".into(),
65 ))),
66 },
67 None => Err(Box::new(WasmEdgeError::Operation(
68 "The wasm file's extension should be `wasm` or `wat`".into(),
69 ))),
70 }
71 }
72
73 fn compile_from_wasm_file(
74 &self,
75 wasm_file: impl AsRef<Path>,
76 aot_file: impl AsRef<Path>,
77 ) -> WasmEdgeResult<()> {
78 let in_path = utils::path_to_cstring(wasm_file.as_ref())?;
79 let out_path = utils::path_to_cstring(aot_file.as_ref())?;
80 unsafe {
81 check(ffi::WasmEdge_CompilerCompile(
82 self.inner.0,
83 in_path.as_ptr(),
84 out_path.as_ptr(),
85 ))
86 }
87 }
88
89 pub fn compile_from_bytes(
101 &self,
102 wasm_bytes: impl AsRef<[u8]>,
103 aot_file: impl AsRef<Path>,
104 ) -> WasmEdgeResult<()> {
105 let wasm_bytes = wasm_bytes.as_ref();
106 let out_path = utils::path_to_cstring(aot_file.as_ref())?;
107 unsafe {
108 check(ffi::WasmEdge_CompilerCompileFromBuffer(
109 self.inner.0,
110 wasm_bytes.as_ptr(),
111 wasm_bytes.len() as u64,
112 out_path.as_ptr(),
113 ))
114 }
115 }
116
117 #[cfg(feature = "ffi")]
119 #[cfg_attr(docsrs, doc(cfg(feature = "ffi")))]
120 pub fn as_ptr(&self) -> *const ffi::WasmEdge_CompilerContext {
121 self.inner.0 as *const _
122 }
123}
124
125#[derive(Debug)]
126pub(crate) struct InnerCompiler(pub(crate) *mut ffi::WasmEdge_CompilerContext);
127unsafe impl Send for InnerCompiler {}
128unsafe impl Sync for InnerCompiler {}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::{
134 AsInstance, CallingFrame, Compiler, Config, Executor, FuncType, Function, ImportModule,
135 Loader, Store, Validator, WasmValue,
136 };
137 use std::{
138 io::Read,
139 sync::{Arc, Mutex},
140 thread,
141 };
142 use wasmedge_types::{
143 error::{CoreError, CoreLoadError},
144 wat2wasm, CompilerOptimizationLevel, CompilerOutputFormat, RunMode,
145 };
146
147 #[test]
148 #[allow(clippy::assertions_on_result_states)]
149 fn test_compiler() {
150 {
151 let result = Config::create();
152 assert!(result.is_ok());
153 let config = result.unwrap();
154
155 let result = Compiler::create(None);
157 assert!(result.is_ok());
158
159 let result = Compiler::create(Some(&config));
161 assert!(result.is_ok());
162 let compiler = result.unwrap();
163
164 let in_path = std::env::current_dir()
166 .unwrap()
167 .ancestors()
168 .nth(2)
169 .unwrap()
170 .join("examples/wasmedge-sys/data/fibonacci.wat");
171 #[cfg(target_os = "linux")]
172 let out_path = std::path::PathBuf::from("test_aot.so");
173 #[cfg(target_os = "macos")]
174 let out_path = std::path::PathBuf::from("test_aot.dylib");
175 #[cfg(target_os = "windows")]
176 let out_path = std::path::PathBuf::from("test_aot.dll");
177 assert!(!out_path.exists());
178 let result = compiler.compile_from_file(in_path, &out_path);
179 assert!(result.is_ok());
180 assert!(out_path.exists());
181 assert!(std::fs::remove_file(out_path).is_ok());
182
183 let result = compiler.compile_from_file("not_exist.wasm", "not_exist_ast.wasm");
185 assert!(result.is_err());
186 assert_eq!(
187 result.unwrap_err(),
188 Box::new(WasmEdgeError::Core(CoreError::Load(
189 CoreLoadError::IllegalPath
190 )))
191 );
192
193 let out_path = std::path::PathBuf::from("test_aot_malformed.wasm");
195 let result = compiler.compile_from_bytes(b"(module)", &out_path);
196 assert!(result.is_err());
197 assert_eq!(
198 result.unwrap_err(),
199 Box::new(WasmEdgeError::Core(CoreError::Load(
200 CoreLoadError::MalformedMagic
201 )))
202 );
203 assert!(!out_path.exists());
204
205 let result = compiler.compile_from_bytes([], &out_path);
207 assert!(result.is_err());
208 assert_eq!(
209 result.unwrap_err(),
210 Box::new(WasmEdgeError::Core(CoreError::Load(
211 CoreLoadError::UnexpectedEnd
212 )))
213 );
214 assert!(!out_path.exists());
215 }
216
217 {
218 let result = Config::create();
219 assert!(result.is_ok());
220 let mut config = result.unwrap();
221 config.set_aot_compiler_output_format(CompilerOutputFormat::Native);
223
224 let result = Compiler::create(Some(&config));
225 assert!(result.is_ok());
226 let compiler = result.unwrap();
227 let in_path = std::env::current_dir()
228 .unwrap()
229 .ancestors()
230 .nth(2)
231 .unwrap()
232 .join("examples/wasmedge-sys/data/fibonacci.wat");
233 #[cfg(target_os = "linux")]
234 let out_path = std::path::PathBuf::from("test_aot_from_file.so");
235 #[cfg(target_os = "macos")]
236 let out_path = std::path::PathBuf::from("test_aot_from_file.dylib");
237 #[cfg(target_os = "windows")]
238 let out_path = std::path::PathBuf::from("test_aot_from_file.dll");
239 assert!(!out_path.exists());
240 let result = compiler.compile_from_file(in_path, &out_path);
241 assert!(result.is_ok());
242 assert!(out_path.exists());
243
244 let result = std::fs::File::open(&out_path);
246 assert!(result.is_ok());
247 let mut f = result.unwrap();
248 let mut buffer = [0u8; 4];
249 let result = f.read(&mut buffer);
250 assert!(result.is_ok());
251 let wasm_magic: [u8; 4] = [0x00, 0x61, 0x73, 0x6D];
252 assert_ne!(buffer, wasm_magic);
253
254 assert!(std::fs::remove_file(out_path).is_ok());
256 }
257
258 {
259 let result = wat2wasm(
260 br#"(module
261 (export "fib" (func $fib))
262 (func $fib (param $n i32) (result i32)
263 (if
264 (i32.lt_s
265 (local.get $n)
266 (i32.const 2)
267 )
268 (then
269 (return (i32.const 1))
270 )
271 )
272 (return
273 (i32.add
274 (call $fib
275 (i32.sub
276 (local.get $n)
277 (i32.const 2)
278 )
279 )
280 (call $fib
281 (i32.sub
282 (local.get $n)
283 (i32.const 1)
284 )
285 )
286 )
287 )
288 )
289 )
290 "#,
291 );
292 assert!(result.is_ok());
293 let wasm_bytes = result.unwrap();
294
295 let result = Config::create();
296 assert!(result.is_ok());
297 let mut config = result.unwrap();
298 config.set_aot_optimization_level(CompilerOptimizationLevel::O0);
299 config.set_aot_compiler_output_format(CompilerOutputFormat::Native);
300
301 let result = Compiler::create(Some(&config));
302 assert!(result.is_ok());
303 let compiler = result.unwrap();
304 #[cfg(target_os = "linux")]
305 let out_path = std::path::PathBuf::from("test_aot_from_bytes.so");
306 #[cfg(target_os = "macos")]
307 let out_path = std::path::PathBuf::from("test_aot_from_bytes.dylib");
308 #[cfg(target_os = "windows")]
309 let out_path = std::path::PathBuf::from("test_aot_from_bytes.dll");
310 assert!(!out_path.exists());
311 let result = compiler.compile_from_bytes(wasm_bytes, &out_path);
312 assert!(result.is_ok());
313 assert!(out_path.exists());
314
315 assert!(std::fs::remove_file(out_path).is_ok());
317 }
318 }
319
320 #[test]
321 #[ignore]
322 #[allow(clippy::assertions_on_result_states)]
323 fn test_compiler_send() {
324 let result = Config::create();
325 assert!(result.is_ok());
326 let config = result.unwrap();
327
328 let result = Compiler::create(None);
330 assert!(result.is_ok());
331
332 let result = Compiler::create(Some(&config));
334 assert!(result.is_ok());
335 let compiler = result.unwrap();
336
337 let handle = thread::spawn(move || {
338 let in_path = std::env::current_dir()
340 .unwrap()
341 .join("examples/data/fibonacci.wat");
342 #[cfg(target_os = "linux")]
343 let out_path = std::path::PathBuf::from("test_aot_fib_send.so");
344 #[cfg(target_os = "macos")]
345 let out_path = std::path::PathBuf::from("test_aot_fib_send.dylib");
346 #[cfg(target_os = "windows")]
347 let out_path = std::path::PathBuf::from("test_aot_fib_send.dll");
348 assert!(!out_path.exists());
349 let result = compiler.compile_from_file(in_path, &out_path);
350 assert!(result.is_ok());
351 assert!(out_path.exists());
352 assert!(std::fs::remove_file(out_path).is_ok());
353 });
354
355 handle.join().unwrap();
356 }
357
358 #[test]
359 #[ignore]
360 #[allow(clippy::assertions_on_result_states)]
361 fn test_compiler_sync() {
362 let result = Config::create();
363 assert!(result.is_ok());
364 let config = result.unwrap();
365
366 let result = Compiler::create(None);
368 assert!(result.is_ok());
369
370 let result = Compiler::create(Some(&config));
372 assert!(result.is_ok());
373 let compiler = Arc::new(Mutex::new(result.unwrap()));
374
375 let compiler_cloned = Arc::clone(&compiler);
376 let handle = thread::spawn(move || {
377 let result = compiler_cloned.lock();
378 assert!(result.is_ok());
379 let compiler = result.unwrap();
380
381 let in_path = std::env::current_dir()
383 .unwrap()
384 .join("examples/data/fibonacci.wat");
385 let out_path = std::path::PathBuf::from("fibonacci_sync_thread_aot.wasm");
386 assert!(!out_path.exists());
387 let result = compiler.compile_from_file(in_path, &out_path);
388 assert!(result.is_ok());
389 assert!(out_path.exists());
390 assert!(std::fs::remove_file(out_path).is_ok());
391 });
392
393 {
394 let result = compiler.lock();
395 assert!(result.is_ok());
396 let compiler_main = result.unwrap();
397 let in_path = std::env::current_dir()
399 .unwrap()
400 .join("examples/data/fibonacci.wat");
401 #[cfg(target_os = "linux")]
402 let out_path = std::path::PathBuf::from("test_aot_fib_sync.so");
403 #[cfg(target_os = "macos")]
404 let out_path = std::path::PathBuf::from("test_aot_fib_sync.dylib");
405 #[cfg(target_os = "windows")]
406 let out_path = std::path::PathBuf::from("test_aot_fib_sync.dll");
407 assert!(!out_path.exists());
408 let result = compiler_main.compile_from_file(in_path, &out_path);
409 assert!(result.is_ok());
410 assert!(out_path.exists());
411 assert!(std::fs::remove_file(out_path).is_ok());
412 }
413
414 handle.join().unwrap();
415 }
416
417 #[test]
418 fn test_aot() -> Result<(), Box<dyn std::error::Error>> {
419 let mut config = Config::create()?;
421 config.tail_call(true);
423 config.annotations(true);
424 config.memory64(true);
425 config.threads(true);
426 config.exception_handling(true);
427 config.function_references(true);
428 config.set_aot_optimization_level(CompilerOptimizationLevel::O0);
429 config.set_aot_compiler_output_format(CompilerOutputFormat::Native);
430 config.interruptible(true);
431 config.set_run_mode(RunMode::Aot);
434
435 let mut executor = Executor::create(Some(&config), None)?;
437
438 let mut store = Store::create()?;
440
441 let import = create_spec_test_module();
442 executor.register_import_module(&mut store, &import)?;
443
444 let result = Compiler::create(Some(&config));
446 assert!(result.is_ok());
447 let compiler = result.unwrap();
448
449 let in_path = std::env::current_dir()
451 .unwrap()
452 .ancestors()
453 .nth(2)
454 .unwrap()
455 .join("examples/wasmedge-sys/data/fibonacci.wat");
456 #[cfg(target_os = "macos")]
457 let out_path = std::path::PathBuf::from("fibonacci_aot.dylib");
458 #[cfg(target_os = "linux")]
459 let out_path = std::path::PathBuf::from("fibonacci_aot.so");
460 #[cfg(target_os = "windows")]
461 let out_path = std::path::PathBuf::from("fibonacci_aot.dll");
462 assert!(!out_path.exists());
463 let result = compiler.compile_from_file(in_path, &out_path);
464 assert!(result.is_ok());
465 assert!(out_path.exists());
466
467 {
468 let extern_module = Loader::create(Some(&config))?.from_file(&out_path)?;
470 Validator::create(Some(&config))?.validate(&extern_module)?;
471 let mut extern_instance =
472 executor.register_named_module(&mut store, &extern_module, "extern")?;
473
474 let mut fib = extern_instance.get_func_mut("fib")?;
475 let returns = executor.call_func(&mut fib, [WasmValue::from_i32(5)])?;
476 assert_eq!(returns[0].to_i32(), 8);
477 }
478
479 {
480 let active_module = Loader::create(Some(&config))?.from_file(&out_path)?;
482 Validator::create(Some(&config))?.validate(&active_module)?;
483 let mut active_instance =
484 executor.register_active_module(&mut store, &active_module)?;
485
486 let mut fib = active_instance.get_func_mut("fib")?;
487 let returns = executor.call_func(&mut fib, [WasmValue::from_i32(5)])?;
488 assert_eq!(returns[0].to_i32(), 8);
489 }
490
491 assert!(std::fs::remove_file(&out_path).is_ok());
493
494 Ok(())
495 }
496
497 #[cfg(feature = "aot")]
498 fn create_spec_test_module() -> ImportModule<()> {
499 let result = ImportModule::create("spectest", Box::new(()));
501 assert!(result.is_ok());
502 let mut import = result.unwrap();
503
504 let func_ty = FuncType::new(vec![], vec![]);
506 let result = unsafe {
507 Function::create_sync_func(&func_ty, spec_test_print, import.get_host_data_mut(), 0)
508 };
509 assert!(result.is_ok());
510 let host_func = result.unwrap();
511 import.add_func("print", host_func);
513 import
514 }
515
516 #[cfg(feature = "aot")]
517 fn spec_test_print(
518 _data: &mut (),
519 _inst: &mut crate::Instance,
520 _frame: &mut CallingFrame,
521 _inputs: Vec<WasmValue>,
522 ) -> Result<Vec<WasmValue>, CoreError> {
523 Ok(vec![])
524 }
525}