Skip to content

Commit

Permalink
Merge #1030
Browse files Browse the repository at this point in the history
1030: feat(runtime-c-api) Ability to generate `ImportObject` for a specific WASI version r=Hywan a=Hywan

❗️⚠️ Contains #1028, must be merged before this one.

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).

To see only the changes provided by this PR (excluding #1028), check Hywan/wasmer@fix-wasi-get-version...feat-runtime-c-api-wasi-version.

Co-authored-by: Ivan Enderlin <[email protected]>
  • Loading branch information
bors[bot] and Hywan authored Dec 6, 2019
2 parents ee16823 + 45e4081 commit 59d2b8b
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#1030](https://github.com/wasmerio/wasmer/pull/1030) Ability to generate `ImportObject` for a specific version WASI version with the C API.
- [#1028](https://github.com/wasmerio/wasmer/pull/1028) Introduce strict/non-strict modes for `get_wasi_version`
- [#1029](https://github.com/wasmerio/wasmer/pull/1029) Add the “floating” `WasiVersion::Latest` version.
- [#1006](https://github.com/wasmerio/wasmer/pull/1006) Fix minor panic issue when `wasmer::compile_with` called with llvm backend
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime-c-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ rebuild in release mode for the tests to see the changes.
The tests can be run via `cargo test`, such as:

```sh
$ cargo test -- --nocapture
$ cargo test --release -- --nocapture
```

To run tests manually, enter the `lib/runtime-c-api/tests` directory
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime-c-api/src/import/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
value::wasmer_value_tag,
wasmer_byte_array, wasmer_result_t,
};
use libc::c_uint;
use libc::{c_uchar, c_uint};
use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc};
use wasmer_runtime::{Global, Memory, Module, Table};
use wasmer_runtime_core::{
Expand Down
125 changes: 113 additions & 12 deletions lib/runtime-c-api/src/import/wasi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
use super::*;
use crate::get_slice_checked;
use std::path::PathBuf;
use std::{path::PathBuf, ptr, str};
use wasmer_wasi as wasi;

#[derive(Debug, PartialEq)]
#[repr(u8)]
pub enum Version {
/// Version cannot be detected or is unknown.
Unknown = 0,

/// Latest version. See `wasmer_wasi::WasiVersion::Latest` to
/// leran more.
Latest = 1,

/// `wasi_unstable`.
Snapshot0 = 2,

/// `wasi_snapshot_preview1`.
Snapshot1 = 3,
}

impl From<c_uchar> for Version {
fn from(value: c_uchar) -> Self {
match value {
1 => Self::Latest,
2 => Self::Snapshot0,
3 => Self::Snapshot1,
_ => Self::Unknown,
}
}
}

/// Opens a directory that's visible to the WASI module as `alias` but
/// is backed by the host file at `host_file_path`
Expand All @@ -14,9 +43,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))
}
Expand Down Expand Up @@ -44,21 +73,77 @@ 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::Latest,
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 for a specific version.
///
/// This function is similar to `wasmer_wasi_generate_import_object`
/// except that the first argument describes the WASI version.
///
/// The version is expected to be of kind `Version`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_wasi_generate_import_object_for_version(
version: c_uchar,
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.into(),
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, false) {
Some(version) => match version {
wasi::WasiVersion::Snapshot0 => Version::Snapshot0,
wasi::WasiVersion::Snapshot1 => Version::Snapshot1,
wasi::WasiVersion::Latest => Version::Latest,
},
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()
Expand All @@ -73,7 +158,15 @@ 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::Latest => wasi::WasiVersion::Latest,
Version::Snapshot0 => wasi::WasiVersion::Snapshot0,
Version::Snapshot1 => wasi::WasiVersion::Snapshot1,
_ => panic!("Version {:?} is invalid.", version),
};

