wasmer_c_api/wasm_c_api/wasi/
mod.rs

1//! Unofficial API for WASI integrating with the standard Wasm C API.
2//!
3//! This API will be superseded by a standard WASI API when/if such a standard is created.
4
5pub 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)]
168//pub extern "C" fn wasi_config_capture_stdin(config: &mut wasi_config_t) {
169//    config.inherit_stdin = false;
170//}
171
172#[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/// Initializes the `imports` with an import object that links to
206/// the custom file system
207#[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, // cast wasi_filesystem_t.ptr as &'static [u8]
245            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    /// cbindgen:ignore
331    pub(super) inner: WasiFunctionEnv,
332    pub(super) store: StoreRef,
333}
334
335/// Create a new WASI environment.
336///
337/// It take ownership over the `wasi_config_t`.
338#[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    // TODO: impl capturer for stdin
369
370    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/// Delete a [`wasi_env_t`].
384#[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/// Set the memory on a [`wasi_env_t`].
393// NOTE: Only here to not break the C API.
394// This was previosly supported, but is no longer possible due to WASIX changes.
395// Customizing memories should be done through the builder or the runtime.
396#[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/// The version of WASI. This is determined by the imports namespace
470/// string.
471#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472#[repr(C)]
473#[allow(non_camel_case_types)]
474pub enum wasi_version_t {
475    /// An invalid version.
476    INVALID_VERSION = -1,
477
478    /// Latest version.
479    ///
480    /// It's a “floating” version, i.e. it's an alias to the latest
481    /// version (for the moment, `Snapshot1`). Using this version is a
482    /// way to ensure that modules will run only if they come with the
483    /// latest WASI version (in case of security issues for instance),
484    /// by just updating the runtime.
485    ///
486    /// Note that this version is never returned by an API. It is
487    /// provided only by the user.
488    LATEST = 0,
489
490    /// `wasi_unstable`.
491    SNAPSHOT0 = 1,
492
493    /// `wasi_snapshot_preview1`.
494    SNAPSHOT1 = 2,
495
496    /// `wasix_32v1`.
497    WASIX32V1 = 3,
498
499    /// `wasix_64v1`.
500    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/// Non-standard function to get the imports needed for the WASI
538/// implementation ordered as expected by the `wasm_module_t`.
539#[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}