Skip to content

Commit

Permalink
Merge #2053
Browse files Browse the repository at this point in the history
2053: feat(c-api) Implement `wasi_get_unordered_imports` r=Hywan a=Hywan

Depend on #2083.

# Description

The problem I've with the C API for WASI is the following:

```c
// At some point, we write this:
bool success = wasi_get_imports(store, module, wasi_env, &imports_to_collect);
```

This function iterates over the module imports, and for each entry, it looks inside the (inner generated) WASI import object if a matching entry exists. If it doesn't, it fails. See:

https://github.com/wasmerio/wasmer/blob/6b028410c23da088d62a6b6919e2c086931ad101/lib/c-api/src/wasm_c_api/wasi/mod.rs#L333-L356

So… if a module requires WASI and WASI only, it works like a charm of course.
But, if a module requires WASI + other imports, it will fail.

And I think it's a common pattern to use WASI + some other imports (like `math.random` or anything related to I/O etc etc.).

What we need is an API to collect all the WASI imports, in no particular order, by module/namespace and name, so that we can implement an API like `ImportObject` to re-order the imports when needed (i.e. when passing a vec of externs to `wasm_instance_new` for example).

# Review

- [x] Add a short description of the the change to the CHANGELOG.md file


Co-authored-by: Ivan Enderlin <[email protected]>
  • Loading branch information
bors[bot] and Hywan authored Feb 1, 2021
2 parents 1cea9cb + 98bff43 commit 1db4d5f
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 660 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
## **[Unreleased]**