let import_object = Box::new(wasi::generate_import_object_for_version(
version,
arg_vec,
env_vec,
po_file_vec,
Expand All @@ -90,12 +183,20 @@ 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
}

#[cfg(test)]
mod tests {
use super::Version;

#[test]
fn test_versions_from_uint() {
assert_eq!(Version::Unknown, 0.into());
assert_eq!(Version::Latest, 1.into());
assert_eq!(Version::Snapshot0, 2.into());
assert_eq!(Version::Snapshot1, 3.into());
}
}
8 changes: 4 additions & 4 deletions lib/runtime-c-api/tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ compile_commands.json
CTestTestfile.cmake
_deps
rust-build
test-context
test-exported-memory
test-exports
test-globals
test-import-function
test-import-object
test-imports
test-instantiate
test-memory
test-module
test-module-exports
test-module-import-instantiate
test-module-imports
test-module-serialize
test-tables
test-validate
test-context
test-module-import-instantiate
test-wasi-import-object

test-wasi-import-object
6 changes: 3 additions & 3 deletions lib/runtime-c-api/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ add_executable(test-context test-context.c)
add_executable(test-module-import-instantiate test-module-import-instantiate.c)

if (DEFINED WASI_TESTS)
add_executable(test-wasi-import-object test-wasi-import-object.c)
add_executable(test-wasi-import-object test-wasi-import-object.c)
endif()

find_library(
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll
PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/
WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll
PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/
)

if(NOT WASMER_LIB)
Expand Down
44 changes: 27 additions & 17 deletions lib/runtime-c-api/tests/test-wasi-import-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ void print_wasmer_error()

// helper function to print byte array to stdout
void print_byte_array(wasmer_byte_array *arr) {
for (int i = 0; i < arr->bytes_len; ++i) {
putchar(arr->bytes[i]);
}
for (int i = 0; i < arr->bytes_len; ++i) {
putchar(arr->bytes[i]);
}
}

int main()
Expand Down Expand Up @@ -164,20 +164,7 @@ int main()
};
int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]);

// Create the WASI import object
wasmer_import_object_t *import_object =
wasmer_wasi_generate_import_object(args, wasi_argc,
envs, wasi_env_len,
NULL, 0,
mapped_dirs, mapped_dir_len);

// Create our imports
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
int imports_len = sizeof(imports) / sizeof(imports[0]);
// Add our imports to the import object
wasmer_import_object_extend(import_object, imports, imports_len);

// Read the wasm file bytes
// Read the Wasm file bytes.
FILE *file = fopen("assets/extended_wasi.wasm", "r");
assert(file);
fseek(file, 0, SEEK_END);
Expand All @@ -191,16 +178,39 @@ int main()
// Compile the WebAssembly module
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);

if (compile_result != WASMER_OK)
{
print_wasmer_error();
}

assert(compile_result == WASMER_OK);

// Detect the WASI version if any. This step is not mandatory, we
// use it to test the WASI version API.
Version wasi_version = wasmer_wasi_get_version(module);

printf("WASI version: %d\n", wasi_version);

// Create the WASI import object
wasmer_import_object_t *import_object =
wasmer_wasi_generate_import_object_for_version(wasi_version,
args, wasi_argc,
envs, wasi_env_len,
NULL, 0,
mapped_dirs, mapped_dir_len);

// Create our imports
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
int imports_len = sizeof(imports) / sizeof(imports[0]);
// Add our imports to the import object
wasmer_import_object_extend(import_object, imports, imports_len);

// Instantiatoe the module with our import_object
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Instantiate result: %d\n", instantiate_result);

if (instantiate_result != WASMER_OK)
{
print_wasmer_error();
Expand Down
46 changes: 46 additions & 0 deletions lib/runtime-c-api/wasmer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@
#include <stdint.h>
#include <stdlib.h>

enum Version {
/**
* Version cannot be detected or is unknown.
*/
Unknown = 0,
/**
* Latest version. See `wasmer_wasi::WasiVersion::Latest` to
* leran more.
*/
Latest = 1,
/**
* `wasi_unstable`.
*/
Snapshot0 = 2,
/**
* `wasi_snapshot_preview1`.
*/
Snapshot1 = 3,
};
typedef uint8_t Version;

/**
* List of export/import kinds.
*/
Expand Down Expand Up @@ -890,4 +911,29 @@ wasmer_import_object_t *wasmer_wasi_generate_import_object(const wasmer_byte_arr
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
unsigned int mapped_dirs_len);

/**
* Creates a WASI import object for a specific version.
*
* This function is similar to `wasmer_wasi_generate_import_object`
* except that the first argument describes the WASI version.
*
* The version is expected to be of kind `Version`.
*/
wasmer_import_object_t *wasmer_wasi_generate_import_object_for_version(unsigned char version,
const wasmer_byte_array *args,
unsigned int args_len,
const wasmer_byte_array *envs,
unsigned int envs_len,
const wasmer_byte_array *preopened_files,
unsigned int preopened_files_len,
const wasmer_wasi_map_dir_entry_t *mapped_dirs,
unsigned int mapped_dirs_len);

/**
* Find the version of WASI used by the module.
*
* In case of error, the returned version is `Version::Unknown`.
*/
Version wasmer_wasi_get_version(const wasmer_module_t *module);

#endif /* WASMER_H */
Loading

0 comments on commit 59d2b8b

Please sign in to comment.