Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-c-api) Ability to generate ImportObject for a specific WASI version #1030

Merged
merged 16 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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