From ca4a1b41a66fbfbcd5466826a47a19e8aab74369 Mon Sep 17 00:00:00 2001
From: Ivan Enderlin <ivan.enderlin@hoa-project.net>
Date: Mon, 2 Dec 2019 15:59:20 +0100
Subject: [PATCH] feat(runtime-c-api) Ability to generate `ImportObject` for a
 specific WASI version.

This patch introduces 2 new functions:

* `wasmer_wasi_generate_import_object_for_version` and
* `wasmer_wasi_get_version`.

It mimics the current API provided by `wasmer_wasi`, nothing fancy
here. It's just a regular port to C/C++.

Because `wasmer_wasi::get_wasi_version` returns an option, and in
order to simplify the C/C++ API, `wasmer_wasi_get_version` can return
`Version::Unknown` in case of an error. It's up to the user to check
the version is valid (i.e. not unknown).
---
 lib/runtime-c-api/src/import/wasi.rs | 91 ++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 12 deletions(-)

diff --git a/lib/runtime-c-api/src/import/wasi.rs b/lib/runtime-c-api/src/import/wasi.rs
index 3df3c7f70..ec362fdf2 100644
--- a/lib/runtime-c-api/src/import/wasi.rs
+++ b/lib/runtime-c-api/src/import/wasi.rs
@@ -1,6 +1,18 @@
 use super::*;
 use crate::get_slice_checked;
-use std::path::PathBuf;
+use std::{path::PathBuf, ptr, str};
+use wasmer_wasi as wasi;
+
+#[derive(Debug)]
+#[repr(u8)]
+pub enum Version {
+    /// Version cannot be detected or is unknown.
+    Unknown,
+    /// `wasi_unstable`.
+    Snapshot0,
+    /// `wasi_snapshot_preview1`.
+    Snapshot1,
+}
 
 /// Opens a directory that's visible to the WASI module as `alias` but
 /// is backed by the host file at `host_file_path`
@@ -14,9 +26,9 @@ pub struct wasmer_wasi_map_dir_entry_t {
 
 impl wasmer_wasi_map_dir_entry_t {
     /// Converts the data into owned, Rust types
-    pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), std::str::Utf8Error> {
+    pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), str::Utf8Error> {
         let alias = self.alias.as_str()?.to_owned();
-        let host_path = std::path::PathBuf::from(self.host_file_path.as_str()?);
+        let host_path = PathBuf::from(self.host_file_path.as_str()?);
 
         Ok((alias, host_path))
     }
@@ -44,21 +56,74 @@ pub unsafe extern "C" fn wasmer_wasi_generate_import_object(
     let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize);
 
     wasmer_wasi_generate_import_object_inner(
+        Version::Snapshot1,
         arg_list,
         env_list,
         preopened_file_list,
         mapped_dir_list,
     )
-    .unwrap_or(std::ptr::null_mut())
+    .unwrap_or(ptr::null_mut())
+}
+
+/// Creates a WASI import object.
+///
+/// This function is similar to `wasmer_wasi_generate_import_object`
+/// except that the first argument describes the WASI version.
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_wasi_generate_import_object_for_version(
+    version: Version,
+    args: *const wasmer_byte_array,
+    args_len: c_uint,
+    envs: *const wasmer_byte_array,
+    envs_len: c_uint,
+    preopened_files: *const wasmer_byte_array,
+    preopened_files_len: c_uint,
+    mapped_dirs: *const wasmer_wasi_map_dir_entry_t,
+    mapped_dirs_len: c_uint,
+) -> *mut wasmer_import_object_t {
+    let arg_list = get_slice_checked(args, args_len as usize);
+    let env_list = get_slice_checked(envs, envs_len as usize);
+    let preopened_file_list = get_slice_checked(preopened_files, preopened_files_len as usize);
+    let mapped_dir_list = get_slice_checked(mapped_dirs, mapped_dirs_len as usize);
+
+    wasmer_wasi_generate_import_object_inner(
+        version,
+        arg_list,
+        env_list,
+        preopened_file_list,
+        mapped_dir_list,
+    )
+    .unwrap_or(ptr::null_mut())
+}
+
+/// Find the version of WASI used by the module.
+///
+/// In case of error, the returned version is `Version::Unknown`.
+#[no_mangle]
+pub unsafe extern "C" fn wasmer_wasi_get_version(module: *const wasmer_module_t) -> Version {
+    if module.is_null() {
+        return Version::Unknown;
+    }
+
+    let module = &*(module as *const Module);
+
+    match wasi::get_wasi_version(module) {
+        Some(version) => match version {
+            wasi::WasiVersion::Snapshot0 => Version::Snapshot0,
+            wasi::WasiVersion::Snapshot1 => Version::Snapshot1,
+        },
+        None => Version::Unknown,
+    }
 }
 
 /// Inner function that wraps error handling
 fn wasmer_wasi_generate_import_object_inner(
+    version: Version,
     arg_list: &[wasmer_byte_array],
     env_list: &[wasmer_byte_array],
     preopened_file_list: &[wasmer_byte_array],
     mapped_dir_list: &[wasmer_wasi_map_dir_entry_t],
-) -> Result<*mut wasmer_import_object_t, std::str::Utf8Error> {
+) -> Result<*mut wasmer_import_object_t, str::Utf8Error> {
     let arg_vec = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect();
     let env_vec = env_list
         .iter()
@@ -73,7 +138,14 @@ fn wasmer_wasi_generate_import_object_inner(
         .map(|entry| unsafe { entry.as_tuple() })
         .collect::<Result<Vec<_>, _>>()?;
 
-    let import_object = Box::new(wasmer_wasi::generate_import_object(
+    let version = match version {
+        Version::Snapshot0 => wasi::WasiVersion::Snapshot0,
+        Version::Snapshot1 => wasi::WasiVersion::Snapshot1,
+        _ => panic!(format!("Version {:?} is invalid.", version)),
+    };
+
+    let import_object = Box::new(wasi::generate_import_object_for_version(
+        version,
         arg_vec,
         env_vec,
         po_file_vec,
@@ -90,12 +162,7 @@ fn wasmer_wasi_generate_import_object_inner(
 #[no_mangle]
 pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wasmer_import_object_t
 {
-    let import_object = Box::new(wasmer_wasi::generate_import_object(
-        vec![],
-        vec![],
-        vec![],
-        vec![],
-    ));
+    let import_object = Box::new(wasi::generate_import_object(vec![], vec![], vec![], vec![]));
 
     Box::into_raw(import_object) as *mut wasmer_import_object_t
 }