1pub use super::unstable::wasi::wasi_get_unordered_imports;
6use super::{
7 externals::{wasm_extern_t, wasm_extern_vec_t, wasm_func_t, wasm_memory_t},
8 instance::wasm_instance_t,
9 module::wasm_module_t,
10 store::{StoreRef, wasm_store_t},
11 types::wasm_byte_vec_t,
12};
13use crate::error::update_last_error;
14use std::convert::TryFrom;
15use std::ffi::CStr;
16use std::os::raw::c_char;
17use std::slice;
18use std::sync::Arc;
19#[cfg(feature = "webc_runner")]
20use wasmer_api::{AsStoreMut, Imports, Module};
21use wasmer_wasix::{
22 Pipe, PluggableRuntime, WasiEnv, WasiEnvBuilder, WasiFunctionEnv, WasiVersion,
23 default_fs_backing, get_wasi_version,
24 runtime::task_manager::{block_on, tokio::TokioTaskManager},
25 virtual_fs::AsyncReadExt,
26 virtual_fs::VirtualFile,
27};
28
29#[derive(Debug)]
30#[allow(non_camel_case_types)]
31pub struct wasi_config_t {
32 inherit_stdout: bool,
33 inherit_stderr: bool,
34 inherit_stdin: bool,
35 builder: WasiEnvBuilder,
36 runtime: Option<tokio::runtime::Runtime>,
37}
38
39#[unsafe(no_mangle)]
40pub unsafe extern "C" fn wasi_config_new(
41 program_name: *const c_char,
42) -> Option<Box<wasi_config_t>> {
43 debug_assert!(!program_name.is_null());
44
45 let name_c_str = unsafe { CStr::from_ptr(program_name) };
46 let prog_name = c_try!(name_c_str.to_str());
47
48 let runtime = tokio::runtime::Builder::new_multi_thread()
49 .enable_all()
50 .build()
51 .unwrap();
52 let _guard = runtime.enter();
53
54 Some(Box::new(wasi_config_t {
55 inherit_stdout: true,
56 inherit_stderr: true,
57 inherit_stdin: true,
58 builder: WasiEnv::builder(prog_name).fs(default_fs_backing()),
59 runtime: Some(runtime),
60 }))
61}
62
63#[unsafe(no_mangle)]
64pub unsafe extern "C" fn wasi_config_env(
65 config: &mut wasi_config_t,
66 key: *const c_char,
67 value: *const c_char,
68) {
69 debug_assert!(!key.is_null());
70 debug_assert!(!value.is_null());
71
72 let key_cstr = unsafe { CStr::from_ptr(key) };
73 let key_bytes = key_cstr.to_bytes();
74 let value_cstr = unsafe { CStr::from_ptr(value) };
75 let value_bytes = value_cstr.to_bytes();
76
77 config.builder.add_env(key_bytes, value_bytes);
78}
79
80#[unsafe(no_mangle)]
81pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const c_char) {
82 debug_assert!(!arg.is_null());
83
84 let arg_cstr = unsafe { CStr::from_ptr(arg) };
85 let arg_bytes = arg_cstr.to_bytes();
86
87 config.builder.add_arg(arg_bytes);
88}
89
90#[unsafe(no_mangle)]
91pub unsafe extern "C" fn wasi_config_preopen_dir(
92 config: &mut wasi_config_t,
93 dir: *const c_char,
94) -> bool {
95 let dir_cstr = unsafe { CStr::from_ptr(dir) };
96 let dir_bytes = dir_cstr.to_bytes();
97 let dir_str = match std::str::from_utf8(dir_bytes) {
98 Ok(dir_str) => dir_str,
99 Err(e) => {
100 update_last_error(e);
101 return false;
102 }
103 };
104
105 if let Err(e) = config.builder.add_preopen_dir(dir_str) {
106 update_last_error(e);
107 return false;
108 }
109
110 true
111}
112
113#[unsafe(no_mangle)]
114pub unsafe extern "C" fn wasi_config_mapdir(
115 config: &mut wasi_config_t,
116 alias: *const c_char,
117 dir: *const c_char,
118) -> bool {
119 let alias_cstr = unsafe { CStr::from_ptr(alias) };
120 let alias_bytes = alias_cstr.to_bytes();
121 let alias_str = match std::str::from_utf8(alias_bytes) {
122 Ok(alias_str) => alias_str,
123 Err(e) => {
124 update_last_error(e);
125 return false;
126 }
127 };
128
129 let dir_cstr = unsafe { CStr::from_ptr(dir) };
130 let dir_bytes = dir_cstr.to_bytes();
131 let dir_str = match std::str::from_utf8(dir_bytes) {
132 Ok(dir_str) => dir_str,
133 Err(e) => {
134 update_last_error(e);
135 return false;
136 }
137 };
138
139 if let Err(e) = config.builder.add_map_dir(alias_str, dir_str) {
140 update_last_error(e);
141 return false;
142 }
143
144 true
145}
146
147#[unsafe(no_mangle)]
148pub extern "C" fn wasi_config_capture_stdout(config: &mut wasi_config_t) {
149 config.inherit_stdout = false;
150}
151
152#[unsafe(no_mangle)]
153pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
154 config.inherit_stdout = true;
155}
156
157#[unsafe(no_mangle)]
158pub extern "C" fn wasi_config_capture_stderr(config: &mut wasi_config_t) {
159 config.inherit_stderr = false;
160}
161
162#[unsafe(no_mangle)]
163pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) {
164 config.inherit_stderr = true;
165}
166
167#[unsafe(no_mangle)]
173pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) {
174 config.inherit_stdin = true;
175}
176
177#[repr(C)]
178pub struct wasi_filesystem_t {
179 ptr: *const c_char,
180 size: usize,
181}
182
183#[unsafe(no_mangle)]
184pub unsafe extern "C" fn wasi_filesystem_init_static_memory(
185 volume_bytes: Option<&wasm_byte_vec_t>,
186) -> Option<Box<wasi_filesystem_t>> {
187 let volume_bytes = volume_bytes.as_ref()?;
188 Some(Box::new(wasi_filesystem_t {
189 ptr: {
190 let ptr = unsafe { volume_bytes.data.as_ref()? } as *const _ as *const c_char;
191 if ptr.is_null() {
192 return None;
193 }
194 ptr
195 },
196 size: volume_bytes.size,
197 }))
198}
199
200#[unsafe(no_mangle)]
201pub unsafe extern "C" fn wasi_filesystem_delete(ptr: *mut wasi_filesystem_t) {
202 let _ = unsafe { Box::from_raw(ptr) };
203}
204
205#[cfg(feature = "webc_runner")]
208#[unsafe(no_mangle)]
209pub unsafe extern "C" fn wasi_env_with_filesystem(
210 config: Box<wasi_config_t>,
211 store: Option<&mut wasm_store_t>,
212 module: Option<&wasm_module_t>,
213 fs: Option<&wasi_filesystem_t>,
214 imports: Option<&mut wasm_extern_vec_t>,
215 package: *const c_char,
216) -> Option<Box<wasi_env_t>> {
217 unsafe { wasi_env_with_filesystem_inner(config, store, module, fs, imports, package) }
218}
219
220#[cfg(feature = "webc_runner")]
221unsafe fn wasi_env_with_filesystem_inner(
222 config: Box<wasi_config_t>,
223 store: Option<&mut wasm_store_t>,
224 module: Option<&wasm_module_t>,
225 fs: Option<&wasi_filesystem_t>,
226 imports: Option<&mut wasm_extern_vec_t>,
227 package: *const c_char,
228) -> Option<Box<wasi_env_t>> {
229 let store = &mut store?.inner;
230 let fs = fs.as_ref()?;
231 let package_str = unsafe { CStr::from_ptr(package) };
232 let package = package_str.to_str().unwrap_or("");
233 let module = &module.as_ref()?.inner;
234 let imports = imports?;
235 #[allow(clippy::unnecessary_cast)]
236 let fs_bytes = unsafe { &*(fs.ptr as *const u8) };
237
238 let (wasi_env, import_object) = {
239 let mut store_mut = unsafe { store.store_mut() };
240 prepare_webc_env(
241 config,
242 &mut store_mut,
243 module,
244 fs_bytes, fs.size,
246 package,
247 )?
248 };
249
250 imports_set_buffer(store, module, import_object, imports)?;
251
252 Some(Box::new(wasi_env_t {
253 inner: wasi_env,
254 store: store.clone(),
255 }))
256}
257
258#[cfg(feature = "webc_runner")]
259fn prepare_webc_env(
260 mut config: Box<wasi_config_t>,
261 store: &mut impl AsStoreMut,
262 module: &Module,
263 bytes: &'static u8,
264 len: usize,
265 package_name: &str,
266) -> Option<(WasiFunctionEnv, Imports)> {
267 use virtual_fs::static_fs::StaticFileSystem;
268 use wasmer_wasix::virtual_fs::FileSystem;
269 use webc::v1::{FsEntryType, WebC};
270
271 let store_mut = store.as_store_mut();
272 let runtime = config.runtime.take();
273
274 let runtime = runtime.unwrap_or_else(|| {
275 tokio::runtime::Builder::new_multi_thread()
276 .enable_all()
277 .build()
278 .unwrap()
279 });
280
281 let handle = runtime.handle().clone();
282 let _guard = handle.enter();
283 let mut rt = PluggableRuntime::new(Arc::new(TokioTaskManager::new(runtime)));
284 rt.set_engine(store_mut.engine().clone());
285
286 let slice = unsafe { std::slice::from_raw_parts(bytes, len) };
287 let volumes = WebC::parse_volumes_from_fileblock(slice).ok()?;
288 let top_level_dirs = volumes
289 .into_iter()
290 .flat_map(|(_, volume)| {
291 volume
292 .header
293 .top_level
294 .iter()
295 .filter(|entry| entry.fs_type == FsEntryType::Dir)
296 .cloned()
297 .map(|e| e.text.to_string())
298 .collect::<Vec<_>>()
299 .into_iter()
300 })
301 .collect::<Vec<_>>();
302
303 let filesystem =
304 Arc::new(StaticFileSystem::init(slice, package_name)?) as Arc<dyn FileSystem + Send + Sync>;
305 let mut builder = config.builder.runtime(Arc::new(rt));
306
307 if !config.inherit_stdout {
308 builder.set_stdout(Box::new(Pipe::channel().0));
309 }
310
311 if !config.inherit_stderr {
312 builder.set_stderr(Box::new(Pipe::channel().0));
313 }
314
315 builder.set_fs(filesystem);
316
317 for f_name in top_level_dirs.iter() {
318 builder
319 .add_preopen_build(|p| p.directory(f_name).read(true).write(true).create(true))
320 .ok()?;
321 }
322 let env = builder.finalize(store).ok()?;
323
324 let import_object = env.import_object(store, module).ok()?;
325 Some((env, import_object))
326}
327
328#[allow(non_camel_case_types)]
329pub struct wasi_env_t {
330 pub(super) inner: WasiFunctionEnv,
332 pub(super) store: StoreRef,
333}
334
335#[unsafe(no_mangle)]
339pub unsafe extern "C" fn wasi_env_new(
340 store: Option<&mut wasm_store_t>,
341 mut config: Box<wasi_config_t>,
342) -> Option<Box<wasi_env_t>> {
343 let store = &mut store?.inner;
344 let mut store_mut = unsafe { store.store_mut() };
345
346 let runtime = config.runtime.take();
347
348 let runtime = runtime.unwrap_or_else(|| {
349 tokio::runtime::Builder::new_multi_thread()
350 .enable_all()
351 .build()
352 .unwrap()
353 });
354
355 let handle = runtime.handle().clone();
356 let _guard = handle.enter();
357 let mut rt = PluggableRuntime::new(Arc::new(TokioTaskManager::new(runtime)));
358 rt.set_engine(store_mut.engine().clone());
359
360 if !config.inherit_stdout {
361 config.builder.set_stdout(Box::new(Pipe::channel().0));
362 }
363
364 if !config.inherit_stderr {
365 config.builder.set_stderr(Box::new(Pipe::channel().0));
366 }
367
368 let env = c_try!(
371 config
372 .builder
373 .runtime(Arc::new(rt))
374 .finalize(&mut store_mut)
375 );
376
377 Some(Box::new(wasi_env_t {
378 inner: env,
379 store: store.clone(),
380 }))
381}
382
383#[unsafe(no_mangle)]
385pub extern "C" fn wasi_env_delete(state: Option<Box<wasi_env_t>>) {
386 if let Some(mut env) = state {
387 let mut store = unsafe { env.store.store_mut() };
388 env.inner.on_exit(&mut store, None);
389 }
390}
391
392#[unsafe(no_mangle)]
397#[deprecated(since = "4.0.0")]
398pub unsafe extern "C" fn wasi_env_set_memory(_env: &mut wasi_env_t, _memory: &wasm_memory_t) {
399 panic!("wasmer_env_set_memory() is not supported");
400}
401
402#[unsafe(no_mangle)]
403pub unsafe extern "C" fn wasi_env_read_stdout(
404 env: &mut wasi_env_t,
405 buffer: *mut c_char,
406 buffer_len: usize,
407) -> isize {
408 let inner_buffer = unsafe { slice::from_raw_parts_mut(buffer as *mut _, buffer_len) };
409 let store = unsafe { env.store.store() };
410
411 let stdout = {
412 let data = env.inner.data(&store);
413 data.stdout()
414 };
415
416 if let Ok(mut stdout) = stdout {
417 if let Some(stdout) = stdout.as_mut() {
418 read_inner(stdout, inner_buffer)
419 } else {
420 update_last_error("could not find a file handle for `stdout`");
421 -1
422 }
423 } else {
424 update_last_error("could not find a file handle for `stdout`");
425 -1
426 }
427}
428
429#[unsafe(no_mangle)]
430pub unsafe extern "C" fn wasi_env_read_stderr(
431 env: &mut wasi_env_t,
432 buffer: *mut c_char,
433 buffer_len: usize,
434) -> isize {
435 let inner_buffer = unsafe { slice::from_raw_parts_mut(buffer as *mut _, buffer_len) };
436 let store = unsafe { env.store.store() };
437 let stderr = {
438 let data = env.inner.data(&store);
439 data.stderr()
440 };
441 if let Ok(mut stderr) = stderr {
442 if let Some(stderr) = stderr.as_mut() {
443 read_inner(stderr, inner_buffer)
444 } else {
445 update_last_error("could not find a file handle for `stderr`");
446 -1
447 }
448 } else {
449 update_last_error("could not find a file handle for `stderr`");
450 -1
451 }
452}
453
454fn read_inner(
455 wasi_file: &mut Box<dyn VirtualFile + Send + Sync + 'static>,
456 inner_buffer: &mut [u8],
457) -> isize {
458 block_on(async {
459 match wasi_file.read(inner_buffer).await {
460 Ok(a) => a as isize,
461 Err(err) => {
462 update_last_error(format!("failed to read wasi_file: {err}"));
463 -1
464 }
465 }
466 })
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472#[repr(C)]
473#[allow(non_camel_case_types)]
474pub enum wasi_version_t {
475 INVALID_VERSION = -1,
477
478 LATEST = 0,
489
490 SNAPSHOT0 = 1,
492
493 SNAPSHOT1 = 2,
495
496 WASIX32V1 = 3,
498
499 WASIX64V1 = 4,
501}
502
503impl From<WasiVersion> for wasi_version_t {
504 fn from(other: WasiVersion) -> Self {
505 match other {
506 WasiVersion::Snapshot0 => wasi_version_t::SNAPSHOT0,
507 WasiVersion::Snapshot1 => wasi_version_t::SNAPSHOT1,
508 WasiVersion::Wasix32v1 => wasi_version_t::WASIX32V1,
509 WasiVersion::Wasix64v1 => wasi_version_t::WASIX64V1,
510 WasiVersion::Latest => wasi_version_t::LATEST,
511 }
512 }
513}
514
515impl TryFrom<wasi_version_t> for WasiVersion {
516 type Error = &'static str;
517
518 fn try_from(other: wasi_version_t) -> Result<Self, Self::Error> {
519 Ok(match other {
520 wasi_version_t::INVALID_VERSION => return Err("Invalid WASI version cannot be used"),
521 wasi_version_t::SNAPSHOT0 => WasiVersion::Snapshot0,
522 wasi_version_t::SNAPSHOT1 => WasiVersion::Snapshot1,
523 wasi_version_t::WASIX32V1 => WasiVersion::Wasix32v1,
524 wasi_version_t::WASIX64V1 => WasiVersion::Wasix64v1,
525 wasi_version_t::LATEST => WasiVersion::Latest,
526 })
527 }
528}
529
530#[unsafe(no_mangle)]
531pub unsafe extern "C" fn wasi_get_wasi_version(module: &wasm_module_t) -> wasi_version_t {
532 get_wasi_version(&module.inner, false)
533 .map(Into::into)
534 .unwrap_or(wasi_version_t::INVALID_VERSION)
535}
536
537#[unsafe(no_mangle)]
540pub unsafe extern "C" fn wasi_get_imports(
541 _store: Option<&wasm_store_t>,
542 wasi_env: Option<&mut wasi_env_t>,
543 module: Option<&wasm_module_t>,
544 imports: &mut wasm_extern_vec_t,
545) -> bool {
546 unsafe { wasi_get_imports_inner(wasi_env, module, imports) }.is_some()
547}
548
549unsafe fn wasi_get_imports_inner(
550 wasi_env: Option<&mut wasi_env_t>,
551 module: Option<&wasm_module_t>,
552 imports: &mut wasm_extern_vec_t,
553) -> Option<()> {
554 let wasi_env = wasi_env?;
555 let store = &mut wasi_env.store;
556 let module = module?;
557
558 let mut import_object = {
559 let mut store_mut = unsafe { store.store_mut() };
560 c_try!(wasi_env.inner.import_object(&mut store_mut, &module.inner))
561 };
562
563 let shared_memory = module.inner.imports().memories().next().map(|a| *a.ty());
564
565 let spawn_type = match shared_memory {
566 Some(ty) => wasmer_wasix::runtime::SpawnType::CreateMemoryOfType(ty),
567 None => wasmer_wasix::runtime::SpawnType::CreateMemory,
568 };
569
570 let tasks = {
571 let store_ref = unsafe { store.store() };
572 wasi_env
573 .inner
574 .data(&store_ref)
575 .runtime
576 .task_manager()
577 .clone()
578 };
579 let memory = {
580 let mut store_mut = unsafe { store.store_mut() };
581 tasks.build_memory(&mut store_mut, &spawn_type).unwrap()
582 };
583
584 if let Some(memory) = memory {
585 import_object.define("env", "memory", memory);
586 }
587
588 imports_set_buffer(store, &module.inner, import_object, imports)?;
589
590 Some(())
591}
592
593pub(crate) fn imports_set_buffer(
594 store: &StoreRef,
595 module: &wasmer_api::Module,
596 import_object: wasmer_api::Imports,
597 imports: &mut wasm_extern_vec_t,
598) -> Option<()> {
599 imports.set_buffer(c_try!(
600 module
601 .imports()
602 .map(|import_type| {
603 let ext = import_object
604 .get_export(import_type.module(), import_type.name())
605 .ok_or_else(|| {
606 format!(
607 "Failed to resolve import \"{}\" \"{}\"",
608 import_type.module(),
609 import_type.name()
610 )
611 })?;
612
613 Ok(Some(Box::new(wasm_extern_t::new(store.clone(), ext))))
614 })
615 .collect::<Result<Vec<_>, String>>()
616 ));
617
618 Some(())
619}
620
621#[unsafe(no_mangle)]
622pub unsafe extern "C" fn wasi_env_initialize_instance(
623 wasi_env: &mut wasi_env_t,
624 store: &mut wasm_store_t,
625 instance: &mut wasm_instance_t,
626) -> bool {
627 let mut store_mut = unsafe { store.inner.store_mut() };
628 wasi_env
629 .inner
630 .initialize(&mut store_mut, instance.inner.clone())
631 .unwrap();
632 true
633}
634
635#[unsafe(no_mangle)]
636pub unsafe extern "C" fn wasi_get_start_function(
637 instance: &mut wasm_instance_t,
638) -> Option<Box<wasm_func_t>> {
639 let start = c_try!(instance.inner.exports.get_function("_start"));
640
641 Some(Box::new(wasm_func_t {
642 extern_: wasm_extern_t::new(instance.store.clone(), start.clone().into()),
643 }))
644}
645
646#[cfg(test)]
647mod tests {
648 #[cfg(not(target_os = "windows"))]
649 use inline_c::assert_c;
650 #[cfg(target_os = "windows")]
651 use wasmer_inline_c::assert_c;
652
653 #[allow(
654 unexpected_cfgs,
655 reason = "tools like cargo-llvm-coverage pass --cfg coverage"
656 )]
657 #[cfg_attr(coverage_nightly, coverage(off))]
658 #[test]
659 fn test_wasi_get_wasi_version_snapshot0() {
660 (assert_c! {
661 #include "tests/wasmer.h"
662
663 int main() {
664 wasm_engine_t* engine = wasm_engine_new();
665 wasm_store_t* store = wasm_store_new(engine);
666 wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0);
667
668 wasm_byte_vec_t wat;
669 wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_unstable\" \"args_get\" (func (param i32 i32) (result i32))))");
670 wasm_byte_vec_t wasm;
671 wat2wasm(&wat, &wasm);
672
673 wasm_module_t* module = wasm_module_new(store, &wasm);
674 assert(module);
675
676 assert(wasi_get_wasi_version(module) == SNAPSHOT0);
677
678 wasm_module_delete(module);
679 wasm_byte_vec_delete(&wasm);
680 wasm_byte_vec_delete(&wat);
681 wasmer_funcenv_delete(env);
682 wasm_store_delete(store);
683 wasm_engine_delete(engine);
684
685 return 0;
686 }
687 })
688 .success();
689 }
690
691 #[allow(
692 unexpected_cfgs,
693 reason = "tools like cargo-llvm-coverage pass --cfg coverage"
694 )]
695 #[cfg_attr(coverage_nightly, coverage(off))]
696 #[test]
697 fn test_wasi_get_wasi_version_snapshot1() {
698 (assert_c! {
699 #include "tests/wasmer.h"
700
701 int main() {
702 wasm_engine_t* engine = wasm_engine_new();
703 wasm_store_t* store = wasm_store_new(engine);
704 wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0);
705
706 wasm_byte_vec_t wat;
707 wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_snapshot_preview1\" \"args_get\" (func (param i32 i32) (result i32))))");
708 wasm_byte_vec_t wasm;
709 wat2wasm(&wat, &wasm);
710
711 wasm_module_t* module = wasm_module_new(store, &wasm);
712 assert(module);
713
714 assert(wasi_get_wasi_version(module) == SNAPSHOT1);
715
716 wasm_module_delete(module);
717 wasm_byte_vec_delete(&wasm);
718 wasm_byte_vec_delete(&wat);
719 wasmer_funcenv_delete(env);
720 wasm_store_delete(store);
721 wasm_engine_delete(engine);
722
723 return 0;
724 }
725 })
726 .success();
727 }
728
729 #[allow(
730 unexpected_cfgs,
731 reason = "tools like cargo-llvm-coverage pass --cfg coverage"
732 )]
733 #[cfg_attr(coverage_nightly, coverage(off))]
734 #[test]
735 fn test_wasi_get_wasi_version_invalid() {
736 (assert_c! {
737 #include "tests/wasmer.h"
738
739 int main() {
740 wasm_engine_t* engine = wasm_engine_new();
741 wasm_store_t* store = wasm_store_new(engine);
742 wasmer_funcenv_t* env = wasmer_funcenv_new(store, 0);
743
744 wasm_byte_vec_t wat;
745 wasmer_byte_vec_new_from_string(&wat, "(module (import \"wasi_snpsht_prvw1\" \"args_get\" (func (param i32 i32) (result i32))))");
746 wasm_byte_vec_t wasm;
747 wat2wasm(&wat, &wasm);
748
749 wasm_module_t* module = wasm_module_new(store, &wasm);
750 assert(module);
751
752 assert(wasi_get_wasi_version(module) == INVALID_VERSION);
753
754 wasm_module_delete(module);
755 wasm_byte_vec_delete(&wasm);
756 wasm_byte_vec_delete(&wat);
757 wasmer_funcenv_delete(env);
758 wasm_store_delete(store);
759 wasm_engine_delete(engine);
760
761 return 0;
762 }
763 })
764 .success();
765 }
766}