### Added
- [#2053](https://github.com/wasmerio/wasmer/pull/2053) Implement the non-standard `wasi_get_unordered_imports` function in the C API.
- [#2059](https://github.com/wasmerio/wasmer/pull/2059) Ability to capture `stdout` and `stderr` with WASI in the C API.
- [#2040](https://github.com/wasmerio/wasmer/pull/2040) Add `InstanceHandle::vmoffsets` to expose the offsets of the `vmctx` region.
- [#2026](https://github.com/wasmerio/wasmer/pull/2010) Expose trap code of a `RuntimeError`, if it's a `Trap`.
- [#2054](https://github.com/wasmerio/wasmer/pull/2054) Add `wasm_config_delete` to the Wasm C API.

### Changed
- [#2083](https://github.com/wasmerio/wasmer/pull/2083) Mark `wasi_env_set_(instance|memory)` as deprecated. You may simply remove them with no side-effect.
- [#2056](https://github.com/wasmerio/wasmer/pull/2056) Change back to depend on the `enumset` crate instead of `wasmer_enumset`

### Fixed
Expand Down
100 changes: 75 additions & 25 deletions lib/c-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,21 @@ macro_rules! map_feature_as_c_define {
}

fn main() {
if !running_self() {
return;
}

let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();

if building_c_api_headers() {
build_wasm_c_api_headers(&crate_dir, &out_dir);
build_wasmer_c_api_headers(&crate_dir, &out_dir);
}

build_wasm_c_api_headers(&crate_dir, &out_dir);
build_wasmer_c_api_headers(&crate_dir, &out_dir);
build_inline_c_env_vars();
}

/// Check whether we should build the C API headers.
///
/// For the moment, it's always enabled, unless if the `DOCS_RS`
/// environment variable is present.
fn building_c_api_headers() -> bool {
env::var("DOCS_RS").is_err()
/// Check whether we should build the C API headers or set `inline-c` up.
fn running_self() -> bool {
env::var("DOCS_RS").is_err() && env::var("_CBINDGEN_IS_RUNNING").is_err()
}

/// Build the header files for the `wasm_c_api` API.
Expand All @@ -89,8 +87,41 @@ fn build_wasm_c_api_headers(crate_dir: &str, out_dir: &str) {
out_header_file.push("wasmer_wasm");

let mut pre_header = format!(
r#"// The Wasmer C/C++ header file compatible with the `wasm-c-api` standard API.
// This file is generated by lib/c-api/build.rs.
r#"// The Wasmer C/C++ header file compatible with the [`wasm-c-api`]
// standard API, as `wasm.h` (included here).
//
// This file is automatically generated by `lib/c-api/build.rs` of the
// [`wasmer-c-api`] Rust crate.
//
// # Stability
//
// The [`wasm-c-api`] standard API is a _living_ standard. There is no
// commitment for stability yet. We (Wasmer) will try our best to keep
// backward compatibility as much as possible. Nonetheless, some
// necessary API aren't yet standardized, and as such, we provide a
// custom API, e.g. `wasi_*` types and functions.
//
// The documentation makes it clear whether a function is unstable.
//
// When a type or a function will be deprecated, it will be marked as
// such with the appropriated compiler warning, and will be removed at
// the next release round.
//
// # Documentation
//
// At the time of writing, the [`wasm-c-api`] standard has no
// documentation. This file also does not include inline
// documentation. However, we have made (and we continue to make) an
// important effort to document everything. [See the documentation
// online][documentation]. Please refer to this page for the real
// canonical documentation. It also contains numerous examples.
//
// To generate the documentation locally, run `cargo doc --open` from
// within the [`wasmer-c-api`] Rust crate.
//
// [`wasm-c-api`]: https://github.com/WebAssembly/wasm-c-api
// [`wasmer-c-api`]: https://github.com/wasmerio/wasmer/tree/master/lib/c-api
// [documentation]: https://wasmerio.github.io/wasmer/crates/wasmer_c_api/
#if !defined(WASMER_WASM_H_PRELUDE)
Expand Down Expand Up @@ -184,10 +215,12 @@ fn build_wasmer_c_api_headers(crate_dir: &str, out_dir: &str) {
out_header_file.set_extension("h");

// Build and generate the header file.
exclude_items_from_wasm_c_api(new_builder(Language::C, crate_dir, guard, &pre_header))
.generate()
.expect("Unable to generate C bindings")
.write_to_file(out_header_file.as_path());
exclude_items_from_wasm_c_api(
new_builder(Language::C, crate_dir, guard, &pre_header).with_documentation(true),
)
.generate()
.expect("Unable to generate C bindings")
.write_to_file(out_header_file.as_path());

// Copy the generated bindings from `OUT_DIR` to
// `CARGO_MANIFEST_DIR`.
Expand All @@ -203,10 +236,12 @@ fn build_wasmer_c_api_headers(crate_dir: &str, out_dir: &str) {
out_header_file.set_extension("hh");

// Build and generate the header file.
exclude_items_from_wasm_c_api(new_builder(Language::Cxx, crate_dir, guard, &pre_header))
.generate()
.expect("Unable to generate C++ bindings")
.write_to_file(out_header_file.as_path());
exclude_items_from_wasm_c_api(
new_builder(Language::Cxx, crate_dir, guard, &pre_header).with_documentation(true),
)
.generate()
.expect("Unable to generate C++ bindings")
.write_to_file(out_header_file.as_path());

// Copy the generated bindings from `OUT_DIR` to
// `CARGO_MANIFEST_DIR`.
Expand Down Expand Up @@ -237,7 +272,7 @@ fn add_wasmer_version(pre_header: &mut String) {

/// Create a fresh new `Builder`, already pre-configured.
fn new_builder(language: Language, crate_dir: &str, include_guard: &str, header: &str) -> Builder {
Builder::new()
let builder = Builder::new()
.with_config(cbindgen::Config {
sort_by: cbindgen::SortKey::Name,
cpp_compat: true,
Expand All @@ -247,13 +282,19 @@ fn new_builder(language: Language, crate_dir: &str, include_guard: &str, header:
.with_crate(crate_dir)
.with_include_guard(include_guard)
.with_header(header)
.with_documentation(true)
.with_documentation(false)
.with_parse_expand(&[env::var("CARGO_PKG_NAME").unwrap()])
.with_define("target_family", "windows", "_WIN32")
.with_define("target_arch", "x86_64", "ARCH_X86_64")
.with_define("feature", "jit", JIT_FEATURE_AS_C_DEFINE)
.with_define("feature", "compiler", COMPILER_FEATURE_AS_C_DEFINE)
.with_define("feature", "wasi", WASI_FEATURE_AS_C_DEFINE)
.with_define("feature", "emscripten", EMSCRIPTEN_FEATURE_AS_C_DEFINE)
.with_define("feature", "emscripten", EMSCRIPTEN_FEATURE_AS_C_DEFINE);

#[cfg(feature = "system-libffi")]
let builder = builder.with_parse_expand_features(&["system-libffi"]);

builder
}

/// Exclude types and functions from the `deprecated` API.
Expand Down Expand Up @@ -421,14 +462,23 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder {
.exclude_item("wasi_env_set_memory")
.exclude_item("wasi_env_t")
.exclude_item("wasi_get_imports")
.exclude_item("wasi_get_imports_inner")
.exclude_item("wasi_get_start_function")
.exclude_item("wasi_get_unordered_imports")
.exclude_item("wasi_get_wasi_version")
.exclude_item("wasi_version_t")
.exclude_item("wasm_config_set_compiler")
.exclude_item("wasm_config_set_engine")
.exclude_item("wasm_module_name")
.exclude_item("wasm_module_set_name")
.exclude_item("wasm_named_extern_module")
.exclude_item("wasm_named_extern_name")
.exclude_item("wasm_named_extern_t")
.exclude_item("wasm_named_extern_unwrap")
.exclude_item("wasm_named_extern_vec_copy")
.exclude_item("wasm_named_extern_vec_delete")
.exclude_item("wasm_named_extern_vec_new")
.exclude_item("wasm_named_extern_vec_new_empty")
.exclude_item("wasm_named_extern_vec_new_uninitialized")
.exclude_item("wasmer_compiler_t")
.exclude_item("wasmer_engine_t")
.exclude_item("wat2wasm")
Expand Down
67 changes: 67 additions & 0 deletions lib/c-api/src/wasm_c_api/externals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ pub struct wasm_extern_t {

wasm_declare_boxed_vec!(extern);

/// Copy a `wasm_extern_t`.
#[no_mangle]
pub unsafe extern "C" fn wasm_extern_copy(r#extern: &wasm_extern_t) -> Box<wasm_extern_t> {
Box::new(r#extern.clone())
}

/// Delete an extern.
#[no_mangle]
pub unsafe extern "C" fn wasm_extern_delete(_extern: Option<Box<wasm_extern_t>>) {}

#[no_mangle]
pub unsafe extern "C" fn wasm_func_as_extern(
func: Option<&wasm_func_t>,
Expand Down Expand Up @@ -125,3 +135,60 @@ pub unsafe extern "C" fn wasm_extern_as_table(
None
}
}

#[cfg(test)]
mod tests {
use inline_c::assert_c;

#[test]
fn test_extern_copy() {
(assert_c! {
#include "tests/wasmer_wasm.h"

int main() {
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);

wasm_byte_vec_t wat;
wasmer_byte_vec_new_from_string(
&wat,
"(module\n"
" (func (export \"function\")))"
);
wasm_byte_vec_t wasm;
wat2wasm(&wat, &wasm);

wasm_module_t* module = wasm_module_new(store, &wasm);
assert(module);

wasm_extern_vec_t imports = WASM_EMPTY_VEC;
wasm_trap_t* traps = NULL;

wasm_instance_t* instance = wasm_instance_new(store, module, &imports, &traps);
assert(instance);

wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);

assert(exports.size == 1);

wasm_extern_t* function = exports.data[0];
assert(wasm_extern_kind(function) == WASM_EXTERN_FUNC);

wasm_extern_t* function_copy = wasm_extern_copy(function);
assert(wasm_extern_kind(function_copy) == WASM_EXTERN_FUNC);

wasm_extern_delete(function_copy);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_byte_vec_delete(&wasm);
wasm_byte_vec_delete(&wat);
wasm_store_delete(store);
wasm_engine_delete(engine);

return 0;
}
})
.success();
}
}
16 changes: 2 additions & 14 deletions lib/c-api/src/wasm_c_api/types/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,8 @@ impl From<ExportType> for wasm_exporttype_t {

impl From<&ExportType> for wasm_exporttype_t {
fn from(other: &ExportType) -> Self {
let name = {
let mut heap_str: Box<str> = other.name().to_string().into_boxed_str();
let char_ptr = heap_str.as_mut_ptr();
let str_len = heap_str.bytes().len();
let name_inner = wasm_name_t {
size: str_len,
data: char_ptr,
};
Box::leak(heap_str);

Box::new(name_inner)
};

let extern_type = Box::new(other.ty().into());
let name: Box<wasm_name_t> = Box::new(other.name().to_string().into());
let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into());

wasm_exporttype_t { name, extern_type }
}
Expand Down
32 changes: 4 additions & 28 deletions lib/c-api/src/wasm_c_api/types/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,13 @@ impl From<ImportType> for wasm_importtype_t {

impl From<&ImportType> for wasm_importtype_t {
fn from(other: &ImportType) -> Self {
let module = {
let mut heap_str: Box<str> = other.module().to_string().into_boxed_str();
let char_ptr = heap_str.as_mut_ptr();
let str_len = heap_str.bytes().len();
let module_inner = wasm_name_t {
size: str_len,
data: char_ptr,
};
Box::leak(heap_str);

Box::new(module_inner)
};

let name = {
let mut heap_str: Box<str> = other.name().to_string().into_boxed_str();
let char_ptr = heap_str.as_mut_ptr();
let str_len = heap_str.bytes().len();
let name_inner = wasm_name_t {
size: str_len,
data: char_ptr,
};
Box::leak(heap_str);

Box::new(name_inner)
};

let extern_type = Box::new(other.ty().into());
let module: Box<wasm_name_t> = Box::new(other.module().to_string().into());
let name: Box<wasm_name_t> = Box::new(other.name().to_string().into());
let extern_type: Box<wasm_externtype_t> = Box::new(other.ty().into());

wasm_importtype_t {
name,
module,
name,
extern_type,
}
}
Expand Down
13 changes: 13 additions & 0 deletions lib/c-api/src/wasm_c_api/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ wasm_declare_vec!(byte);
#[allow(non_camel_case_types)]
pub type wasm_name_t = wasm_byte_vec_t;

impl From<String> for wasm_name_t {
fn from(string: String) -> Self {
let mut boxed_str: Box<str> = string.into_boxed_str();
let data = boxed_str.as_mut_ptr();
let size = boxed_str.bytes().len();
let wasm_name = Self { data, size };

Box::leak(boxed_str);

wasm_name
}
}

// opaque type over `ExternRef`?
#[allow(non_camel_case_types)]
pub struct wasm_ref_t;
Expand Down
Loading

0 comments on commit 1db4d5f

Please sign in to comment.