Merge pull request #6 from wasmerio/add-c-api

Add C API
This commit is contained in:
Mark McCaskey
2020-05-08 16:10:09 -07:00
committed by GitHub
141 changed files with 20357 additions and 40 deletions

View File

@@ -43,3 +43,13 @@ jobs:
- run: cargo test --release - run: cargo test --release
env: env:
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
- name: Build and Test C API
run: |
make capi
make test-capi-cranelift
if: matrix.os != 'windows-latest'
- name: Build C API on Windows
run: make capi
if: matrix.os == 'windows-latest'
env:
RUST_BACKTRACE: 1

60
Cargo.lock generated
View File

@@ -139,6 +139,23 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[package]]
name = "cbindgen"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15be43e426e5133330fffd63026e178e70ecc74df7a4a844a8ff6e6ffc2fcde"
dependencies = [
"clap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.50" version = "1.0.50"
@@ -604,6 +621,12 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -1123,6 +1146,12 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "ryu"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@@ -1217,6 +1246,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.4.0" version = "1.4.0"
@@ -1373,6 +1413,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.13" version = "0.1.13"
@@ -1662,6 +1711,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "wasmer-runtime-c-api"
version = "0.16.2"
dependencies = [
"cbindgen",
"lazy_static",
"libc",
"wasmer",
"wasmer-wasi",
]
[[package]] [[package]]
name = "wasmer-wasi" name = "wasmer-wasi"
version = "0.16.2" version = "0.16.2"

View File

@@ -42,6 +42,7 @@ bytesize = "1.0.0"
[workspace] [workspace]
members = [ members = [
"lib/c-api",
] ]
[build-dependencies] [build-dependencies]

View File

@@ -6,3 +6,32 @@ test:
doc: doc:
cargo doc --all-features --document-private-items cargo doc --all-features --document-private-items
capi-singlepass:
cargo build --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features singlepass-backend,wasi
capi-cranelift:
cargo build --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features cranelift-backend,wasi
capi-llvm:
cargo build --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features llvm-backend,wasi
# We use cranelift as the default backend for the capi for now
capi: capi-cranelift
test-capi-singlepass: capi-singlepass
cargo test --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features singlepass-backend,wasi
test-capi-cranelift: capi-cranelift
cargo test --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features cranelift-backend,wasi -- --nocapture --test-threads=1
test-capi-llvm: capi-llvm
cargo test --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features llvm-backend,wasi
test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-emscripten

View File

@@ -229,7 +229,9 @@ impl Table {
let item = init.into_checked_anyfunc(store)?; let item = init.into_checked_anyfunc(store)?;
let tunables = store.engine().tunables(); let tunables = store.engine().tunables();
let table_plan = tunables.table_plan(ty); let table_plan = tunables.table_plan(ty);
let table = tunables.create_table(table_plan).unwrap(); let table = tunables
.create_table(table_plan)
.map_err(RuntimeError::new)?;
let definition = table.vmtable(); let definition = table.vmtable();
for i in 0..definition.current_elements { for i in 0..definition.current_elements {
@@ -582,19 +584,30 @@ impl Function {
params: &[Val], params: &[Val],
results: &mut [Val], results: &mut [Val],
) -> Result<(), RuntimeError> { ) -> Result<(), RuntimeError> {
let format_types_for_error_message = |items: &[Val]| {
items
.iter()
.map(|param| param.ty().to_string())
.collect::<Vec<String>>()
.join(", ")
};
let signature = self.ty(); let signature = self.ty();
if signature.params().len() != params.len() { if signature.params().len() != params.len() {
return Err(RuntimeError::new(format!( return Err(RuntimeError::new(format!(
"expected {} arguments, got {}", "expected {} arguments, got {}: Parameters of type [{}] did not match signature {}",
signature.params().len(), signature.params().len(),
params.len() params.len(),
format_types_for_error_message(params),
&signature
))); )));
} }
if signature.results().len() != results.len() { if signature.results().len() != results.len() {
return Err(RuntimeError::new(format!( return Err(RuntimeError::new(format!(
"expected {} results, got {}", "expected {} results, got {}: Results of type [{}] did not match signature {}",
signature.results().len(), signature.results().len(),
results.len() results.len(),
format_types_for_error_message(results),
&signature,
))); )));
} }
@@ -604,7 +617,11 @@ impl Function {
let param_tys = signature.params().iter(); let param_tys = signature.params().iter();
for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) { for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) {
if arg.ty() != ty.clone() { if arg.ty() != ty.clone() {
return Err(RuntimeError::new("argument type mismatch")); let param_types = format_types_for_error_message(params);
return Err(RuntimeError::new(format!(
"Parameters of type [{}] did not match signature {}",
param_types, &signature,
)));
} }
unsafe { unsafe {
arg.write_value_to(slot); arg.write_value_to(slot);

View File

@@ -26,7 +26,7 @@ pub use crate::types::{
MemoryType, Mutability, TableType, Val, ValType, MemoryType, Mutability, TableType, Val, ValType,
}; };
pub use wasm_common::{ValueType, WasmExternType, WasmTypeList}; pub use wasm_common::{Bytes, Pages, ValueType, WasmExternType, WasmTypeList};
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
pub use wasmer_compiler::CompilerConfig; pub use wasmer_compiler::CompilerConfig;
pub use wasmer_compiler::{Features, Target}; pub use wasmer_compiler::{Features, Target};

View File

@@ -6,15 +6,15 @@ use wasmer_engine::Engine;
#[derive(Clone)] #[derive(Clone)]
pub struct Store { pub struct Store {
engine: Arc<dyn Engine>, engine: Arc<dyn Engine + Send + Sync>,
} }
impl Store { impl Store {
pub fn new(engine: Arc<dyn Engine>) -> Store { pub fn new(engine: Arc<dyn Engine + Send + Sync>) -> Store {
Store { engine } Store { engine }
} }
pub fn engine(&self) -> &Arc<dyn Engine> { pub fn engine(&self) -> &Arc<dyn Engine + Send + Sync> {
&self.engine &self.engine
} }
@@ -37,28 +37,30 @@ impl Default for Store {
// sure this function doesn't emit a compile error even if // sure this function doesn't emit a compile error even if
// more than one compiler is enabled. // more than one compiler is enabled.
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn get_config() -> Box<dyn CompilerConfig> { fn get_config() -> Arc<dyn CompilerConfig + Send + Sync> {
#[cfg(feature = "cranelift")] #[cfg(feature = "cranelift")]
return Box::new(wasmer_compiler_cranelift::CraneliftConfig::default()); return Arc::new(wasmer_compiler_cranelift::CraneliftConfig::default());
#[cfg(feature = "llvm")] #[cfg(feature = "llvm")]
return Box::new(wasmer_compiler_llvm::LLVMConfig::default()); return Arc::new(wasmer_compiler_llvm::LLVMConfig::default());
#[cfg(feature = "singlepass")] #[cfg(feature = "singlepass")]
return Box::new(wasmer_compiler_singlepass::SinglepassConfig::default()); return Arc::new(wasmer_compiler_singlepass::SinglepassConfig::default());
} }
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn get_engine(config: Box<dyn CompilerConfig>) -> Arc<dyn Engine> { fn get_engine(
config: Arc<dyn CompilerConfig + Send + Sync>,
) -> Arc<dyn Engine + Send + Sync> {
let tunables = Tunables::for_target(config.target().triple()); let tunables = Tunables::for_target(config.target().triple());
#[cfg(feature = "jit")] #[cfg(feature = "jit")]
return Arc::new(wasmer_engine_jit::JITEngine::new(&config, tunables)); return Arc::new(wasmer_engine_jit::JITEngine::new(&*config, tunables));
} }
let config = get_config(); let config = get_config();
let engine = get_engine(config); let engine = get_engine(config);
Store::new(config) Store::new(engine)
} }
} }

1
lib/c-api/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
doc/html/

52
lib/c-api/Cargo.toml Normal file
View File

@@ -0,0 +1,52 @@
[package]
name = "wasmer-runtime-c-api"
version = "0.16.2"
description = "Wasmer C API library"
documentation = "https://wasmerio.github.io/wasmer/c/runtime-c-api/"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
keywords = ["wasm", "webassembly", "runtime"]
categories = ["wasm"]
edition = "2018"
readme = "README.md"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
lazy_static = "1"
libc = "0.2.60"
# for generating code in the same way thot the wasm-c-api does
# Commented out for now until we can find a solution to the exported function problem
# wasmer-wasm-c-api = { version = "0.16.2", path = "crates/wasm-c-api" }
[dependencies.wasmer]
default-features = false
features = ["compiler", "engine", "jit", "cranelift"]
path = "../api"
version = "0.16.2"
[dependencies.wasmer-wasi]
default-features = false
path = "../wasi"
version = "0.16.2"
optional = true
#[dependencies.wasmer-emscripten]
#path = "../emscripten"
#version = "0.16.2"
#optional = true
[features]
default = ["cranelift-backend", "wasi"]
singlepass-backend = ["wasmer/singlepass"]
cranelift-backend = ["wasmer/cranelift"]
llvm-backend = ["wasmer/llvm"]
wasi = ["wasmer-wasi"]
#emscripten = ["wasmer-emscripten"]
# used to avoid generating standard Wasm C API types in our header files
ignore-wasm-c-api = []
[build-dependencies]
cbindgen = "0.14"

140
lib/c-api/README.md Normal file
View File

@@ -0,0 +1,140 @@
<p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/logo.png" alt="Wasmer logo">
</a>
</p>
<p align="center">
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a>
<a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
<a href="https://crates.io/crates/wasmer-runtime-c-api">
<img src="https://img.shields.io/crates/d/wasmer-runtime-c-api.svg?style=flat-square" alt="Number of downloads from crates.io">
</a>
<a href="https://wasmerio.github.io/wasmer/c/runtime-c-api/">
<img src="https://img.shields.io/badge/Docs-Wasmer%20C%20API-blue?style=flat-square" alt="Wasmer C API Documentation">
</a>
</p>
# Wasmer Runtime C API
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with WASI, Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer).
This crate exposes a C and a C++ API for the Wasmer runtime.
# Usage
The C and C++ header files can be found in the source tree of this
crate, respectively [`wasmer.h`][wasmer_h] and
[`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
up-to-date in this repository.
The runtime shared library (so, dll, dylib) can also be downloaded in Wasmer [release page](https://github.com/wasmerio/wasmer/releases).
You can find the full C API documentation here:
https://wasmerio.github.io/wasmer/c/runtime-c-api/
Here is a simple example to use the C API:
```c
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the Wasm file bytes.
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
// Prepare the imports.
wasmer_import_t imports[] = {};
// Instantiate!
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
assert(instantiation_result == WASMER_OK);
// Let's call a function.
// Start by preparing the arguments.
// Value of argument #1 is `7i32`.
wasmer_value_t argument_one;
argument_one.tag = WASM_I32;
argument_one.value.I32 = 7;
// Value of argument #2 is `8i32`.
wasmer_value_t argument_two;
argument_two.tag = WASM_I32;
argument_two.value.I32 = 8;
// Prepare the arguments.
wasmer_value_t arguments[] = {argument_one, argument_two};
// Prepare the return value.
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
// Call the `sum` function with the prepared arguments and the return value.
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1);
// Let's display the result.
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
// `sum(7, 8) == 15`.
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
wasmer_instance_destroy(instance);
return 0;
}
```
# Testing
Tests are run using the release build of the library. If you make
changes or compile with non-default features, please ensure you
rebuild in release mode for the tests to see the changes.
The tests can be run via `cargo test`, such as:
```sh
$ cargo test --release -- --nocapture
```
To run tests manually, enter the `lib/runtime-c-api/tests` directory
and run the following commands:
```sh
$ cmake .
$ make
$ make test
```
# License
Wasmer is primarily distributed under the terms of the [MIT
license][mit-license] ([LICENSE][license]).
[wasmer_h]: ./wasmer.h
[wasmer_hh]: ./wasmer.hh
[mit-license]: http://opensource.org/licenses/MIT
[license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE

106
lib/c-api/build.rs Normal file
View File

@@ -0,0 +1,106 @@
extern crate cbindgen;
use cbindgen::{Builder, Language};
use std::{env, fs, path::PathBuf};
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let mut crate_wasmer_header_file = PathBuf::from(&crate_dir);
crate_wasmer_header_file.push("wasmer");
let out_dir = env::var("OUT_DIR").unwrap();
let mut out_wasmer_header_file = PathBuf::from(&out_dir);
out_wasmer_header_file.push("wasmer");
let mut pre_header = r#"
#if !defined(WASMER_H_MACROS)
#define WASMER_H_MACROS
// Define the `ARCH_X86_X64` constant.
#if defined(MSVC) && defined(_M_AMD64)
# define ARCH_X86_64
#elif (defined(GCC) || defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__)
# define ARCH_X86_64
#endif
// Compatibility with non-Clang compilers.
#if !defined(__has_attribute)
# define __has_attribute(x) 0
#endif
// Compatibility with non-Clang compilers.
#if !defined(__has_declspec_attribute)
# define __has_declspec_attribute(x) 0
#endif
// Define the `DEPRECATED` macro.
#if defined(GCC) || defined(__GNUC__) || __has_attribute(deprecated)
# define DEPRECATED(message) __attribute__((deprecated(message)))
#elif defined(MSVC) || __has_declspec_attribute(deprecated)
# define DEPRECATED(message) __declspec(deprecated(message))
#endif
"#
.to_string();
#[cfg(feature = "wasi")]
{
pre_header += "#define WASMER_WASI_ENABLED\n";
}
#[cfg(feature = "emscripten")]
{
pre_header += "#define WASMER_EMSCRIPTEN_ENABLED\n";
}
// Close pre header.
pre_header += "#endif // WASMER_H_MACROS\n";
// Generate the C bindings in the `OUT_DIR`.
out_wasmer_header_file.set_extension("h");
Builder::new()
.with_crate(crate_dir.clone())
.with_language(Language::C)
.with_include_guard("WASMER_H")
.with_header(&pre_header)
.with_define("target_family", "windows", "_WIN32")
.with_define("target_arch", "x86_64", "ARCH_X86_64")
.with_define("feature", "wasi", "WASMER_WASI_ENABLED")
.with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED")
.generate()
.expect("Unable to generate C bindings")
.write_to_file(out_wasmer_header_file.as_path());
// Generate the C++ bindings in the `OUT_DIR`.
out_wasmer_header_file.set_extension("hh");
Builder::new()
.with_crate(crate_dir)
.with_language(Language::Cxx)
.with_include_guard("WASMER_H")
.with_header(&pre_header)
.with_define("target_family", "windows", "_WIN32")
.with_define("target_arch", "x86_64", "ARCH_X86_64")
.with_define("feature", "wasi", "WASMER_WASI_ENABLED")
.with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED")
.generate()
.expect("Unable to generate C++ bindings")
.write_to_file(out_wasmer_header_file.as_path());
// Copy the generated C bindings from `OUT_DIR` to
// `CARGO_MANIFEST_DIR`.
crate_wasmer_header_file.set_extension("h");
out_wasmer_header_file.set_extension("h");
fs::copy(
out_wasmer_header_file.as_path(),
crate_wasmer_header_file.as_path(),
)
.expect("Unable to copy the generated C bindings");
// Copy the generated C++ bindings from `OUT_DIR` to
// `CARGO_MANIFEST_DIR`.
crate_wasmer_header_file.set_extension("hh");
out_wasmer_header_file.set_extension("hh");
fs::copy(out_wasmer_header_file, crate_wasmer_header_file)
.expect("Unable to copy the generated C++ bindings");
}

View File

@@ -0,0 +1,14 @@
[package]
name = "wasmer-wasm-c-api"
version = "0.16.2"
description = "The standard Wasm C API for Wasmer"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib", "staticlib"]
[dependencies]
paste = "0.1"
wasmer = { version = "0.16.2", path = "../../../api", default-features = false }

View File

@@ -0,0 +1 @@

107
lib/c-api/doc/index.md Normal file
View File

@@ -0,0 +1,107 @@
# Wasmer Runtime C API
[Wasmer] is a standalone WebAssembly runtime for running WebAssembly [outside of the browser](https://webassembly.org/docs/non-web/), supporting [WASI](https://github.com/WebAssembly/WASI) and [Emscripten](https://emscripten.org/).
The Wasmer Runtime C API exposes a C and a C++ API to interact
with the Wasmer Runtime, so you can use WebAssembly anywhere.
[Wasmer]: https://github.com/wasmerio/wasmer
[WebAssembly]: https://webassembly.org/
# Usage
Since the Wasmer runtime is written in Rust, the C and C++ API are
designed to work hand-in-hand with its shared library. The C and C++
header files, namely [`wasmer.h`][wasmer_h] and `wasmer.hh` are documented
in the docs.
Their source code can be found in the source tree of the [wasmer-runtime-c-api](https://github.com/wasmerio/wasmer/tree/master/lib/runtime-c-api)
crate.
The C and C++ header files along with the runtime shared
libraries (`.so`, `.dylib`, `.dll`) can also be downloaded in the
Wasmer [release page].
[release page]: https://github.com/wasmerio/wasmer/releases
Here is a simple example to use the C API:
```c
#include <stdio.h>
#include "wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the Wasm file bytes.
FILE *file = fopen("sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
// Prepare the imports.
wasmer_import_t imports[] = {};
// Instantiate!
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
assert(instantiation_result == WASMER_OK);
// Let's call a function.
// Start by preparing the arguments.
// Value of argument #1 is `7i32`.
wasmer_value_t argument_one;
argument_one.tag = WASM_I32;
argument_one.value.I32 = 7;
// Value of argument #2 is `8i32`.
wasmer_value_t argument_two;
argument_two.tag = WASM_I32;
argument_two.value.I32 = 8;
// Prepare the arguments.
wasmer_value_t arguments[] = {argument_one, argument_two};
// Prepare the return value.
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
// Call the `sum` function with the prepared arguments and the return value.
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1);
// Let's display the result.
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
// `sum(7, 8) == 15`.
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
wasmer_instance_destroy(instance);
return 0;
}
```
# Examples
You can check more examples of how to use the Wasmer C API here:
https://docs.wasmer.io/integrations/c/examples
# License
Wasmer is primarily distributed under the terms of the [MIT
license][mit-license] ([LICENSE][license]).
[wasmer_h]: https://wasmerio.github.io/wasmer/c/runtime-c-api/wasmer_8h.html
[mit-license]: http://opensource.org/licenses/MIT
[license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE

194
lib/c-api/doc/theme/css/wasmer.css vendored Normal file
View File

@@ -0,0 +1,194 @@
:root {
--primary-color: hsl(261.9, 61.9%, 16.5%);
--primary-negative-color: hsl(0, 0%, 100%);
--primary-dark-color: hsl(261.9, 61.9%, 16.5%);
--primary-light-color: hsl(0, 0%, 96.1%);
--secondary-color: hsl(241.2, 68.9%, 57.1%);
--secondary-light-color: hsl(241.2, 68.9%, 60%);
--white-color: hsl(0, 0%, 100%);
}
body {
overflow-y: hidden;
}
[role="header"] {
display: grid;
grid-template-areas: "a b";
margin-bottom: 1rem;
padding: .5rem 2vw;
}
[role="header"] > * {
margin: 0;
padding: 0;
}
[role="header"] > img {
grid-area: a;
max-width: 30vw;
max-height: 15vh;
}
[role="header"] > div {
grid-area: b;
text-align: right;
}
[role="footer"] {
position: fixed;
bottom: 0;
text-align: end;
background: var(--white-color);
}
#MSearchBox {
display: none;
}
#side-nav {
margin: 0;
padding-bottom: 3rem;
}
#side-nav .ui-resizable-e {
background: radial-gradient(var(--secondary-light-color), var(--white-color) 60%);
}
#nav-tree {
background: none;
}
#nav-tree .selected {
text-shadow: none;
color: var(--primary-negative-color);
background: var(--secondary-color);
}
#nav-sync {
display: none;
}
#doc-content > .header {
border: 0;
background: none;
}
#doc-content > .header > .summary {
font-size: small;
}
body, div, p, ul, li {
color: var(--primary-color);
}
#doc-content .contents p,
#doc-content .contents ul,
#doc-content .contents div:not(.line),
#doc-content .contents table.memberdecls {
font-family: serif;
font-size: 1.1rem;
}
a, .contents a, .contents a:visited {
color: var(--secondary-color);
}
h1, .headertitle > .title {
color: var(--primary-color);
font-size: 2.5rem;
line-height: 2.7rem;
}
h2, .memberdecls h2, h2.groupheader {
color: var(--primary-color);
font-size: 2rem;
line-height: 2.5rem;
}
h2.groupheader {
border-color: var(--secondary-color);
border-bottom-style: dotted;
margin-bottom: 1rem !important;
}
h3, h2.memtitle {
font-size: 1.5rem;
line-height: 2rem;
}
.memberdecls [class^="separator"] {
display: none;
}
.memberdecls [class^="memitem"] > td,
.memberdecls [class^="memdesc"] > td {
background: none;
}
.memtitle {
margin: 0;
padding: 0;
border: none;
background: none;
}
.memitem {
margin-left: 1rem;
width: calc(100% - 1rem);
}
.memtitle {
font-weight: bold;
font-family: monospace;
}
.memtitle ~ .memtitle {
margin-top: 1.5rem;
}
.memitem .memproto,
.memitem .memproto .memname,
.memitem .memdoc,
.memitem .memdoc .definition {
margin: 0;
padding: 0;
box-shadow: none;
border: none;
background: none;
}
.memitem .memproto {
padding-left: 1rem;
margin: .5rem 0;
background: var(--primary-light-color) !important;
}
.memproto table.memname {
font-family: monospace;
font-size: .85rem;
}
table.fieldtable {
border-radius: 0;
box-shadow: none;
}
table.fieldtable th {
color: var(--primary-negative-color);
border-radius: 0;
background: var(--secondary-color);
}
table.fieldtable,
table.fieldtable td {
border-color: var(--secondary-color) !important;
}
div.fragment {
padding: 1rem;
}
div.fragment div.line {
line-height: 1.15rem;
}

10
lib/c-api/doc/theme/footer.html vendored Normal file
View File

@@ -0,0 +1,10 @@
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath" role="footer">
$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a>
$doxygenversion
</div>
<!--END GENERATE_TREEVIEW-->
</body>
</html>

53
lib/c-api/doc/theme/header.html vendored Normal file
View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="content-type" content="text/javascript; charset=utf-8" />
<meta http-equiv="content-type" content="text/css; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0" />
<meta name="generator" content="Doxygen $doxygenversion"/>
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top" role="header">
<!--BEGIN TITLEAREA-->
<!--BEGIN PROJECT_LOGO-->
<img alt="Wasmer's logo" src="$relpath^$projectlogo" />
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<div>
<h1>
$projectname
<!--BEGIN PROJECT_NUMBER--><span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
<!--BEGIN PROJECT_BRIEF-->
<span id="projectbrief">$projectbrief</span>
<!--END PROJECT_BRIEF-->
</h1>
<br />
<small>
<a href="https://github.com/wasmerio/wasmer"><img src="https://img.shields.io/github/stars/wasmerio/wasmer?style=social" alt="Visit the repository" /></a>
<a href="https://github.com/wasmerio/wasmer"><img src="https://img.shields.io/github/license/wasmerio/wasmer" alt="License" /></a>
<a href="https://spectrum.chat/wasmer"><img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"></a>
</small>
</div>
<!--END PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<div id="searchbox">$searchbox</div>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
<!--END TITLEAREA-->

338
lib/c-api/doxyfile Normal file
View File

@@ -0,0 +1,338 @@
# Doxyfile 1.8.17
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "wasmer-runtime-c-api"
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO = ../../assets/logo.png
OUTPUT_DIRECTORY = doc/
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
OUTPUT_TEXT_DIRECTION = None
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
JAVADOC_BANNER = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING = dox=md
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = doc/index.md
INPUT += wasmer.h
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.hh
RECURSIVE = NO
EXCLUDE = doc
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = doc/index.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER = doc/theme/header.html
HTML_FOOTER = doc/theme/footer.html
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET = doc/theme/css/wasmer.css
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = YES
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = YES
GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
FORMULA_MACROFILE =
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME =
MAKEINDEX_CMD_NAME = makeindex
LATEX_MAKEINDEX_CMD = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = YES
DOT_PATH = ${DOXYGEN_DOT_PATH}
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

113
lib/c-api/src/error.rs Normal file
View File

@@ -0,0 +1,113 @@
//! Read runtime errors.
use libc::{c_char, c_int};
use std::{
cell::RefCell,
error::Error,
fmt::{self, Display, Formatter},
ptr, slice,
};
thread_local! {
static LAST_ERROR: RefCell<Option<Box<dyn Error>>> = RefCell::new(None);
}
pub fn update_last_error<E: Error + 'static>(err: E) {
LAST_ERROR.with(|prev| {
*prev.borrow_mut() = Some(Box::new(err));
});
}
/// Retrieve the most recent error, clearing it in the process.
pub(crate) fn take_last_error() -> Option<Box<dyn Error>> {
LAST_ERROR.with(|prev| prev.borrow_mut().take())
}
/// Gets the length in bytes of the last error if any.
///
/// This can be used to dynamically allocate a buffer with the correct number of
/// bytes needed to store a message.
///
/// See `wasmer_last_error_message()` to get a full example.
#[no_mangle]
pub extern "C" fn wasmer_last_error_length() -> c_int {
LAST_ERROR.with(|prev| match *prev.borrow() {
Some(ref err) => err.to_string().len() as c_int + 1,
None => 0,
})
}
/// Gets the last error message if any into the provided buffer
/// `buffer` up to the given `length`.
///
/// The `length` parameter must be large enough to store the last
/// error message. Ideally, the value should come from
/// `wasmer_last_error_length()`.
///
/// The function returns the length of the string in bytes, `-1` if an
/// error occurs. Potential errors are:
///
/// * The buffer is a null pointer,
/// * The buffer is too smal to hold the error message.
///
/// Note: The error message always has a trailing null character.
///
/// Example:
///
/// ```c
/// int error_length = wasmer_last_error_length();
///
/// if (error_length > 0) {
/// char *error_message = malloc(error_length);
/// wasmer_last_error_message(error_message, error_length);
/// printf("Error message: `%s`\n", error_message);
/// } else {
/// printf("No error message\n");
/// }
/// ```
#[no_mangle]
pub unsafe extern "C" fn wasmer_last_error_message(buffer: *mut c_char, length: c_int) -> c_int {
if buffer.is_null() {
// buffer pointer is null
return -1;
}
let error_message = match take_last_error() {
Some(err) => err.to_string(),
None => return 0,
};
let length = length as usize;
if error_message.len() >= length {
// buffer is too small to hold the error message
return -1;
}
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length);
ptr::copy_nonoverlapping(
error_message.as_ptr(),
buffer.as_mut_ptr(),
error_message.len(),
);
// Add a trailing null so people using the string as a `char *` don't
// accidentally read into garbage.
buffer[error_message.len()] = 0;
error_message.len() as c_int + 1
}
#[derive(Debug)]
pub struct CApiError {
pub msg: String,
}
impl Display for CApiError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", &self.msg)
}
}
impl Error for CApiError {}

573
lib/c-api/src/export.rs Normal file
View File

@@ -0,0 +1,573 @@
//! Create, read, destroy export definitions (function, global, memory
//! and table) on an instance.
use crate::{
error::{update_last_error, CApiError},
global::wasmer_global_t,
import::wasmer_import_func_t,
memory::wasmer_memory_t,
module::wasmer_module_t,
table::wasmer_table_t,
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
wasmer_byte_array, wasmer_result_t,
};
use libc::{c_int, c_uint};
use std::ptr::{self, NonNull};
use std::slice;
use wasmer::{ExportType, ExternType, Function, ImportType, Instance, Memory, Module, Val};
/// Intermediate representation of an `Export` instance that is
/// exposed to C.
pub(crate) struct NamedExport {
/// The export type and name.
pub(crate) export_type: ExportType,
/// The instance that holds the export.
pub(crate) instance: *mut Instance,
}
/// Opaque pointer to `ImportType`.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_export_t;
/// Opaque pointer to `wasmer_export_t`.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_export_func_t;
/// Intermediate representation of a vector of `NamedExport` that is
/// exposed to C.
pub(crate) struct NamedExports(pub Vec<NamedExport>);
/// Opaque pointer to the opaque structure `crate::NamedExports`,
/// which is a wrapper around a vector of the opaque structure
/// `crate::NamedExport`.
///
/// Check the `wasmer_instance_exports()` function to learn more.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_exports_t;
/// Intermediate representation of an export descriptor that is
/// exposed to C.
pub(crate) struct NamedExportDescriptor {
/// The export name.
name: String,
/// The export kind.
kind: wasmer_import_export_kind,
}
/// Opaque pointer to `NamedExportDescriptor`.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_export_descriptor_t;
/// Intermediate representation of a vector of `NamedExportDescriptor`
/// that is exposed to C.
pub struct NamedExportDescriptors(Vec<NamedExportDescriptor>);
/// Opaque pointer to `NamedExportDescriptors`.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_export_descriptors_t;
/// Union of import/export value.
#[repr(C)]
#[derive(Clone, Copy)]
pub union wasmer_import_export_value {
pub func: *const wasmer_import_func_t,
pub table: *const wasmer_table_t,
pub memory: *const wasmer_memory_t,
pub global: *const wasmer_global_t,
}
/// List of export/import kinds.
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Clone, PartialEq, Eq)]
// ================
// ! DANGER !
// ================
// Do not modify these values without updating the `TryFrom` implementation below
pub enum wasmer_import_export_kind {
/// The export/import is a function.
WASM_FUNCTION = 0,
/// The export/import is a global.
WASM_GLOBAL = 1,
/// The export/import is a memory.
WASM_MEMORY = 2,
/// The export/import is a table.
WASM_TABLE = 3,
}
impl wasmer_import_export_kind {
pub fn to_str(&self) -> &'static str {
match self {
Self::WASM_FUNCTION => "function",
Self::WASM_GLOBAL => "global",
Self::WASM_MEMORY => "memory",
Self::WASM_TABLE => "table",
}
}
}
impl std::convert::TryFrom<u32> for wasmer_import_export_kind {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::WASM_FUNCTION,
1 => Self::WASM_GLOBAL,
2 => Self::WASM_MEMORY,
3 => Self::WASM_TABLE,
_ => return Err(()),
})
}
}
/// Gets export descriptors for the given module
///
/// The caller owns the object and should call `wasmer_export_descriptors_destroy` to free it.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_export_descriptors(
module: *const wasmer_module_t,
export_descriptors: *mut *mut wasmer_export_descriptors_t,
) {
let module = &*(module as *const Module);
let named_export_descriptors: Box<NamedExportDescriptors> = Box::new(NamedExportDescriptors(
module.exports().into_iter().map(|e| e.into()).collect(),
));
*export_descriptors =
Box::into_raw(named_export_descriptors) as *mut wasmer_export_descriptors_t;
}
/// Frees the memory for the given export descriptors
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_export_descriptors_destroy(
export_descriptors: *mut wasmer_export_descriptors_t,
) {
if !export_descriptors.is_null() {
unsafe { Box::from_raw(export_descriptors as *mut NamedExportDescriptors) };
}
}
/// Gets the length of the export descriptors
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_export_descriptors_len(
exports: *mut wasmer_export_descriptors_t,
) -> c_int {
if exports.is_null() {
return 0;
}
(*(exports as *mut NamedExportDescriptors)).0.len() as c_int
}
/// Gets export descriptor by index
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_export_descriptors_get(
export_descriptors: *mut wasmer_export_descriptors_t,
idx: c_int,
) -> *mut wasmer_export_descriptor_t {
if export_descriptors.is_null() {
return ptr::null_mut();
}
let named_export_descriptors = &mut *(export_descriptors as *mut NamedExportDescriptors);
&mut (*named_export_descriptors).0[idx as usize] as *mut NamedExportDescriptor
as *mut wasmer_export_descriptor_t
}
/// Gets name for the export descriptor
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_descriptor_name(
export_descriptor: *mut wasmer_export_descriptor_t,
) -> wasmer_byte_array {
let named_export_descriptor = &*(export_descriptor as *mut NamedExportDescriptor);
wasmer_byte_array {
bytes: named_export_descriptor.name.as_ptr(),
bytes_len: named_export_descriptor.name.len() as u32,
}
}
/// Gets export descriptor kind
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_descriptor_kind(
export: *mut wasmer_export_descriptor_t,
) -> wasmer_import_export_kind {
let named_export_descriptor = &*(export as *mut NamedExportDescriptor);
named_export_descriptor.kind.clone()
}
/// Frees the memory for the given exports.
///
/// Check the `wasmer_instance_exports()` function to get a complete
/// example.
///
/// If `exports` is a null pointer, this function does nothing.
///
/// Example:
///
/// ```c
/// // Get some exports.
/// wasmer_exports_t *exports = NULL;
/// wasmer_instance_exports(instance, &exports);
///
/// // Destroy the exports.
/// wasmer_exports_destroy(exports);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_exports_destroy(exports: *mut wasmer_exports_t) {
if !exports.is_null() {
unsafe { Box::from_raw(exports as *mut NamedExports) };
}
}
/// Gets the length of the exports
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_exports_len(exports: Option<NonNull<wasmer_exports_t>>) -> c_int {
if let Some(exports_inner) = exports {
exports_inner.cast::<NamedExports>().as_ref().0.len() as c_int
} else {
0
}
}
/// Gets wasmer_export by index
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_exports_get(
exports: Option<NonNull<wasmer_exports_t>>,
idx: c_int,
) -> Option<NonNull<wasmer_export_t>> {
let named_exports = &mut *(exports?.as_ptr() as *mut NamedExports);
Some(NonNull::new_unchecked(
&mut (*named_exports).0[idx as usize] as *mut NamedExport as *mut wasmer_export_t,
))
}
/// Gets wasmer_export kind
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_kind(
export: *mut wasmer_export_t,
) -> wasmer_import_export_kind {
let named_export = &*(export as *mut NamedExport);
(&named_export.export_type).into()
}
/// Sets the result parameter to the arity of the params of the wasmer_export_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_func_params_arity(
func: *const wasmer_export_func_t,
result: *mut u32,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export_type.ty();
if let ExternType::Function(ref signature) = *export {
*result = signature.params().len() as u32;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_export_func_params_arity".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
/// Sets the params buffer to the parameter types of the given wasmer_export_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_func_params(
func: *const wasmer_export_func_t,
params: *mut wasmer_value_tag,
params_len: u32,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export_type.ty();
if let ExternType::Function(ref signature) = *export {
let params: &mut [wasmer_value_tag] =
slice::from_raw_parts_mut(params, params_len as usize);
for (i, item) in signature.params().iter().enumerate() {
params[i] = item.into();
}
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_export_func_params".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
/// Sets the returns buffer to the parameter types of the given wasmer_export_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_func_returns(
func: *const wasmer_export_func_t,
returns: *mut wasmer_value_tag,
returns_len: u32,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export_type.ty();
if let ExternType::Function(ref signature) = *export {
let returns: &mut [wasmer_value_tag] =
slice::from_raw_parts_mut(returns, returns_len as usize);
for (i, item) in signature.results().iter().enumerate() {
returns[i] = item.into();
}
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_export_func_returns".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
/// Sets the result parameter to the arity of the returns of the wasmer_export_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_func_returns_arity(
func: *const wasmer_export_func_t,
result: *mut u32,
) -> wasmer_result_t {
let named_export = &*(func as *const NamedExport);
let export = &named_export.export_type.ty();
if let ExternType::Function(ref signature) = *export {
*result = signature.results().len() as u32;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_export_func_results_arity".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
/// Gets export func from export
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_to_func(
export: *const wasmer_export_t,
) -> *const wasmer_export_func_t {
export as *const wasmer_export_func_t
}
/// Gets a memory pointer from an export pointer.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_to_memory(
export: *const wasmer_export_t,
memory: *mut *mut wasmer_memory_t,
) -> wasmer_result_t {
let named_export = &*(export as *const NamedExport);
let instance = &*named_export.instance;
if let Ok(exported_memory) = instance
.exports
.get::<Memory>(&named_export.export_type.name())
{
let mem = Box::new(exported_memory.clone());
*memory = Box::into_raw(mem) as *mut wasmer_memory_t;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "cannot cast the `wasmer_export_t` pointer to a `wasmer_memory_t` \
pointer because it does not represent a memory export."
.to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
/// Gets name from wasmer_export
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_export_name(export: *mut wasmer_export_t) -> wasmer_byte_array {
let named_export = &*(export as *mut NamedExport);
wasmer_byte_array {
bytes: named_export.export_type.name().as_ptr(),
bytes_len: named_export.export_type.name().len() as u32,
}
}
/// Calls a `func` with the provided parameters.
/// Results are set using the provided `results` pointer.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_export_func_call(
func: *const wasmer_export_func_t,
params: *const wasmer_value_t,
params_len: c_uint,
results: *mut wasmer_value_t,
results_len: c_uint,
) -> wasmer_result_t {
if func.is_null() {
update_last_error(CApiError {
msg: "func ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
if params_len > 0 && params.is_null() {
update_last_error(CApiError {
msg: "params ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let params: Vec<Val> = {
if params_len == 0 {
vec![]
} else {
slice::from_raw_parts::<wasmer_value_t>(params, params_len as usize)
.iter()
.cloned()
.map(|x| x.into())
.collect()
}
};
let named_export = &*(func as *mut NamedExport);
let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize);
let instance = &*named_export.instance;
let f: &Function = match instance.exports.get(&named_export.export_type.name()) {
Ok(f) => f,
Err(err) => {
update_last_error(err);
return wasmer_result_t::WASMER_ERROR;
}
};
let result = f.call(&params[..]);
match result {
Ok(results_vec) => {
if !results_vec.is_empty() {
let ret = match results_vec[0] {
Val::I32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I32,
value: wasmer_value { I32: x },
},
Val::I64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I64,
value: wasmer_value { I64: x },
},
Val::F32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F32,
value: wasmer_value { F32: x },
},
Val::F64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Val::V128(_) => unimplemented!("returning V128 type"),
Val::AnyRef(_) => unimplemented!("returning AnyRef type"),
Val::FuncRef(_) => unimplemented!("returning FuncRef type"),
};
results[0] = ret;
}
wasmer_result_t::WASMER_OK
}
Err(err) => {
update_last_error(err);
wasmer_result_t::WASMER_ERROR
}
}
}
impl From<ExportType> for NamedExportDescriptor {
fn from(et: ExportType) -> Self {
NamedExportDescriptor {
name: et.name().to_string(),
kind: et.into(),
}
}
}
impl From<&ImportType> for wasmer_import_export_kind {
fn from(it: &ImportType) -> Self {
it.ty().into()
}
}
impl From<ImportType> for wasmer_import_export_kind {
fn from(it: ImportType) -> Self {
(&it).into()
}
}
impl From<&ExportType> for wasmer_import_export_kind {
fn from(et: &ExportType) -> Self {
et.ty().into()
}
}
impl From<ExportType> for wasmer_import_export_kind {
fn from(et: ExportType) -> Self {
(&et).into()
}
}
impl From<&ExternType> for wasmer_import_export_kind {
fn from(et: &ExternType) -> Self {
match et {
ExternType::Memory(_) => wasmer_import_export_kind::WASM_MEMORY,
ExternType::Global(_) => wasmer_import_export_kind::WASM_GLOBAL,
ExternType::Table(_) => wasmer_import_export_kind::WASM_TABLE,
ExternType::Function(_) => wasmer_import_export_kind::WASM_FUNCTION,
}
}
}
impl From<ExternType> for wasmer_import_export_kind {
fn from(et: ExternType) -> Self {
(&et).into()
}
}

71
lib/c-api/src/global.rs Normal file
View File

@@ -0,0 +1,71 @@
//! Create, set, get and destroy global variables of an instance.
use crate::value::{wasmer_value_t, wasmer_value_tag};
use wasmer::Global;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_global_descriptor_t {
mutable: bool,
kind: wasmer_value_tag,
}
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_global_t;
/// Creates a new Global and returns a pointer to it.
/// The caller owns the object and should call `wasmer_global_destroy` to free it.
#[no_mangle]
pub unsafe extern "C" fn wasmer_global_new(
value: wasmer_value_t,
mutable: bool,
) -> *mut wasmer_global_t {
let store = crate::get_global_store();
let global = if mutable {
Global::new_mut(store, value.into())
} else {
Global::new(store, value.into())
};
Box::into_raw(Box::new(global)) as *mut wasmer_global_t
}
/// Gets the value stored by the given Global
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_global_get(global: *mut wasmer_global_t) -> wasmer_value_t {
let global = unsafe { &*(global as *mut Global) };
let value: wasmer_value_t = global.get().into();
value
}
/// Sets the value stored by the given Global
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_global_set(global: *mut wasmer_global_t, value: wasmer_value_t) {
let global = unsafe { &*(global as *mut Global) };
global.set(value.into());
}
/// Returns a descriptor (type, mutability) of the given Global
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_global_get_descriptor(
global: *mut wasmer_global_t,
) -> wasmer_global_descriptor_t {
let global = unsafe { &*(global as *mut Global) };
let descriptor = global.ty();
wasmer_global_descriptor_t {
mutable: descriptor.mutability.into(),
kind: descriptor.ty.into(),
}
}
/// Frees memory for the given Global
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_global_destroy(global: *mut wasmer_global_t) {
if !global.is_null() {
unsafe { Box::from_raw(global as *mut Global) };
}
}

View File

@@ -0,0 +1,145 @@
//! Functions and types for dealing with Emscripten imports
use super::*;
use crate::{get_slice_checked, instance::wasmer_instance_t, module::wasmer_module_t};
use std::ptr;
use wasmer::wasm::{Instance, Module};
use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals};
/// Type used to construct an import_object_t with Emscripten imports.
#[repr(C)]
pub struct wasmer_emscripten_globals_t;
/// Create a `wasmer_emscripten_globals_t` from a Wasm module.
#[no_mangle]
pub unsafe extern "C" fn wasmer_emscripten_get_globals(
module: *const wasmer_module_t,
) -> *mut wasmer_emscripten_globals_t {
if module.is_null() {
return ptr::null_mut();
}
let module = &*(module as *const Module);
match EmscriptenGlobals::new(module) {
Ok(globals) => Box::into_raw(Box::new(globals)) as *mut wasmer_emscripten_globals_t,
Err(msg) => {
update_last_error(CApiError { msg });
return ptr::null_mut();
}
}
}
/// Destroy `wasmer_emscrpten_globals_t` created by
/// `wasmer_emscripten_get_emscripten_globals`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_emscripten_destroy_globals(
globals: *mut wasmer_emscripten_globals_t,
) {
if globals.is_null() {
return;
}
let _ = Box::from_raw(globals);
}
/// Execute global constructors (required if the module is compiled from C++)
/// and sets up the internal environment.
///
/// This function sets the data pointer in the same way that
/// [`wasmer_instance_context_data_set`] does.
#[no_mangle]
pub unsafe extern "C" fn wasmer_emscripten_set_up(
instance: *mut wasmer_instance_t,
globals: *mut wasmer_emscripten_globals_t,
) -> wasmer_result_t {
if globals.is_null() || instance.is_null() {
return wasmer_result_t::WASMER_ERROR;
}
let instance = &mut *(instance as *mut Instance);
let globals = &*(globals as *mut EmscriptenGlobals);
let em_data = Box::into_raw(Box::new(EmscriptenData::new(
instance,
&globals.data,
Default::default(),
))) as *mut c_void;
instance.context_mut().data = em_data;
match wasmer_emscripten::set_up_emscripten(instance) {
Ok(_) => wasmer_result_t::WASMER_OK,
Err(e) => {
update_last_error(e);
wasmer_result_t::WASMER_ERROR
}
}
}
/// Convenience function for setting up arguments and calling the Emscripten
/// main function.
///
/// WARNING:
///
/// Do not call this function on untrusted code when operating without
/// additional sandboxing in place.
/// Emscripten has access to many host system calls and therefore may do very
/// bad things.
#[no_mangle]
pub unsafe extern "C" fn wasmer_emscripten_call_main(
instance: *mut wasmer_instance_t,
args: *const wasmer_byte_array,
args_len: c_uint,
) -> wasmer_result_t {
if instance.is_null() || args.is_null() {
return wasmer_result_t::WASMER_ERROR;
}
let instance = &mut *(instance as *mut Instance);
let arg_list = get_slice_checked(args, args_len as usize);
let arg_process_result: Result<Vec<&str>, _> =
arg_list.iter().map(|arg| arg.as_str()).collect();
let arg_vec = match arg_process_result.as_ref() {
Ok(arg_vec) => arg_vec,
Err(err) => {
update_last_error(*err);
return wasmer_result_t::WASMER_ERROR;
}
};
let prog_name = if let Some(prog_name) = arg_vec.first() {
prog_name
} else {
update_last_error(CApiError {
msg: "First argument (program name) is required to execute Emscripten's main function"
.to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
match wasmer_emscripten::emscripten_call_main(instance, prog_name, &arg_vec[1..]) {
Ok(_) => wasmer_result_t::WASMER_OK,
Err(e) => {
update_last_error(e);
wasmer_result_t::WASMER_ERROR
}
}
}
/// Create a `wasmer_import_object_t` with Emscripten imports, use
/// `wasmer_emscripten_get_emscripten_globals` to get a
/// `wasmer_emscripten_globals_t` from a `wasmer_module_t`.
///
/// WARNING:
///
/// This `import_object_t` contains thin-wrappers around host system calls.
/// Do not use this to execute untrusted code without additional sandboxing.
#[no_mangle]
pub unsafe extern "C" fn wasmer_emscripten_generate_import_object(
globals: *mut wasmer_emscripten_globals_t,
) -> *mut wasmer_import_object_t {
if globals.is_null() {
return ptr::null_mut();
}
// TODO: figure out if we should be using UnsafeCell here or something
let g = &mut *(globals as *mut EmscriptenGlobals);
let import_object = Box::new(wasmer_emscripten::generate_emscripten_env(g));
Box::into_raw(import_object) as *mut wasmer_import_object_t
}

769
lib/c-api/src/import/mod.rs Normal file
View File

@@ -0,0 +1,769 @@
//! Create, read, destroy import definitions (function, global, memory
//! and table) on an instance.
use crate::{
error::{update_last_error, CApiError},
export::{wasmer_import_export_kind, wasmer_import_export_value},
instance::wasmer_instance_context_t,
module::wasmer_module_t,
value::wasmer_value_tag,
wasmer_byte_array, wasmer_result_t,
};
use libc::c_uint;
use std::{
//convert::TryFrom,
ffi::c_void,
os::raw::c_char,
ptr,
slice,
//sync::Arc,
};
use wasmer::{
Function, Global, ImportObject, ImportObjectIterator, ImportType, Memory, Module, Table,
};
//use wasmer::wasm::{Export, FuncSig, Global, Memory, Module, Table, Type};
/*use wasmer_runtime_core::{
export::{Context, FuncPointer},
module::ImportName,
};*/
#[repr(C)]
pub struct wasmer_import_t {
pub module_name: wasmer_byte_array,
pub import_name: wasmer_byte_array,
pub tag: wasmer_import_export_kind,
pub value: wasmer_import_export_value,
}
#[repr(C)]
pub struct wasmer_import_object_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_func_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_descriptor_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_descriptors_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_import_object_iter_t;
/// Creates a new empty import object.
/// See also `wasmer_import_object_append`
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_new() -> *mut wasmer_import_object_t {
let import_object = Box::new(ImportObject::new());
Box::into_raw(import_object) as *mut wasmer_import_object_t
}
#[cfg(feature = "wasi")]
mod wasi;
#[cfg(feature = "wasi")]
pub use self::wasi::*;
#[cfg(feature = "emscripten")]
mod emscripten;
#[cfg(feature = "emscripten")]
pub use self::emscripten::*;
/// Gets an entry from an ImportObject at the name and namespace.
/// Stores `name`, `namespace`, and `import_export_value` in `import`.
/// Thus these must remain valid for the lifetime of `import`.
///
/// The caller owns all data involved.
/// `import_export_value` will be written to based on `tag`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_get_import(
import_object: *const wasmer_import_object_t,
namespace: wasmer_byte_array,
name: wasmer_byte_array,
import: *mut wasmer_import_t,
import_export_value: *mut wasmer_import_export_value,
tag: u32,
) -> wasmer_result_t {
todo!("Disabled until ImportObject APIs are updated")
/*
let tag: wasmer_import_export_kind = if let Ok(t) = TryFrom::try_from(tag) {
t
} else {
update_last_error(CApiError {
msg: "wasmer_import_export_tag out of range".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
let namespace_str = if let Ok(ns) = namespace.as_str() {
ns
} else {
update_last_error(CApiError {
msg: "error converting namespace to UTF-8 string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let name_str = if let Ok(name) = name.as_str() {
name
} else {
update_last_error(CApiError {
msg: "error converting name to UTF-8 string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
if import.is_null() || import_export_value.is_null() {
update_last_error(CApiError {
msg: "pointers to import and import_export_value must not be null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let import_out = &mut *import;
let import_export_value_out = &mut *import_export_value;
if let Some(export) = import_object.get_export(namespace_str, name_str) {
match export {
Extern::Function(function) => {
if tag != wasmer_import_export_kind::WASM_FUNCTION {
update_last_error(CApiError {
msg: format!("Found function, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_FUNCTION;
let writer = import_export_value_out.func as *mut Function;
*writer = function.clone();
}
Extern::Memory(memory) => {
if tag != wasmer_import_export_kind::WASM_MEMORY {
update_last_error(CApiError {
msg: format!("Found memory, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_MEMORY;
let writer = import_export_value_out.func as *mut Memory;
*writer = memory.clone();
}
Extern::Table(table) => {
if tag != wasmer_import_export_kind::WASM_TABLE {
update_last_error(CApiError {
msg: format!("Found table, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_TABLE;
let writer = import_export_value_out.func as *mut Table;
*writer = table.clone();
}
Extern::Global(global) => {
if tag != wasmer_import_export_kind::WASM_GLOBAL {
update_last_error(CApiError {
msg: format!("Found global, expected {}", tag.to_str()),
});
return wasmer_result_t::WASMER_ERROR;
}
import_out.tag = wasmer_import_export_kind::WASM_GLOBAL;
let writer = import_export_value_out.func as *mut Global;
*writer = global.clone();
}
}
import_out.value = *import_export_value;
import_out.module_name = namespace;
import_out.import_name = name;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: format!("Extern {} {} not found", namespace_str, name_str),
});
wasmer_result_t::WASMER_ERROR
}
*/
}
/// private wrapper data type used for casting
#[repr(C)]
struct WasmerImportObjectIterator(
std::iter::Peekable<Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>>,
);
/// Create an iterator over the functions in the import object.
/// Get the next import with `wasmer_import_object_iter_next`
/// Free the iterator with `wasmer_import_object_iter_destroy`
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iterate_functions(
import_object: *const wasmer_import_object_t,
) -> *mut wasmer_import_object_iter_t {
todo!("Disabled until ImportObject APIs are updated")
/*
if import_object.is_null() {
update_last_error(CApiError {
msg: "import_object must not be null".to_owned(),
});
return std::ptr::null_mut();
}
let import_object: &ImportObject = &*(import_object as *const ImportObject);
let iter_inner = Box::new(import_object.clone_ref().into_iter().filter(|((_, _), e)| {
if let Extern::Function(_) = e {
true
} else {
false
}
})) as Box<dyn Iterator<Item = <ImportObjectIterator as Iterator>::Item>>;
let iterator = Box::new(WasmerImportObjectIterator(iter_inner.peekable()));
Box::into_raw(iterator) as *mut wasmer_import_object_iter_t
*/
}
/// Writes the next value to `import`. `WASMER_ERROR` is returned if there
/// was an error or there's nothing left to return.
///
/// To free the memory allocated here, pass the import to `wasmer_import_object_imports_destroy`.
/// To check if the iterator is done, use `wasmer_import_object_iter_at_end`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_next(
import_object_iter: *mut wasmer_import_object_iter_t,
import: *mut wasmer_import_t,
) -> wasmer_result_t {
todo!("Disabled until ImportObject APIs are updated")
/*
if import_object_iter.is_null() || import.is_null() {
update_last_error(CApiError {
msg: "import_object_iter and import must not be null".to_owned(),
});
return wasmer_result_t::WASMER_ERROR;
}
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
let out = &mut *import;
// TODO: the copying here can be optimized away, we just need to use a different type of
// iterator internally
if let Some(((namespace, name), export)) = iter.0.next() {
let ns = {
let mut n = namespace.clone();
n.shrink_to_fit();
n.into_bytes()
};
let ns_bytes = wasmer_byte_array {
bytes: ns.as_ptr(),
bytes_len: ns.len() as u32,
};
let name = {
let mut n = name.clone();
n.shrink_to_fit();
n.into_bytes()
};
let name_bytes = wasmer_byte_array {
bytes: name.as_ptr(),
bytes_len: name.len() as u32,
};
out.module_name = ns_bytes;
out.import_name = name_bytes;
std::mem::forget(ns);
std::mem::forget(name);
match export {
Extern::Function(function) => {
let func = Box::new(function.clone());
out.tag = wasmer_import_export_kind::WASM_FUNCTION;
out.value = wasmer_import_export_value {
func: Box::into_raw(func) as *mut _ as *const _,
};
}
Extern::Global(global) => {
let glbl = Box::new(global.clone());
out.tag = wasmer_import_export_kind::WASM_GLOBAL;
out.value = wasmer_import_export_value {
global: Box::into_raw(glbl) as *mut _ as *const _,
};
}
Extern::Memory(memory) => {
let mem = Box::new(memory.clone());
out.tag = wasmer_import_export_kind::WASM_MEMORY;
out.value = wasmer_import_export_value {
memory: Box::into_raw(mem) as *mut _ as *const _,
};
}
Extern::Table(table) => {
let tbl = Box::new(table.clone());
out.tag = wasmer_import_export_kind::WASM_TABLE;
out.value = wasmer_import_export_value {
memory: Box::into_raw(tbl) as *mut _ as *const _,
};
}
}
wasmer_result_t::WASMER_OK
} else {
wasmer_result_t::WASMER_ERROR
}
*/
}
/// Returns true if further calls to `wasmer_import_object_iter_next` will
/// not return any new data
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_at_end(
import_object_iter: *mut wasmer_import_object_iter_t,
) -> bool {
if import_object_iter.is_null() {
update_last_error(CApiError {
msg: "import_object_iter must not be null".to_owned(),
});
return true;
}
let iter = &mut *(import_object_iter as *mut WasmerImportObjectIterator);
iter.0.peek().is_none()
}
/// Frees the memory allocated by `wasmer_import_object_iterate_functions`
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_iter_destroy(
import_object_iter: *mut wasmer_import_object_iter_t,
) {
if !import_object_iter.is_null() {
let _ = Box::from_raw(import_object_iter as *mut WasmerImportObjectIterator);
}
}
/// Frees the memory allocated in `wasmer_import_object_iter_next`
///
/// This function does not free the memory in `wasmer_import_object_t`;
/// it only frees memory allocated while querying a `wasmer_import_object_t`.
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_imports_destroy(
imports: *mut wasmer_import_t,
imports_len: u32,
) {
if imports.is_null() {
return;
}
let imports: &[wasmer_import_t] = &*slice::from_raw_parts_mut(imports, imports_len as usize);
for import in imports {
let _namespace: Vec<u8> = Vec::from_raw_parts(
import.module_name.bytes as *mut u8,
import.module_name.bytes_len as usize,
import.module_name.bytes_len as usize,
);
let _name: Vec<u8> = Vec::from_raw_parts(
import.import_name.bytes as *mut u8,
import.import_name.bytes_len as usize,
import.import_name.bytes_len as usize,
);
match import.tag {
wasmer_import_export_kind::WASM_FUNCTION => {
let _: Box<Function> = Box::from_raw(import.value.func as *mut _);
}
wasmer_import_export_kind::WASM_GLOBAL => {
let _: Box<Global> = Box::from_raw(import.value.global as *mut _);
}
wasmer_import_export_kind::WASM_MEMORY => {
let _: Box<Memory> = Box::from_raw(import.value.memory as *mut _);
}
wasmer_import_export_kind::WASM_TABLE => {
let _: Box<Table> = Box::from_raw(import.value.table as *mut _);
}
}
}
}
/// Extends an existing import object with new imports
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_object_extend(
import_object: *mut wasmer_import_object_t,
imports: *const wasmer_import_t,
imports_len: c_uint,
) -> wasmer_result_t {
todo!("Disabled until import object APIs change")
/*
let import_object: &mut ImportObject = &mut *(import_object as *mut ImportObject);
let mut extensions: Vec<((String, String), Export)> = Vec::new();
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
for import in imports {
let module_name = slice::from_raw_parts(
import.module_name.bytes,
import.module_name.bytes_len as usize,
);
let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting module name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_name = slice::from_raw_parts(
import.import_name.bytes,
import.import_name.bytes_len as usize,
);
let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting import_name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory;
Extern::Memory((&*mem).clone())
}
wasmer_import_export_kind::WASM_FUNCTION => {
let func_export = import.value.func as *mut Function;
Extern::Function((&*func_export).clone())
}
wasmer_import_export_kind::WASM_GLOBAL => {
let global = import.value.global as *mut Global;
Extern::Global((&*global).clone())
}
wasmer_import_export_kind::WASM_TABLE => {
let table = import.value.table as *mut Table;
Extern::Table((&*table).clone())
}
};
let extension = ((module_name.to_string(), import_name.to_string()), export);
extensions.push(extension)
}
import_object.extend(extensions);
return wasmer_result_t::WASMER_OK;
*/
}
/// Gets import descriptors for the given module
///
/// The caller owns the object and should call `wasmer_import_descriptors_destroy` to free it.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_descriptors(
module: *const wasmer_module_t,
import_descriptors: *mut *mut wasmer_import_descriptors_t,
) {
if module.is_null() {
return;
}
let module = &*(module as *const Module);
let descriptors = module.imports().collect::<Vec<ImportType>>();
let named_import_descriptors: Box<NamedImportDescriptors> =
Box::new(NamedImportDescriptors(descriptors));
*import_descriptors =
Box::into_raw(named_import_descriptors) as *mut wasmer_import_descriptors_t;
}
pub struct NamedImportDescriptors(Vec<ImportType>);
/// Frees the memory for the given import descriptors
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_import_descriptors_destroy(
import_descriptors: *mut wasmer_import_descriptors_t,
) {
if !import_descriptors.is_null() {
unsafe { Box::from_raw(import_descriptors as *mut NamedImportDescriptors) };
}
}
/// Gets the length of the import descriptors
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_descriptors_len(
exports: *mut wasmer_import_descriptors_t,
) -> c_uint {
if exports.is_null() {
return 0;
}
(*(exports as *mut NamedImportDescriptors)).0.len() as c_uint
}
/// Gets import descriptor by index
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_import_descriptors_get(
import_descriptors: *mut wasmer_import_descriptors_t,
idx: c_uint,
) -> *mut wasmer_import_descriptor_t {
if import_descriptors.is_null() {
return ptr::null_mut();
}
let named_import_descriptors = &mut *(import_descriptors as *mut NamedImportDescriptors);
&mut (*named_import_descriptors).0[idx as usize] as *mut ImportType
as *mut wasmer_import_descriptor_t
}
/// Gets name for the import descriptor
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_descriptor_name(
import_descriptor: *mut wasmer_import_descriptor_t,
) -> wasmer_byte_array {
let named_import_descriptor = &*(import_descriptor as *mut ImportType);
wasmer_byte_array {
bytes: named_import_descriptor.name().as_ptr(),
bytes_len: named_import_descriptor.name().len() as u32,
}
}
/// Gets module name for the import descriptor
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_descriptor_module_name(
import_descriptor: *mut wasmer_import_descriptor_t,
) -> wasmer_byte_array {
let named_import_descriptor = &*(import_descriptor as *mut ImportType);
wasmer_byte_array {
bytes: named_import_descriptor.module().as_ptr(),
bytes_len: named_import_descriptor.module().len() as u32,
}
}
/// Gets export descriptor kind
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_descriptor_kind(
export: *mut wasmer_import_descriptor_t,
) -> wasmer_import_export_kind {
let named_import_descriptor = &*(export as *mut ImportType);
named_import_descriptor.ty().into()
}
/// Sets the result parameter to the arity of the params of the wasmer_import_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_func_params_arity(
func: *const wasmer_import_func_t,
result: *mut u32,
) -> wasmer_result_t {
todo!("Figure out how to get a usable siganture from an Function")
/*let function = &*(func as *const Function);
if let Extren::Function(function) = *export {
*result = signature.params().len() as u32;
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_import_func_params_arity".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
*/
}
/// Creates new host function, aka imported function. `func` is a
/// function pointer, where the first argument is the famous `vm::Ctx`
/// (in Rust), or `wasmer_instance_context_t` (in C). All arguments
/// must be typed with compatible WebAssembly native types:
///
/// | WebAssembly type | C/C++ type |
/// | ---------------- | ---------- |
/// | `i32` | `int32_t` |
/// | `i64` | `int64_t` |
/// | `f32` | `float` |
/// | `f64` | `double` |
///
/// The function pointer must have a lifetime greater than the
/// WebAssembly instance lifetime.
///
/// The caller owns the object and should call
/// `wasmer_import_func_destroy` to free it.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_func_new(
func: extern "C" fn(data: *mut c_void),
params: *const wasmer_value_tag,
params_len: c_uint,
returns: *const wasmer_value_tag,
returns_len: c_uint,
) -> *mut wasmer_import_func_t {
unimplemented!("`wasmer_import_func_new` cannot be implemented yet")
/*
let params: &[wasmer_value_tag] = slice::from_raw_parts(params, params_len as usize);
let params: Vec<Type> = params.iter().cloned().map(|x| x.into()).collect();
let returns: &[wasmer_value_tag] = slice::from_raw_parts(returns, returns_len as usize);
let returns: Vec<Type> = returns.iter().cloned().map(|x| x.into()).collect();
let export = Box::new(Extern::Function {
func: FuncPointer::new(func as _),
ctx: Context::Internal,
signature: Arc::new(FuncSig::new(params, returns)),
});
Box::into_raw(export) as *mut wasmer_import_func_t
*/
}
/// Stop the execution of a host function, aka imported function. The
/// function must be used _only_ inside a host function.
///
/// The pointer to `wasmer_instance_context_t` is received by the host
/// function as its first argument. Just passing it to `ctx` is fine.
///
/// The error message must have a greater lifetime than the host
/// function itself since the error is read outside the host function
/// with `wasmer_last_error_message`.
///
/// This function returns `wasmer_result_t::WASMER_ERROR` if `ctx` or
/// `error_message` are null.
///
/// This function never returns otherwise.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trap(
ctx: *const wasmer_instance_context_t,
error_message: *const c_char,
) -> wasmer_result_t {
todo!("wasmer_trap: manually trap without Ctx")
/*
if ctx.is_null() {
update_last_error(CApiError {
msg: "ctx ptr is null in wasmer_trap".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
if error_message.is_null() {
update_last_error(CApiError {
msg: "error_message is null in wasmer_trap".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let ctx = &*(ctx as *const Ctx);
let error_message = CStr::from_ptr(error_message).to_str().unwrap();
(&*ctx.module)
.runnable_module
.do_early_trap(Box::new(error_message)); // never returns
// cbindgen does not generate a binding for a function that
// returns `!`. Since we also need to error in some cases, the
// output type of `wasmer_trap` is `wasmer_result_t`. But the OK
// case is not reachable because `do_early_trap` never
// returns. That's a compromise to satisfy the Rust type system,
// cbindgen, and get an acceptable clean code.
#[allow(unreachable_code)]
wasmer_result_t::WASMER_OK
*/
}
/// Sets the params buffer to the parameter types of the given wasmer_import_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_func_params(
func: *const wasmer_import_func_t,
params: *mut wasmer_value_tag,
params_len: c_uint,
) -> wasmer_result_t {
todo!("Figure out how to get a usable signature from an `Function`")
/*
let function = &*(func as *const Function);
if let Extern::Function(function) = *export {
let params: &mut [wasmer_value_tag] =
slice::from_raw_parts_mut(params, params_len as usize);
for (i, item) in signature.params().iter().enumerate() {
params[i] = item.into();
}
wasmer_result_t::WASMER_OK
} else {
update_last_error(CApiError {
msg: "func ptr error in wasmer_import_func_params".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
*/
}
/// Sets the returns buffer to the parameter types of the given wasmer_import_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_func_returns(
func: *const wasmer_import_func_t,
returns: *mut wasmer_value_tag,
returns_len: c_uint,
) -> wasmer_result_t {
todo!("Figure out how to get a usable signature from an `Function`")
/*
let function = &*(func as *const Function);
let returns: &mut [wasmer_value_tag] =
slice::from_raw_parts_mut(returns, returns_len as usize);
for (i, item) in signature.returns().iter().enumerate() {
returns[i] = item.into();
}
wasmer_result_t::WASMER_OK
*/
}
/// Sets the result parameter to the arity of the returns of the wasmer_import_func_t
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_import_func_returns_arity(
func: *const wasmer_import_func_t,
result: *mut u32,
) -> wasmer_result_t {
todo!("Figure out how to get a usable signature from an `Function`")
/*
let function = &*(func as *const Function);
*result = signature.returns().len() as u32;
wasmer_result_t::WASMER_OK
*/
}
/// Frees memory for the given Func
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_import_func_destroy(func: *mut wasmer_import_func_t) {
if !func.is_null() {
unsafe { Box::from_raw(func as *mut Function) };
}
}
/// Frees memory of the given ImportObject
#[no_mangle]
pub extern "C" fn wasmer_import_object_destroy(import_object: *mut wasmer_import_object_t) {
if !import_object.is_null() {
unsafe { Box::from_raw(import_object as *mut ImportObject) };
}
}

View File

@@ -0,0 +1,239 @@
use super::*;
use crate::get_slice_checked;
use libc::c_uchar;
use std::{path::PathBuf, ptr, str};
use wasmer::{Memory, MemoryType};
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
/// learn 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`
#[repr(C)]
pub struct wasmer_wasi_map_dir_entry_t {
/// What the WASI module will see in its virtual root
pub alias: wasmer_byte_array,
/// The backing file that the WASI module will interact with via the alias
pub host_file_path: wasmer_byte_array,
}
impl wasmer_wasi_map_dir_entry_t {
/// Converts the data into owned, Rust types
pub unsafe fn as_tuple(&self) -> Result<(String, PathBuf), str::Utf8Error> {
let alias = self.alias.as_str()?.to_owned();
let host_path = PathBuf::from(self.host_file_path.as_str()?);
Ok((alias, host_path))
}
}
/// Creates a WASI import object.
///
/// This function treats null pointers as empty collections.
/// For example, passing null for a string in `args`, will lead to a zero
/// length argument in that position.
#[no_mangle]
pub unsafe extern "C" fn wasmer_wasi_generate_import_object(
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 {
todo!("wasmer_wasi_generate_import_object: blocked on global store")
/*
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::Latest,
arg_list,
env_list,
preopened_file_list,
mapped_dir_list,
)
.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, str::Utf8Error> {
todo!("Arg and env parsing need to be done here; this logic already exists and it's important to get it right, so we should probably reorganize code to make it easier to do the right thing.")
/*
let arg_vec: Vec<_> = arg_list.iter().map(|arg| unsafe { arg.as_vec() }).collect();
let env_vec: Vec<_> = env_list
.iter()
.map(|env_var| unsafe { env_var.as_vec() })
.collect();
let po_file_vec = preopened_file_list
.iter()
.map(|po_file| Ok(unsafe { PathBuf::from(po_file.as_str()?) }.to_owned()))
.collect::<Result<Vec<_>, _>>()?;
let mapped_dir_vec = mapped_dir_list
.iter()
.map(|entry| unsafe { entry.as_tuple() })
.collect::<Result<Vec<_>, _>>()?;
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 store = crate::get_global_store();
let mut wasi_state_builder = wasi::WasiState::new(
arg_vec
.first()
.unwrap_or_else("wasmer-wasi-default-program-name"),
);
wasi_state_builder
.args(&arg_vec[1..])
.envs(env_vec)
.preopen_dirs(po_file_vec)?
.map_dirs(mapped_dir_vec)?;
let wasi_state = wasi_state_builder.build().unwrap();
let mut wasi_env = wasi::WasiEnv::new(wasi_state);
let memory_type = MemoryType::new(0, None, false);
let memory = Memory::new(store, memory_type);
wasi_env.set_memory(&memory);
let import_object = Box::new(wasi::generate_import_object_from_env(
store,
&mut wasi_env,
version,
));
Ok(Box::into_raw(import_object) as *mut wasmer_import_object_t)
*/
}
/// Convenience function that creates a WASI import object with no arguments,
/// environment variables, preopened files, or mapped directories.
///
/// This function is the same as calling [`wasmer_wasi_generate_import_object`] with all
/// empty values.
#[no_mangle]
pub unsafe extern "C" fn wasmer_wasi_generate_default_import_object() -> *mut wasmer_import_object_t
{
let store = crate::get_global_store();
let mut wasi_state_builder = wasi::WasiState::new("wasmer-wasi-default-program-name");
let wasi_state = wasi_state_builder.build().unwrap();
let mut wasi_env = wasi::WasiEnv::new(wasi_state);
// this API will now leak a `Memory`
let memory_type = MemoryType::new(0, None, false);
let memory = Memory::new(store, memory_type);
wasi_env.set_memory(&memory);
// TODO(mark): review lifetime of `Memory` here
let import_object = Box::new(wasi::generate_import_object_from_env(
store,
&mut wasi_env,
wasi::WasiVersion::Latest,
));
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());
}
}

558
lib/c-api/src/instance.rs Normal file
View File

@@ -0,0 +1,558 @@
//! Instantiate a module, call functions, and read exports.
use crate::{
error::{update_last_error, CApiError},
export::{wasmer_exports_t, wasmer_import_export_kind, NamedExport, NamedExports},
import::wasmer_import_t,
memory::wasmer_memory_t,
value::{wasmer_value, wasmer_value_t, wasmer_value_tag},
wasmer_result_t,
};
use libc::{c_char, c_int, c_void};
use std::collections::HashMap;
use std::ffi::CStr;
use std::ptr;
use std::slice;
use wasmer::{
Exports, Extern, Function, Global, ImportObject, Instance, Memory, Module, Table, Val,
};
/// Opaque pointer to a `wasmer_runtime::Instance` value in Rust.
///
/// A `wasmer_runtime::Instance` represents a WebAssembly instance. It
/// is generally generated by the `wasmer_instantiate()` function, or by
/// the `wasmer_module_instantiate()` function for the most common paths.
#[repr(C)]
pub struct wasmer_instance_t;
/// Opaque pointer to a `wasmer_runtime::Ctx` value in Rust.
///
/// An instance context is passed to any host function (aka imported
/// function) as the first argument. It is necessary to read the
/// instance data or the memory, respectively with the
/// `wasmer_instance_context_data_get()` function, and the
/// `wasmer_instance_context_memory()` function.
///
/// It is also possible to get the instance context outside a host
/// function by using the `wasmer_instance_context_get()`
/// function. See also `wasmer_instance_context_data_set()` to set the
/// instance context data.
///
/// Example:
///
/// ```c
/// // A host function that prints data from the WebAssembly memory to
/// // the standard output.
/// void print(wasmer_instance_context_t *context, int32_t pointer, int32_t length) {
/// // Use `wasmer_instance_context` to get back the first instance memory.
/// const wasmer_memory_t *memory = wasmer_instance_context_memory(context, 0);
///
/// // Continue…
/// }
/// ```
#[repr(C)]
pub struct wasmer_instance_context_t;
/// Creates a new WebAssembly instance from the given bytes and imports.
///
/// The result is stored in the first argument `instance` if
/// successful, i.e. when the function returns
/// `wasmer_result_t::WASMER_OK`. Otherwise
/// `wasmer_result_t::WASMER_ERROR` is returned, and
/// `wasmer_last_error_length()` with `wasmer_last_error_message()` must
/// be used to read the error message.
///
/// The caller is responsible to free the instance with
/// `wasmer_instance_destroy()`.
///
/// Example:
///
/// ```c
/// // 1. Read a WebAssembly module from a file.
/// FILE *file = fopen("sum.wasm", "r");
/// fseek(file, 0, SEEK_END);
/// long bytes_length = ftell(file);
/// uint8_t *bytes = malloc(bytes_length);
/// fseek(file, 0, SEEK_SET);
/// fread(bytes, 1, bytes_length, file);
/// fclose(file);
///
/// // 2. Declare the imports (here, none).
/// wasmer_import_t imports[] = {};
///
/// // 3. Instantiate the WebAssembly module.
/// wasmer_instance_t *instance = NULL;
/// wasmer_result_t result = wasmer_instantiate(&instance, bytes, bytes_length, imports, 0);
///
/// // 4. Check for errors.
/// if (result != WASMER_OK) {
/// int error_length = wasmer_last_error_length();
/// char *error = malloc(error_length);
/// wasmer_last_error_message(error, error_length);
/// // Do something with `error`…
/// }
///
/// // 5. Free the memory!
/// wasmer_instance_destroy(instance);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instantiate(
instance: *mut *mut wasmer_instance_t,
wasm_bytes: *mut u8,
wasm_bytes_len: u32,
imports: *mut wasmer_import_t,
imports_len: c_int,
) -> wasmer_result_t {
if wasm_bytes.is_null() {
update_last_error(CApiError {
msg: "wasm bytes ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
let mut import_object = ImportObject::new();
let mut namespaces = HashMap::new();
for import in imports {
let module_name = slice::from_raw_parts(
import.module_name.bytes,
import.module_name.bytes_len as usize,
);
let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting module name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_name = slice::from_raw_parts(
import.import_name.bytes,
import.import_name.bytes_len as usize,
);
let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting import_name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let namespace = namespaces.entry(module_name).or_insert_with(Exports::new);
// TODO check that tag is actually in bounds here
let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory;
Extern::Memory((&*mem).clone())
}
wasmer_import_export_kind::WASM_FUNCTION => {
let func_export = import.value.func as *mut Function;
Extern::Function((&*func_export).clone())
}
wasmer_import_export_kind::WASM_GLOBAL => {
let global = import.value.global as *mut Global;
Extern::Global((&*global).clone())
}
wasmer_import_export_kind::WASM_TABLE => {
let table = import.value.table as *mut Table;
Extern::Table((&*table).clone())
}
};
namespace.insert(import_name, export);
}
for (module_name, namespace) in namespaces.into_iter() {
import_object.register(module_name, namespace);
}
let bytes: &[u8] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize);
let store = crate::get_global_store();
let module_result = Module::from_binary(store, bytes);
let module = match module_result {
Ok(module) => module,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
let result = Instance::new(&module, &import_object);
let new_instance = match result {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
wasmer_result_t::WASMER_OK
}
/// Returns the instance context. Learn more by looking at the
/// `wasmer_instance_context_t` struct.
///
/// This function returns `null` if `instance` is a null pointer.
///
/// Example:
///
/// ```c
/// const wasmer_instance_context_get *context = wasmer_instance_context_get(instance);
/// my_data *data = (my_data *) wasmer_instance_context_data_get(context);
/// // Do something with `my_data`.
/// ```
///
/// It is often useful with `wasmer_instance_context_data_set()`.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_instance_context_get(
instance: *mut wasmer_instance_t,
) -> *const wasmer_instance_context_t {
if instance.is_null() {
return ptr::null() as _;
}
unimplemented!("wasmer_instance_context_get: API changed")
/*
let instance = unsafe { &*(instance as *const Instance) };
let context: *const Ctx = instance.context() as *const _;
context as *const wasmer_instance_context_t
*/
}
/// Calls an exported function of a WebAssembly instance by `name`
/// with the provided parameters. The exported function results are
/// stored on the provided `results` pointer.
///
/// This function returns `wasmer_result_t::WASMER_OK` upon success,
/// `wasmer_result_t::WASMER_ERROR` otherwise. You can use
/// `wasmer_last_error_message()` to get the generated error message.
///
/// Potential errors are the following:
///
/// * `instance` is a null pointer,
/// * `name` is a null pointer,
/// * `params` is a null pointer.
///
/// Example of calling an exported function that needs two parameters, and returns one value:
///
/// ```c
/// // First argument.
/// wasmer_value_t argument_one = {
/// .tag = WASM_I32,
/// .value.I32 = 3,
/// };
///
/// // Second argument.
/// wasmer_value_t argument_two = {
/// .tag = WASM_I32,
/// .value.I32 = 4,
/// };
///
/// // First result.
/// wasmer_value_t result_one;
///
/// // All arguments and results.
/// wasmer_value_t arguments[] = {argument_one, argument_two};
/// wasmer_value_t results[] = {result_one};
///
/// wasmer_result_t call_result = wasmer_instance_call(
/// instance, // instance pointer
/// "sum", // the exported function name
/// arguments, // the arguments
/// 2, // the number of arguments
/// results, // the results
/// 1 // the number of results
/// );
///
/// if (call_result == WASMER_OK) {
/// printf("Result is: %d\n", results[0].value.I32);
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instance_call(
instance: *mut wasmer_instance_t,
name: *const c_char,
params: *const wasmer_value_t,
params_len: u32,
results: *mut wasmer_value_t,
results_len: u32,
) -> wasmer_result_t {
if instance.is_null() {
update_last_error(CApiError {
msg: "instance ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
if name.is_null() {
update_last_error(CApiError {
msg: "name ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
if params.is_null() {
update_last_error(CApiError {
msg: "params ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let params: &[wasmer_value_t] = slice::from_raw_parts(params, params_len as usize);
let params: Vec<Val> = params.iter().cloned().map(|x| x.into()).collect();
let func_name_c = CStr::from_ptr(name);
let func_name_r = func_name_c.to_str().unwrap();
let results: &mut [wasmer_value_t] = slice::from_raw_parts_mut(results, results_len as usize);
let instance = &*(instance as *mut Instance);
let f: &Function = match instance.exports.get(func_name_r) {
Ok(f) => f,
Err(err) => {
update_last_error(err);
return wasmer_result_t::WASMER_ERROR;
}
};
let result = f.call(&params[..]);
match result {
Ok(results_vec) => {
if !results_vec.is_empty() {
let ret = match results_vec[0] {
Val::I32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I32,
value: wasmer_value { I32: x },
},
Val::I64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I64,
value: wasmer_value { I64: x },
},
Val::F32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F32,
value: wasmer_value { F32: x },
},
Val::F64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Val::V128(_) => unimplemented!("calling function with V128 parameter"),
Val::AnyRef(_) => unimplemented!("returning AnyRef type"),
Val::FuncRef(_) => unimplemented!("returning FuncRef type"),
};
results[0] = ret;
}
wasmer_result_t::WASMER_OK
}
Err(err) => {
update_last_error(err);
wasmer_result_t::WASMER_ERROR
}
}
}
/// Gets all the exports of the given WebAssembly instance.
///
/// This function stores a Rust vector of exports into `exports` as an
/// opaque pointer of kind `wasmer_exports_t`.
///
/// As is, you can do anything with `exports` except using the
/// companion functions, like `wasmer_exports_len()`,
/// `wasmer_exports_get()` or `wasmer_export_kind()`. See the example below.
///
/// **Warning**: The caller owns the object and should call
/// `wasmer_exports_destroy()` to free it.
///
/// Example:
///
/// ```c
/// // Get the exports.
/// wasmer_exports_t *exports = NULL;
/// wasmer_instance_exports(instance, &exports);
///
/// // Get the number of exports.
/// int exports_length = wasmer_exports_len(exports);
/// printf("Number of exports: %d\n", exports_length);
///
/// // Read the first export.
/// wasmer_export_t *export = wasmer_exports_get(exports, 0);
///
/// // Get the kind of the export.
/// wasmer_import_export_kind export_kind = wasmer_export_kind(export);
///
/// // Assert it is a function (why not).
/// assert(export_kind == WASM_FUNCTION);
///
/// // Read the export name.
/// wasmer_byte_array name_bytes = wasmer_export_name(export);
///
/// assert(name_bytes.bytes_len == sizeof("sum") - 1);
/// assert(memcmp(name_bytes.bytes, "sum", sizeof("sum") - 1) == 0);
///
/// // Destroy the exports.
/// wasmer_exports_destroy(exports);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instance_exports(
instance: *mut wasmer_instance_t,
exports: *mut *mut wasmer_exports_t,
) {
if instance.is_null() {
return;
}
let instance_ref = &mut *(instance as *mut Instance);
let mut exports_vec: Vec<NamedExport> = instance_ref
.module()
.exports()
.map(|export_type| NamedExport {
export_type,
instance: instance as *mut Instance,
})
.collect();
let named_exports: Box<NamedExports> = Box::new(NamedExports(exports_vec));
*exports = Box::into_raw(named_exports) as *mut wasmer_exports_t;
}
/// Sets the data that can be hold by an instance context.
///
/// An instance context (represented by the opaque
/// `wasmer_instance_context_t` structure) can hold user-defined
/// data. This function sets the data. This function is complementary
/// of `wasmer_instance_context_data_get()`.
///
/// This function does nothing if `instance` is a null pointer.
///
/// Example:
///
/// ```c
/// // Define your own data.
/// typedef struct {
/// // …
/// } my_data;
///
/// // Allocate them and set them on the given instance.
/// my_data *data = malloc(sizeof(my_data));
/// data->… = …;
/// wasmer_instance_context_data_set(instance, (void*) data);
///
/// // You can read your data.
/// {
/// my_data *data = (my_data*) wasmer_instance_context_data_get(wasmer_instance_context_get(instance));
/// // …
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_instance_context_data_set(
instance: *mut wasmer_instance_t,
data_ptr: *mut c_void,
) {
unimplemented!(
"wasmer_instance_context_data_set: API changed in a way that this is non-obvious"
)
/*
if instance.is_null() {
return;
}
let instance = unsafe { &mut *(instance as *mut Instance) };
instance.context_mut().data = data_ptr;
*/
}
/// Gets the `memory_idx`th memory of the instance.
///
/// Note that the index is always `0` until multiple memories are supported.
///
/// This function is mostly used inside host functions (aka imported
/// functions) to read the instance memory.
///
/// Example of a _host function_ that reads and prints a string based on a pointer and a length:
///
/// ```c
/// void print_string(const wasmer_instance_context_t *context, int32_t pointer, int32_t length) {
/// // Get the 0th memory.
/// const wasmer_memory_t *memory = wasmer_instance_context_memory(context, 0);
///
/// // Get the memory data as a pointer.
/// uint8_t *memory_bytes = wasmer_memory_data(memory);
///
/// // Print what we assumed to be a string!
/// printf("%.*s", length, memory_bytes + pointer);
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_instance_context_memory(
ctx: *const wasmer_instance_context_t,
_memory_idx: u32,
) -> *const wasmer_memory_t {
unimplemented!("wasmer_instance_context_memory: API changed")
/*let ctx = unsafe { &*(ctx as *const Ctx) };
let memory = ctx.memory(0);
memory as *const Memory as *const wasmer_memory_t
*/
}
/// Gets the data that can be hold by an instance.
///
/// This function is complementary of
/// `wasmer_instance_context_data_set()`. Please read its
/// documentation. You can also read the documentation of
/// `wasmer_instance_context_t` to get other examples.
///
/// This function returns nothing if `ctx` is a null pointer.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_instance_context_data_get(
ctx: *const wasmer_instance_context_t,
) -> *mut c_void {
unimplemented!("wasmer_instance_context_data_get: API changed")
/*
if ctx.is_null() {
return ptr::null_mut() as _;
}
let ctx = unsafe { &*(ctx as *const Ctx) };
ctx.data
*/
}
/// Frees memory for the given `wasmer_instance_t`.
///
/// Check the `wasmer_instantiate()` function to get a complete
/// example.
///
/// If `instance` is a null pointer, this function does nothing.
///
/// Example:
///
/// ```c
/// // Get an instance.
/// wasmer_instance_t *instance = NULL;
/// wasmer_instantiate(&instance, bytes, bytes_length, imports, 0);
///
/// // Destroy the instance.
/// wasmer_instance_destroy(instance);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_instance_destroy(instance: *mut wasmer_instance_t) {
if !instance.is_null() {
unsafe { Box::from_raw(instance as *mut Instance) };
}
}

192
lib/c-api/src/lib.rs Normal file
View File

@@ -0,0 +1,192 @@
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]
//! # Wasmer Runtime C API
//!
//! Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
//! compatible with Emscripten, Rust and Go. [Learn
//! more](https://github.com/wasmerio/wasmer).
//!
//! This crate exposes a C and C++ API for the Wasmer runtime.
//!
//! # Usage
//!
//! The C and C++ header files can be found in the source tree of this
//! crate, respectively [`wasmer.h`][wasmer_h] and
//! [`wasmer.hh`][wasmer_hh]. They are automatically generated, and always
//! up-to-date in this repository.
//!
//! Here is a simple example to use the C API:
//!
//! ```c
//! #include <stdio.h>
//! #include "wasmer.h"
//! #include <assert.h>
//! #include <stdint.h>
//!
//! int main()
//! {
//! // Read the Wasm file bytes.
//! FILE *file = fopen("sum.wasm", "r");
//! fseek(file, 0, SEEK_END);
//! long len = ftell(file);
//! uint8_t *bytes = malloc(len);
//! fseek(file, 0, SEEK_SET);
//! fread(bytes, 1, len, file);
//! fclose(file);
//!
//! // Prepare the imports.
//! wasmer_import_t imports[] = {};
//!
//! // Instantiate!
//! wasmer_instance_t *instance = NULL;
//! wasmer_result_t instantiation_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
//!
//! assert(instantiation_result == WASMER_OK);
//!
//! // Let's call a function.
//! // Start by preparing the arguments.
//!
//! // Value of argument #1 is `7i32`.
//! wasmer_value_t argument_one;
//! argument_one.tag = WASM_I32;
//! argument_one.value.I32 = 7;
//!
//! // Value of argument #2 is `8i32`.
//! wasmer_value_t argument_two;
//! argument_two.tag = WASM_I32;
//! argument_two.value.I32 = 8;
//!
//! // Prepare the arguments.
//! wasmer_value_t arguments[] = {argument_one, argument_two};
//!
//! // Prepare the return value.
//! wasmer_value_t result_one;
//! wasmer_value_t results[] = {result_one};
//!
//! // Call the `sum` function with the prepared arguments and the return value.
//! wasmer_result_t call_result = wasmer_instance_call(instance, "sum", arguments, 2, results, 1);
//!
//! // Let's display the result.
//! printf("Call result: %d\n", call_result);
//! printf("Result: %d\n", results[0].value.I32);
//!
//! // `sum(7, 8) == 15`.
//! assert(results[0].value.I32 == 15);
//! assert(call_result == WASMER_OK);
//!
//! wasmer_instance_destroy(instance);
//!
//! return 0;
//! }
//! ```
//!
//! [wasmer_h]: ./wasmer.h
//! [wasmer_hh]: ./wasmer.hh
#![deny(
dead_code,
unused_imports,
// temporarily disabled
//unused_variables,
unused_unsafe,
unreachable_patterns
)]
#[macro_use]
extern crate lazy_static;
pub mod error;
pub mod export;
pub mod global;
pub mod import;
pub mod instance;
pub mod memory;
pub mod module;
pub mod table;
// `not(target_family = "windows")` is simpler than `unix`. See build.rs
// if you want to change the meaning of these `cfg`s in the header file.
/*
TODO: reenable `trampoline` module when the refactor gains feature parity with Wasmer master
#[cfg(all(not(target_family = "windows"), target_arch = "x86_64"))]
pub mod trampoline;*/
pub mod value;
/// The `wasmer_result_t` enum is a type that represents either a
/// success, or a failure.
#[allow(non_camel_case_types)]
#[repr(C)]
pub enum wasmer_result_t {
/// Represents a success.
WASMER_OK = 1,
/// Represents a failure.
WASMER_ERROR = 2,
}
/// The `wasmer_limits_t` struct is a type that describes the limits of something
/// such as a memory or a table. See the `wasmer_memory_new()` function to get
/// more information.
#[repr(C)]
pub struct wasmer_limits_t {
/// The minimum number of allowed pages.
pub min: u32,
/// The maximum number of allowed pages.
pub max: wasmer_limit_option_t,
}
/// The `wasmer_limit_option_t` struct represents an optional limit
/// for `wasmer_limits_t`.
#[repr(C)]
pub struct wasmer_limit_option_t {
/// Whether the limit is set.
pub has_some: bool,
/// The limit value.
pub some: u32,
}
#[repr(C)]
pub struct wasmer_byte_array {
pub bytes: *const u8,
pub bytes_len: u32,
}
impl wasmer_byte_array {
/// Get the data as a slice
pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
get_slice_checked(self.bytes, self.bytes_len as usize)
}
/// Copy the data into an owned Vec
pub unsafe fn as_vec(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.bytes_len as usize);
out.extend_from_slice(self.as_slice());
out
}
/// Read the data as a &str, returns an error if the string is not valid UTF8
pub unsafe fn as_str<'a>(&self) -> Result<&'a str, std::str::Utf8Error> {
std::str::from_utf8(self.as_slice())
}
}
/// Gets a slice from a pointer and a length, returning an empty slice if the
/// pointer is null
#[inline]
pub(crate) unsafe fn get_slice_checked<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
if ptr.is_null() {
&[]
} else {
std::slice::from_raw_parts(ptr, len)
}
}
lazy_static! {
pub(crate) static ref GLOBAL_STORE: wasmer::Store = wasmer::Store::default();
}
pub(crate) fn get_global_store() -> &'static wasmer::Store {
&*GLOBAL_STORE
}

212
lib/c-api/src/memory.rs Normal file
View File

@@ -0,0 +1,212 @@
//! Create, read, write, grow, destroy memory of an instance.
use crate::{
error::{update_last_error, CApiError},
wasmer_limits_t, wasmer_result_t,
};
use std::{cell::Cell, ptr};
use wasmer::{Bytes, Memory, MemoryType, Pages};
/// Opaque pointer to a `wasmer_runtime::Memory` value in Rust.
///
/// A `wasmer_runtime::Memory` represents a WebAssembly memory. It is
/// possible to create one with `wasmer_memory_new()` and pass it as
/// imports of an instance, or to read it from exports of an instance
/// with `wasmer_export_to_memory()`.
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_memory_t;
/// Creates a new empty WebAssembly memory for the given descriptor.
///
/// The result is stored in the first argument `memory` if successful,
/// i.e. when the function returns
/// `wasmer_result_t::WASMER_OK`. Otherwise,
/// `wasmer_result_t::WASMER_ERROR` is returned, and
/// `wasmer_last_error_length()` with `wasmer_last_error_message()`
/// must be used to read the error message.
///
/// The caller owns the memory and is responsible to free it with
/// `wasmer_memory_destroy()`.
///
/// Example:
///
/// ```c
/// // 1. The memory object.
/// wasmer_memory_t *memory = NULL;
///
/// // 2. The memory descriptor.
/// wasmer_limits_t memory_descriptor = {
/// .min = 10,
/// .max = {
/// .has_some = true,
/// .some = 15,
/// },
/// };
///
/// // 3. Initialize the memory.
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
///
/// if (result != WASMER_OK) {
/// int error_length = wasmer_last_error_length();
/// char *error = malloc(error_length);
/// wasmer_last_error_message(error, error_length);
/// // Do something with `error`…
/// }
///
/// // 4. Free the memory!
/// wasmer_memory_destroy(memory);
/// ```
#[no_mangle]
pub unsafe extern "C" fn wasmer_memory_new(
memory: *mut *mut wasmer_memory_t,
limits: wasmer_limits_t,
) -> wasmer_result_t {
let max = if limits.max.has_some {
Some(Pages(limits.max.some))
} else {
None
};
let store = crate::get_global_store();
let desc = MemoryType::new(Pages(limits.min), max, false);
let new_memory = Memory::new(store, desc);
*memory = Box::into_raw(Box::new(new_memory)) as *mut wasmer_memory_t;
wasmer_result_t::WASMER_OK
}
/// Grows a memory by the given number of pages (of 65Kb each).
///
/// The functions return `wasmer_result_t::WASMER_OK` upon success,
/// `wasmer_result_t::WASMER_ERROR` otherwise. Use
/// `wasmer_last_error_length()` with `wasmer_last_error_message()` to
/// read the error message.
///
/// Example:
///
/// ```c
/// wasmer_result_t result = wasmer_memory_grow(memory, 10);
///
/// if (result != WASMER_OK) {
/// // …
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_memory_grow(memory: *mut wasmer_memory_t, delta: u32) -> wasmer_result_t {
if memory.is_null() {
update_last_error(CApiError {
msg: "`memory` is NULL.".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let memory = unsafe { &*(memory as *mut Memory) };
let grow_result = memory.grow(Pages(delta));
match grow_result {
Some(_) => wasmer_result_t::WASMER_OK,
_ => wasmer_result_t::WASMER_ERROR,
}
}
/// Reads the current length (in pages) of the given memory.
///
/// The function returns zero if `memory` is a null pointer.
///
/// Example:
///
/// ```c
/// uint32_t memory_length = wasmer_memory_length(memory);
///
/// printf("Memory pages length: %d\n", memory_length);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_memory_length(memory: *const wasmer_memory_t) -> u32 {
if memory.is_null() {
return 0;
}
let memory = unsafe { &*(memory as *const Memory) };
let Pages(length) = memory.size();
length
}
/// Gets a pointer to the beginning of the contiguous memory data
/// bytes.
///
/// The function returns `NULL` if `memory` is a null pointer.
///
/// Note that when the memory grows, it can be reallocated, and thus
/// the returned pointer can be invalidated.
///
/// Example:
///
/// ```c
/// uint8_t *memory_data = wasmer_memory_data(memory);
/// char *str = (char*) malloc(sizeof(char) * 7);
///
/// for (uint32_t nth = 0; nth < 7; ++nth) {
/// str[nth] = (char) memory_data[nth];
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_memory_data(memory: *const wasmer_memory_t) -> *mut u8 {
if memory.is_null() {
return ptr::null_mut();
}
let memory = unsafe { &*(memory as *const Memory) };
memory.view::<u8>()[..].as_ptr() as *mut Cell<u8> as *mut u8
}
/// Gets the size in bytes of the memory data.
///
/// This function returns 0 if `memory` is a null pointer.
///
/// Example:
///
/// ```c
/// uint32_t memory_data_length = wasmer_memory_data_length(memory);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_memory_data_length(memory: *const wasmer_memory_t) -> u32 {
if memory.is_null() {
return 0;
}
let memory = unsafe { &*(memory as *const Memory) };
let Bytes(length) = memory.size().bytes();
length as u32
}
/// Frees memory for the given `wasmer_memory_t`.
///
/// Check the `wasmer_memory_new()` function to get a complete
/// example.
///
/// If `memory` is a null pointer, this function does nothing.
///
/// Example:
///
/// ```c
/// // Get a memory.
/// wasmer_memory_t *memory = NULL;
/// wasmer_result_t result = wasmer_memory_new(&memory, memory_descriptor);
///
/// // Destroy the memory.
/// wasmer_memory_destroy(memory);
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_memory_destroy(memory: *mut wasmer_memory_t) {
if !memory.is_null() {
unsafe { Box::from_raw(memory as *mut Memory) };
}
}

313
lib/c-api/src/module.rs Normal file
View File

@@ -0,0 +1,313 @@
//! Compile, validate, instantiate, serialize, and destroy modules.
use crate::{
error::{update_last_error, CApiError},
export::wasmer_import_export_kind,
import::{wasmer_import_object_t, wasmer_import_t},
instance::wasmer_instance_t,
wasmer_byte_array, wasmer_result_t,
};
use libc::c_int;
use std::collections::HashMap;
use std::slice;
use wasmer::{Exports, Extern, Function, Global, ImportObject, Instance, Memory, Module, Table};
#[repr(C)]
pub struct wasmer_module_t;
#[repr(C)]
pub struct wasmer_serialized_module_t;
/// Creates a new Module from the given wasm bytes.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_compile(
module: *mut *mut wasmer_module_t,
wasm_bytes: *mut u8,
wasm_bytes_len: u32,
) -> wasmer_result_t {
let bytes: &[u8] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize);
let store = crate::get_global_store();
let result = Module::from_binary(store, bytes);
let new_module = match result {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*module = Box::into_raw(Box::new(new_module)) as *mut wasmer_module_t;
wasmer_result_t::WASMER_OK
}
/// Validates a sequence of bytes hoping it represents a valid WebAssembly module.
///
/// The function returns true if the bytes are valid, false otherwise.
///
/// Example:
///
/// ```c
/// bool result = wasmer_validate(bytes, bytes_length);
///
/// if (false == result) {
/// // Do something…
/// }
/// ```
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_validate(wasm_bytes: *const u8, wasm_bytes_len: u32) -> bool {
if wasm_bytes.is_null() {
return false;
}
let bytes: &[u8] = slice::from_raw_parts(wasm_bytes, wasm_bytes_len as usize);
let store = crate::get_global_store();
Module::validate(store, bytes).is_ok()
}
/// Creates a new Instance from the given module and imports.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_instantiate(
module: *const wasmer_module_t,
instance: *mut *mut wasmer_instance_t,
imports: *mut wasmer_import_t,
imports_len: c_int,
) -> wasmer_result_t {
let imports: &[wasmer_import_t] = slice::from_raw_parts(imports, imports_len as usize);
let mut import_object = ImportObject::new();
let mut namespaces = HashMap::new();
for import in imports {
let module_name = slice::from_raw_parts(
import.module_name.bytes,
import.module_name.bytes_len as usize,
);
let module_name = if let Ok(s) = std::str::from_utf8(module_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting module name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let import_name = slice::from_raw_parts(
import.import_name.bytes,
import.import_name.bytes_len as usize,
);
let import_name = if let Ok(s) = std::str::from_utf8(import_name) {
s
} else {
update_last_error(CApiError {
msg: "error converting import_name to string".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
};
let namespace = namespaces.entry(module_name).or_insert_with(Exports::new);
let export = match import.tag {
wasmer_import_export_kind::WASM_MEMORY => {
let mem = import.value.memory as *mut Memory;
Extern::Memory((&*mem).clone())
}
wasmer_import_export_kind::WASM_FUNCTION => {
let func_export = import.value.func as *mut Function;
Extern::Function((&*func_export).clone())
}
wasmer_import_export_kind::WASM_GLOBAL => {
let global = import.value.global as *mut Global;
Extern::Global((&*global).clone())
}
wasmer_import_export_kind::WASM_TABLE => {
let table = import.value.table as *mut Table;
Extern::Table((&*table).clone())
}
};
namespace.insert(import_name, export);
}
for (module_name, namespace) in namespaces.into_iter() {
import_object.register(module_name, namespace);
}
let module = &*(module as *const Module);
let new_instance = match Instance::new(module, &import_object) {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
wasmer_result_t::WASMER_OK
}
/// Given:
/// * A prepared `wasmer` import-object
/// * A compiled wasmer module
///
/// Instantiates a wasmer instance
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_import_instantiate(
instance: *mut *mut wasmer_instance_t,
module: *const wasmer_module_t,
import_object: *const wasmer_import_object_t,
) -> wasmer_result_t {
let import_object: &ImportObject = &*(import_object as *const ImportObject);
let module: &Module = &*(module as *const Module);
let new_instance: Instance = match Instance::new(module, import_object) {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
return wasmer_result_t::WASMER_OK;
}
/// Serialize the given Module.
///
/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_serialize(
serialized_module_out: *mut *mut wasmer_serialized_module_t,
module: *const wasmer_module_t,
) -> wasmer_result_t {
let module = &*(module as *const Module);
match module.serialize() {
Ok(mut serialized_module) => {
let boxed_slice = serialized_module.into_boxed_slice();
*serialized_module_out = Box::into_raw(Box::new(boxed_slice)) as _;
wasmer_result_t::WASMER_OK
}
Err(_) => {
update_last_error(CApiError {
msg: "Failed to serialize the module".to_string(),
});
wasmer_result_t::WASMER_ERROR
}
}
}
/// Get bytes of the serialized module.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_serialized_module_bytes(
serialized_module: *const wasmer_serialized_module_t,
) -> wasmer_byte_array {
let serialized_module = &*(serialized_module as *const &[u8]);
wasmer_byte_array {
bytes: serialized_module.as_ptr(),
bytes_len: serialized_module.len() as u32,
}
}
/// Transform a sequence of bytes into a serialized module.
///
/// The caller owns the object and should call `wasmer_serialized_module_destroy` to free it.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_serialized_module_from_bytes(
serialized_module: *mut *mut wasmer_serialized_module_t,
serialized_module_bytes: *const u8,
serialized_module_bytes_length: u32,
) -> wasmer_result_t {
if serialized_module.is_null() {
update_last_error(CApiError {
msg: "`serialized_module_bytes` pointer is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let serialized_module_bytes: &[u8] = slice::from_raw_parts(
serialized_module_bytes,
serialized_module_bytes_length as usize,
);
*serialized_module = Box::into_raw(Box::new(serialized_module_bytes)) as _;
wasmer_result_t::WASMER_OK
}
/// Deserialize the given serialized module.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(dead_code, unused_variables)]
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_module_deserialize(
module: *mut *mut wasmer_module_t,
serialized_module: *const wasmer_serialized_module_t,
) -> wasmer_result_t {
if serialized_module.is_null() {
update_last_error(CApiError {
msg: "`serialized_module` pointer is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
let serialized_module: &[u8] = &*(serialized_module as *const &[u8]);
let store = crate::get_global_store();
match Module::deserialize(store, serialized_module) {
Ok(deserialized_module) => {
*module = Box::into_raw(Box::new(deserialized_module)) as _;
wasmer_result_t::WASMER_OK
}
Err(e) => {
update_last_error(CApiError { msg: e.to_string() });
wasmer_result_t::WASMER_ERROR
}
}
}
/// Frees memory for the given serialized Module.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_serialized_module_destroy(
serialized_module: *mut wasmer_serialized_module_t,
) {
// TODO(mark): review all serialized logic memory logic
if !serialized_module.is_null() {
unsafe { Box::from_raw(serialized_module as *mut &[u8]) };
}
}
/// Frees memory for the given Module
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_module_destroy(module: *mut wasmer_module_t) {
if !module.is_null() {
unsafe { Box::from_raw(module as *mut Module) };
}
}

97
lib/c-api/src/table.rs Normal file
View File

@@ -0,0 +1,97 @@
//! Create, grow, destroy tables of an instance.
use crate::{error::update_last_error, wasmer_limits_t, wasmer_result_t};
use wasmer::{AnyRef, Table, TableType, Val, ValType};
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_table_t;
// TODO: this logic should be in wasmer itself
fn get_default_table_value(table_type: ValType) -> Val {
match table_type {
ValType::I32 => Val::I32(0),
ValType::I64 => Val::I64(0),
ValType::F32 => Val::F32(0.),
ValType::F64 => Val::F64(0.),
ValType::V128 => Val::V128(0),
ValType::AnyRef => Val::AnyRef(AnyRef::null()), // todo!("Figure out what the default AnyRef value is"),
ValType::FuncRef => Val::AnyRef(AnyRef::null()), //todo!("Figure out what the default FuncRef value is"),
}
}
/// Creates a new Table for the given descriptor and initializes the given
/// pointer to pointer to a pointer to the new Table.
///
/// The caller owns the object and should call `wasmer_table_destroy` to free it.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[no_mangle]
pub unsafe extern "C" fn wasmer_table_new(
table: *mut *mut wasmer_table_t,
limits: wasmer_limits_t,
) -> wasmer_result_t {
let max = if limits.max.has_some {
Some(limits.max.some)
} else {
None
};
let desc = TableType {
ty: ValType::FuncRef,
minimum: limits.min,
maximum: max,
};
let store = crate::get_global_store();
let result = Table::new(store, desc, get_default_table_value(ValType::FuncRef));
let new_table = match result {
Ok(table) => table,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
*table = Box::into_raw(Box::new(new_table)) as *mut wasmer_table_t;
wasmer_result_t::WASMER_OK
}
/// Grows a Table by the given number of elements.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
///
/// Returns `wasmer_result_t::WASMER_ERROR` upon failure. Use `wasmer_last_error_length`
/// and `wasmer_last_error_message` to get an error message.
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_table_grow(table: *mut wasmer_table_t, delta: u32) -> wasmer_result_t {
let table = unsafe { &*(table as *mut Table) };
let table_type = table.ty().ty;
let table_default_value = get_default_table_value(table_type);
let delta_result = table.grow(delta, table_default_value);
match delta_result {
Ok(_) => wasmer_result_t::WASMER_OK,
Err(grow_error) => {
update_last_error(grow_error);
wasmer_result_t::WASMER_ERROR
}
}
}
/// Returns the current length of the given Table
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_table_length(table: *mut wasmer_table_t) -> u32 {
let table = unsafe { &*(table as *mut Table) };
table.size()
}
/// Frees memory for the given Table
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub extern "C" fn wasmer_table_destroy(table: *mut wasmer_table_t) {
if !table.is_null() {
unsafe { Box::from_raw(table as *mut Table) };
}
}

View File

@@ -0,0 +1,92 @@
//! Trampoline emitter for transforming function calls.
use std::ffi::c_void;
use std::mem;
use wasmer_runtime_core::trampoline::*;
#[repr(C)]
pub struct wasmer_trampoline_buffer_builder_t;
#[repr(C)]
pub struct wasmer_trampoline_buffer_t;
#[repr(C)]
pub struct wasmer_trampoline_callable_t;
/// Creates a new trampoline builder.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoline_buffer_builder_t
{
Box::into_raw(Box::new(TrampolineBufferBuilder::new())) as *mut _
}
/// Adds a context trampoline to the builder.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline(
builder: *mut wasmer_trampoline_buffer_builder_t,
func: *const wasmer_trampoline_callable_t,
ctx: *const c_void,
) -> usize {
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
builder.add_context_trampoline(func as *const CallTarget, ctx as *const CallContext)
}
/// Adds a callinfo trampoline to the builder.
///
/// Deprecated. In a future version `DynamicFunc::new` will be exposed to the C API and should be used instead of this function.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
builder: *mut wasmer_trampoline_buffer_builder_t,
func: *const wasmer_trampoline_callable_t,
ctx: *const c_void,
num_params: u32,
) -> usize {
use wasmer::ValType;
let builder = &mut *(builder as *mut TrampolineBufferBuilder);
builder.add_callinfo_trampoline(
mem::transmute(func),
ctx as *const CallContext,
&vec![ValType::I64; num_params as usize],
&[ValType::I64],
)
}
/// Finalizes the trampoline builder into an executable buffer.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build(
builder: *mut wasmer_trampoline_buffer_builder_t,
) -> *mut wasmer_trampoline_buffer_t {
let builder = Box::from_raw(builder as *mut TrampolineBufferBuilder);
Box::into_raw(Box::new(builder.build())) as *mut _
}
/// Destroys the trampoline buffer if not null.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(buffer: *mut wasmer_trampoline_buffer_t) {
if !buffer.is_null() {
Box::from_raw(buffer as *mut TrampolineBuffer);
}
}
/// Returns the callable pointer for the trampoline with index `idx`.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline(
buffer: *const wasmer_trampoline_buffer_t,
idx: usize,
) -> *const wasmer_trampoline_callable_t {
let buffer = &*(buffer as *const TrampolineBuffer);
buffer.get_trampoline(idx) as _
}
/// Returns the context added by `add_context_trampoline`, from within the callee function.
#[no_mangle]
#[allow(clippy::cast_ptr_alignment)]
pub unsafe extern "C" fn wasmer_trampoline_get_context() -> *mut c_void {
get_context() as *const c_void as *mut c_void
}

165
lib/c-api/src/value.rs Normal file
View File

@@ -0,0 +1,165 @@
//! Create and map Rust to WebAssembly values.
use wasmer::Val;
use wasmer::ValType;
/// Represents all possibles WebAssembly value types.
///
/// See `wasmer_value_t` to get a complete example.
#[allow(non_camel_case_types)]
#[repr(u32)]
#[derive(Clone)]
pub enum wasmer_value_tag {
/// Represents the `i32` WebAssembly type.
WASM_I32,
/// Represents the `i64` WebAssembly type.
WASM_I64,
/// Represents the `f32` WebAssembly type.
WASM_F32,
/// Represents the `f64` WebAssembly type.
WASM_F64,
}
/// Represents a WebAssembly value.
///
/// This is a [Rust union][rust-union], which is equivalent to the C
/// union. See `wasmer_value_t` to get a complete example.
///
/// [rust-union]: https://doc.rust-lang.org/reference/items/unions.html
#[repr(C)]
#[derive(Clone, Copy)]
#[allow(non_snake_case)]
pub union wasmer_value {
pub I32: i32,
pub I64: i64,
pub F32: f32,
pub F64: f64,
}
/// Represents a WebAssembly type and value pair,
/// i.e. `wasmer_value_tag` and `wasmer_value`. Since the latter is an
/// union, it's the safe way to read or write a WebAssembly value in
/// C.
///
/// Example:
///
/// ```c
/// // Create a WebAssembly value.
/// wasmer_value_t wasm_value = {
/// .tag = WASM_I32,
/// .value.I32 = 42,
/// };
///
/// // Read a WebAssembly value.
/// if (wasm_value.tag == WASM_I32) {
/// int32_t x = wasm_value.value.I32;
/// // …
/// }
/// ```
#[repr(C)]
#[derive(Clone)]
pub struct wasmer_value_t {
/// The value type.
pub tag: wasmer_value_tag,
/// The value.
pub value: wasmer_value,
}
impl From<wasmer_value_t> for Val {
fn from(v: wasmer_value_t) -> Self {
unsafe {
#[allow(unreachable_patterns, non_snake_case)]
match v {
wasmer_value_t {
tag: wasmer_value_tag::WASM_I32,
value: wasmer_value { I32 },
} => Val::I32(I32),
wasmer_value_t {
tag: wasmer_value_tag::WASM_I64,
value: wasmer_value { I64 },
} => Val::I64(I64),
wasmer_value_t {
tag: wasmer_value_tag::WASM_F32,
value: wasmer_value { F32 },
} => Val::F32(F32),
wasmer_value_t {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64 },
} => Val::F64(F64),
_ => unreachable!("unknown WASM type"),
}
}
}
}
impl From<Val> for wasmer_value_t {
fn from(val: Val) -> Self {
match val {
Val::I32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I32,
value: wasmer_value { I32: x },
},
Val::I64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_I64,
value: wasmer_value { I64: x },
},
Val::F32(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F32,
value: wasmer_value { F32: x },
},
Val::F64(x) => wasmer_value_t {
tag: wasmer_value_tag::WASM_F64,
value: wasmer_value { F64: x },
},
Val::V128(_) => unimplemented!("V128 not supported in C API"),
Val::AnyRef(_) => unimplemented!("AnyRef not supported in C API"),
Val::FuncRef(_) => unimplemented!("AnyFunc not supported in C API"),
}
}
}
impl From<ValType> for wasmer_value_tag {
fn from(ty: ValType) -> Self {
#[allow(unreachable_patterns)]
match ty {
ValType::I32 => wasmer_value_tag::WASM_I32,
ValType::I64 => wasmer_value_tag::WASM_I64,
ValType::F32 => wasmer_value_tag::WASM_F32,
ValType::F64 => wasmer_value_tag::WASM_F64,
ValType::V128 => unreachable!("V128 not supported in C API"),
ValType::AnyRef => unimplemented!("AnyRef not supported in C API"),
ValType::FuncRef => unimplemented!("FuncRef not supported in C API"),
}
}
}
impl From<wasmer_value_tag> for ValType {
fn from(v: wasmer_value_tag) -> Self {
#[allow(unreachable_patterns)]
match v {
wasmer_value_tag::WASM_I32 => ValType::I32,
wasmer_value_tag::WASM_I64 => ValType::I64,
wasmer_value_tag::WASM_F32 => ValType::F32,
wasmer_value_tag::WASM_F64 => ValType::F64,
_ => unreachable!("unknown WASM type"),
}
}
}
impl From<&ValType> for wasmer_value_tag {
fn from(ty: &ValType) -> Self {
match *ty {
ValType::I32 => wasmer_value_tag::WASM_I32,
ValType::I64 => wasmer_value_tag::WASM_I64,
ValType::F32 => wasmer_value_tag::WASM_F32,
ValType::F64 => wasmer_value_tag::WASM_F64,
ValType::V128 => unimplemented!("V128 not supported in C API"),
ValType::AnyRef => unimplemented!("AnyRef not supported in C API"),
ValType::FuncRef => unimplemented!("FuncRef not supported in C API"),
}
}
}

34
lib/c-api/tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
rust-build
test-context
test-exported-memory
test-exports
test-globals
test-import-function
test-import-trap
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-wasi-import-object
test-emscripten-import-object
# ignore wasm-c-api binaries
wasm-c-api-*

View File

@@ -0,0 +1,179 @@
cmake_minimum_required (VERSION 2.6)
project (WasmerRuntimeCApiTests)
add_executable(test-exported-memory test-exported-memory.c)
add_executable(test-exports test-exports.c)
add_executable(test-globals test-globals.c)
# functionality not yet implemented in wasmer reborn
#add_executable(test-import-function test-import-function.c)
add_executable(test-import-trap test-import-trap.c)
add_executable(test-imports test-imports.c)
add_executable(test-import-object test-import-object.c)
add_executable(test-instantiate test-instantiate.c)
add_executable(test-memory test-memory.c)
add_executable(test-module test-module.c)
add_executable(test-module-exports test-module-exports.c)
add_executable(test-module-imports test-module-imports.c)
add_executable(test-module-serialize test-module-serialize.c)
add_executable(test-tables test-tables.c)
add_executable(test-validate test-validate.c)
add_executable(test-context test-context.c)
add_executable(test-module-import-instantiate test-module-import-instantiate.c)
# Wasm C API tests
# Commented out until we can find a solution to the exported function problem
#add_executable(wasm-c-api-hello wasm-c-api/example/hello.c)
#add_executable(wasm-c-api-memory wasm-c-api/example/memory.c)
#add_executable(wasm-c-api-global wasm-c-api/example/global.c)
#add_executable(wasm-c-api-serialize wasm-c-api/example/serialize.c)
if (DEFINED WASI_TESTS)
add_executable(test-wasi-import-object test-wasi-import-object.c)
endif()
if (DEFINED EMSCRIPTEN_TESTS)
add_executable(test-emscripten-import-object test-emscripten-import-object.c)
endif()
include_directories(wasm-c-api/include)
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/
)
if(NOT WASMER_LIB)
message(FATAL_ERROR "wasmer library not found")
endif()
enable_testing()
set(
COMPILER_OPTIONS
# Clang or gcc
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:
"-Werror" >
# MSVC
$<$<CXX_COMPILER_ID:MSVC>:
"/WX" >
)
target_link_libraries(test-exported-memory general ${WASMER_LIB})
target_compile_options(test-exported-memory PRIVATE ${COMPILER_OPTIONS})
add_test(test-exported-memory test-exported-memory)
target_link_libraries(test-exports general ${WASMER_LIB})
target_compile_options(test-exports PRIVATE ${COMPILER_OPTIONS})
add_test(test-exports test-exports)
target_link_libraries(test-globals general ${WASMER_LIB})
target_compile_options(test-globals PRIVATE ${COMPILER_OPTIONS})
add_test(test-globals test-globals)
# functionality not yet implemented in wasmer reborn
#target_link_libraries(test-import-function general ${WASMER_LIB})
#target_compile_options(test-import-function PRIVATE ${COMPILER_OPTIONS})
#add_test(test-import-function test-import-function)
target_link_libraries(test-import-trap general ${WASMER_LIB})
target_compile_options(test-import-trap PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-import-trap test-import-trap)
target_link_libraries(test-imports general ${WASMER_LIB})
target_compile_options(test-imports PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-imports test-imports)
target_link_libraries(test-import-object general ${WASMER_LIB})
target_compile_options(test-import-object PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-import-object test-import-object)
if (DEFINED WASI_TESTS)
target_link_libraries(test-wasi-import-object general ${WASMER_LIB})
target_compile_options(test-wasi-import-object PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-wasi-import-object test-wasi-import-object)
endif()
if (DEFINED EMSCRIPTEN_TESTS)
target_link_libraries(test-emscripten-import-object general ${WASMER_LIB})
target_compile_options(test-emscripten-import-object PRIVATE ${COMPILER_OPTIONS})
add_test(test-emscripten-import-object test-emscripten-import-object)
endif()
target_link_libraries(test-instantiate general ${WASMER_LIB})
target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS})
add_test(test-instantiate test-instantiate)
target_link_libraries(test-memory general ${WASMER_LIB})
target_compile_options(test-memory PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-memory test-memory)
target_link_libraries(test-module general ${WASMER_LIB})
target_compile_options(test-module PRIVATE ${COMPILER_OPTIONS})
add_test(test-module test-module)
target_link_libraries(test-module-exports general ${WASMER_LIB})
target_compile_options(test-module-exports PRIVATE ${COMPILER_OPTIONS})
add_test(test-module-exports test-module-exports)
target_link_libraries(test-module-imports general ${WASMER_LIB})
target_compile_options(test-module-imports PRIVATE ${COMPILER_OPTIONS})
add_test(test-module-imports test-module-imports)
target_link_libraries(test-module-serialize general ${WASMER_LIB})
target_compile_options(test-module-serialize PRIVATE ${COMPILER_OPTIONS})
add_test(test-module-serialize test-module-serialize)
target_link_libraries(test-tables general ${WASMER_LIB})
target_compile_options(test-tables PRIVATE ${COMPILER_OPTIONS})
add_test(test-tables test-tables)
target_link_libraries(test-validate general ${WASMER_LIB})
target_compile_options(test-validate PRIVATE ${COMPILER_OPTIONS})
add_test(test-validate test-validate)
target_link_libraries(test-context general ${WASMER_LIB})
target_compile_options(test-context PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-context test-context)
target_link_libraries(test-module-import-instantiate general ${WASMER_LIB})
target_compile_options(test-module-import-instantiate PRIVATE ${COMPILER_OPTIONS})
# TODO: reenable this test
#add_test(test-module-import-instantiate test-module-import-instantiate)
# Commented out until we can find a solution to the exported function problem
#
#target_link_libraries(wasm-c-api-hello general ${WASMER_LIB})
#target_compile_options(wasm-c-api-hello PRIVATE ${COMPILER_OPTIONS})
#add_test(NAME wasm-c-api-hello
# COMMAND wasm-c-api-hello
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example
#)
#
#target_link_libraries(wasm-c-api-memory general ${WASMER_LIB})
#target_compile_options(wasm-c-api-memory PRIVATE ${COMPILER_OPTIONS})
#add_test(NAME wasm-c-api-memory
# COMMAND wasm-c-api-memory
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example
#)
#
#target_link_libraries(wasm-c-api-global general ${WASMER_LIB})
#target_compile_options(wasm-c-api-global PRIVATE ${COMPILER_OPTIONS})
#add_test(NAME wasm-c-api-global
# COMMAND wasm-c-api-global
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example
#)
#
#target_link_libraries(wasm-c-api-serialize general ${WASMER_LIB})
#target_compile_options(wasm-c-api-serialize PRIVATE ${COMPILER_OPTIONS})
#add_test(NAME wasm-c-api-serialize
# COMMAND wasm-c-api-serialize
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/example
#)

View File

@@ -0,0 +1,3 @@
These are used in tests in the parent directory.
To keep the generated wasm small, use `wasm-opt` and `wasm-strip` from wabt-tools (can be installed via wapm). Addtionally, consider passing the `-C opt-level=z` flag to `rustc` to optimize for size.

View File

@@ -0,0 +1,10 @@
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, world\n");
for ( int i = 0; i < argc; ++i ) {
printf("Arg %d: '%s'\n", i, argv[i]);
}
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,37 @@
#[no_mangle]
pub extern "C" fn sum(x: i32, y: i32) -> i32 {
x + y
}
#[no_mangle]
pub extern "C" fn arity_0() -> i32 {
42
}
#[no_mangle]
pub extern "C" fn i32_i32(x: i32) -> i32 {
x
}
#[no_mangle]
pub extern "C" fn i64_i64(x: i64) -> i64 {
x
}
#[no_mangle]
pub extern "C" fn f32_f32(x: f32) -> f32 {
x
}
#[no_mangle]
pub extern "C" fn f64_f64(x: f64) -> f64 {
x
}
#[no_mangle]
pub extern "C" fn string() -> *const u8 {
b"Hello, World!\0".as_ptr()
}
#[no_mangle]
pub extern "C" fn void() {}

Binary file not shown.

View File

@@ -0,0 +1,31 @@
extern "C" {
fn host_print(ptr: u32, len: u32);
}
fn main() {
let args = std::env::args().collect::<Vec<String>>();
println!("Found {} args on program {}", args.len(), args[0]);
let env_vars = std::env::vars()
.map(|(arg, val)| format!("{}={}", arg, val))
.collect::<Vec<String>>();
let env_var_list = env_vars.join(", ");
println!("Found {} env vars: {}", env_vars.len(), env_var_list);
let dirs_in_root = std::fs::read_dir("/")
.unwrap()
.map(|e| e.map(|inner| format!("{:?}", inner)))
.collect::<Result<Vec<String>, _>>()
.unwrap();
println!(
"Found {} pre opened dirs: {}",
dirs_in_root.len(),
dirs_in_root.join(", ")
);
const HOST_STR: &str = "This string came from a WASI module";
unsafe { host_print(HOST_STR.as_ptr() as u32, HOST_STR.len() as u32) };
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,12 @@
(module
(func $inc (import "env" "inc"))
(func $mul (import "env" "mul"))
(func $get (import "env" "get") (result i32))
(func (export "inc_and_get") (result i32)
call $inc
call $get)
(func (export "mul_and_get") (result i32)
call $mul
call $get))

View File

@@ -0,0 +1,4 @@
#[no_mangle]
pub extern "C" fn return_hello() -> *const u8 {
b"Hello, World!\0"[..].as_ptr()
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,60 @@
use std::process::Command;
#[test]
fn test_c_api() {
let project_tests_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests");
let cmake_args = vec![
".",
#[cfg(feature = "wasi")]
"-DWASI_TESTS=ON",
#[cfg(feature = "emscripten")]
"-DEMSCRIPTEN_TESTS=ON",
// We need something like this to get this working on Windows, but this doesn't seem
// quite right -- perhaps it's double escaping the quotes?
#[cfg(target_os = "windows")]
r#"-G "MinGW Makefiles""#,
];
// we use -f so it doesn't fail if the file doesn't exist
run_command("rm", project_tests_dir, vec!["-f", "CMakeCache.txt"]);
run_command("cmake", project_tests_dir, cmake_args);
run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]);
run_command("make", project_tests_dir, vec!["test", "ARGS=\"-V\""]);
}
fn run_command(command_str: &str, dir: &str, args: Vec<&str>) {
println!("Running command: `{}` args: {:?}", command_str, args);
let mut command = Command::new(command_str);
command.args(&args);
command.current_dir(dir);
let result = command.output();
match result {
Ok(r) => {
println!("output:");
if let Some(code) = r.status.code() {
println!("status: {}", code);
} else {
println!("status: None");
}
println!("stdout:");
println!("{}", String::from_utf8_lossy(&r.stdout[..]));
println!("stderr:");
println!("{}", String::from_utf8_lossy(&r.stderr[..]));
if r.status.success() {
assert!(true)
} else {
panic!("Command failed with exit status: {:?}", r.status);
}
}
Err(e) => panic!("Command failed: {}", e),
}
}

View File

@@ -0,0 +1,137 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int32_t amount;
int32_t value;
} counter_data;
typedef struct {
uint8_t* bytes;
long bytes_len;
} wasm_file_t;
wasm_file_t read_wasm_file(const char* file_name) {
wasm_file_t wasm_file;
FILE *file = fopen(file_name, "r");
fseek(file, 0, SEEK_END);
wasm_file.bytes_len = ftell(file);
wasm_file.bytes = malloc(wasm_file.bytes_len);
fseek(file, 0, SEEK_SET);
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
fclose(file);
return wasm_file;
}
void inc_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value + data->amount;
}
void mul_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value * data->amount;
}
int32_t get_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
return data->value;
}
counter_data *init_counter(int32_t value, int32_t amount) {
counter_data* counter = malloc(sizeof(counter_data));
counter->value = value;
counter->amount = amount;
return counter;
}
void assert_counter(wasmer_instance_t *instance, int32_t expected) {
wasmer_value_t result_one;
wasmer_value_t params[] = {};
wasmer_value_t results[] = {result_one};
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
printf("Call result: %d\n", call1_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == expected);
assert(call1_result == WASMER_OK);
const wasmer_instance_context_t *ctx = wasmer_instance_context_get(instance);
counter_data *cd = (counter_data*)wasmer_instance_context_data_get(ctx);
assert(cd->value == expected);
}
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
wasmer_import_t import;
wasmer_byte_array module_name_bytes;
wasmer_byte_array import_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
return import;
}
int main()
{
// Prepare Imports
wasmer_value_tag inc_params_sig[] = {};
wasmer_value_tag inc_returns_sig[] = {};
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
wasmer_value_tag mul_params_sig[] = {};
wasmer_value_tag mul_returns_sig[] = {};
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
wasmer_value_tag get_params_sig[] = {};
wasmer_value_tag get_returns_sig[] = {WASM_I32};
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
wasmer_import_t get_import = create_import("env", "get", get_func);
wasmer_import_t imports[] = {inc_import, mul_import, get_import};
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_res = wasmer_instantiate(&instance, wasm_file.bytes, wasm_file.bytes_len, imports, 3);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);
wasmer_instance_context_data_set(instance, counter);
// Run `instance.inc_and_get` and assert
assert_counter(instance, 7);
assert_counter(instance, 12);
assert_counter(instance, 17);
// Clear resources
wasmer_import_func_destroy(inc_func);
wasmer_import_func_destroy(get_func);
wasmer_instance_destroy(instance);
free(counter);
free(wasm_file.bytes);
return 0;
}

View File

@@ -0,0 +1,138 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static bool host_print_called = false;
// Host function that will be imported into the Web Assembly Instance
void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
host_print_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
// 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]);
}
}
int main()
{
// Read the Wasm file bytes.
FILE *file = fopen("assets/emscripten_hello_world.wasm", "r");
assert(file);
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
// 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);
// Set up data for Emscripten
wasmer_emscripten_globals_t *emscripten_globals = wasmer_emscripten_get_globals(module);
if (!emscripten_globals)
{
print_wasmer_error();
}
assert(emscripten_globals);
// Create the Emscripten import object
wasmer_import_object_t *import_object =
wasmer_emscripten_generate_import_object(emscripten_globals);
// 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();
}
assert(instantiate_result == WASMER_OK);
// Set up emscripten to be called
wasmer_result_t setup_result = wasmer_emscripten_set_up(instance, emscripten_globals);
printf("Set up result: %d\n", setup_result);
if (setup_result != WASMER_OK)
{
print_wasmer_error();
}
assert(setup_result == WASMER_OK);
const char *emscripten_prog_name = "emscripten_test_program";
const char *emscripten_first_arg = "--help";
wasmer_byte_array args[] = {
{ .bytes = (const uint8_t *) emscripten_prog_name,
.bytes_len = strlen(emscripten_prog_name) },
{ .bytes = (const uint8_t *) emscripten_first_arg,
.bytes_len = strlen(emscripten_first_arg) }
};
int emscripten_argc = sizeof(args) / sizeof(args[0]);
wasmer_result_t main_result = wasmer_emscripten_call_main(instance, args, emscripten_argc);
printf("Main result: %d\n", main_result);
assert(main_result == WASMER_OK);
wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object);
puts("Functions in import object:");
while ( !wasmer_import_object_iter_at_end(func_iter) ) {
wasmer_import_t import;
wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import);
assert(result == WASMER_OK);
print_byte_array(&import.module_name);
putchar(' ');
print_byte_array(&import.import_name);
putchar('\n');
assert(import.tag == WASM_FUNCTION);
assert(import.value.func);
wasmer_import_object_imports_destroy(&import, 1);
}
wasmer_import_object_iter_destroy(func_iter);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_emscripten_destroy_globals(emscripten_globals);
wasmer_instance_destroy(instance);
wasmer_import_object_destroy(import_object);
wasmer_module_destroy(module);
return 0;
}

View File

@@ -0,0 +1,74 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/return_hello.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
// Instantiate the module.
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
// Call the `return_hello` function.
wasmer_value_t params[] = {};
wasmer_value_t result;
wasmer_value_t results[] = {result};
wasmer_result_t call_result = wasmer_instance_call(instance, "return_hello", params, 0, results, 1);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(call_result == WASMER_OK);
assert(results[0].value.I32 == 1048576);
// Get all exports.
wasmer_exports_t *exports = NULL;
wasmer_instance_exports(instance, &exports);
int export_length = wasmer_exports_len(exports);
printf("exports_length: %d\n", export_length);
assert(export_length == 5);
// Get the `memory` export.
wasmer_export_t *export = wasmer_exports_get(exports, 0);
wasmer_import_export_kind kind = wasmer_export_kind(export);
printf("Wasmer import export kind: %d (Memory is %d)\n", kind, WASM_MEMORY);
assert(kind == WASM_MEMORY);
wasmer_byte_array export_name = wasmer_export_name(export);
printf("export_name: `%.*s`\n", export_name.bytes_len, export_name.bytes);
// Cast the export into a memory.
wasmer_memory_t *memory;
wasmer_result_t export_to_memory_result = wasmer_export_to_memory(export, &memory);
printf("Export to memory result: %d\n", export_to_memory_result);
printf("Memory pointer: %p\n", memory);
assert(export_to_memory_result == WASMER_OK);
uint32_t memory_length = wasmer_memory_length(memory);
assert(memory_length == 17);
// Read the data from the memory.
uint8_t *memory_data = wasmer_memory_data(memory);
uint8_t *returned_string = memory_data + results[0].value.I32;
printf("Returned string from Wasm: %s\n", returned_string);
assert(strcmp("Hello, World!", (const char *) returned_string) == 0);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
return 0;
}

View File

@@ -0,0 +1,445 @@
#include <inttypes.h>
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
int main()
{
// Read the WebAssembly bytes.
uint8_t *wasm_bytes = NULL;
long wasm_bytes_length = 0;
{
FILE *file = fopen("assets/exports.wasm", "r");
fseek(file, 0, SEEK_END);
wasm_bytes_length = ftell(file);
wasm_bytes = (uint8_t *) malloc(wasm_bytes_length);
fseek(file, 0, SEEK_SET);
fread(wasm_bytes, 1, wasm_bytes_length, file);
fclose(file);
}
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, wasm_bytes, wasm_bytes_length, imports, 0);
assert(compile_result == WASMER_OK);
wasmer_exports_t *exports = NULL;
wasmer_instance_exports(instance, &exports);
int exports_length = wasmer_exports_len(exports);
printf("Number of exports: %d\n", exports_length);
{
printf("\nCheck the `sum` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 3);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("sum") - 1);
assert(memcmp(name_bytes.bytes, "sum", sizeof("sum") - 1) == 0);
printf("Check arity\n");
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 2);
assert(outputs_arity == 1);
printf("Check signature\n");
wasmer_value_tag *input_types = (wasmer_value_tag *) calloc(inputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_params(exported_function, input_types, inputs_arity);
assert(input_types[0] == WASM_I32);
assert(input_types[1] == WASM_I32);
free(input_types);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_I32);
free(output_types);
printf("Call the exported function\n");
wasmer_value_t input_0;
input_0.tag = WASM_I32;
input_0.value.I32 = 7;
wasmer_value_t input_1;
input_1.tag = WASM_I32;
input_1.value.I32 = 8;
wasmer_value_t inputs[] = {input_0, input_1};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", outputs[0].value.I32);
assert(outputs[0].value.I32 == 15);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `arity_0` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 4);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("arity_0") - 1);
assert(memcmp(name_bytes.bytes, "arity_0", sizeof("arity_0") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 0);
assert(outputs_arity == 1);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_I32);
free(output_types);
wasmer_value_t inputs[] = {};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %d\n", outputs[0].value.I32);
assert(outputs[0].value.I32 == 42);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `i32_i32` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 5);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("i32_i32") - 1);
assert(memcmp(name_bytes.bytes, "i32_i32", sizeof("i32_i32") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 1);
assert(outputs_arity == 1);
wasmer_value_tag *input_types = (wasmer_value_tag *) calloc(inputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_params(exported_function, input_types, inputs_arity);
assert(input_types[0] == WASM_I32);
free(input_types);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_I32);
free(output_types);
wasmer_value_t input_0;
input_0.tag = WASM_I32;
input_0.value.I32 = 7;
wasmer_value_t inputs[] = {input_0};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %d\n", outputs[0].value.I32);
assert(outputs[0].value.I32 == 7);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `i64_i64` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 6);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("i64_i64") - 1);
assert(memcmp(name_bytes.bytes, "i64_i64", sizeof("i64_i64") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 1);
assert(outputs_arity == 1);
wasmer_value_tag *input_types = (wasmer_value_tag *) calloc(inputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_params(exported_function, input_types, inputs_arity);
assert(input_types[0] == WASM_I64);
free(input_types);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_I64);
free(output_types);
wasmer_value_t input_0;
input_0.tag = WASM_I64;
input_0.value.I64 = 7;
wasmer_value_t inputs[] = {input_0};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %" PRId64 "\n", outputs[0].value.I64);
assert(outputs[0].value.I64 == 7);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `f32_f32` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 7);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("f32_f32") - 1);
assert(memcmp(name_bytes.bytes, "f32_f32", sizeof("f32_f32") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 1);
assert(outputs_arity == 1);
wasmer_value_tag *input_types = (wasmer_value_tag *) calloc(inputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_params(exported_function, input_types, inputs_arity);
assert(input_types[0] == WASM_F32);
free(input_types);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_F32);
free(output_types);
wasmer_value_t input_0;
input_0.tag = WASM_F32;
input_0.value.F32 = 7.42;
wasmer_value_t inputs[] = {input_0};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %f\n", outputs[0].value.F32);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `f64_f64` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 8);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("f64_f64") - 1);
assert(memcmp(name_bytes.bytes, "f64_f64", sizeof("f64_f64") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 1);
assert(outputs_arity == 1);
wasmer_value_tag *input_types = (wasmer_value_tag *) calloc(inputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_params(exported_function, input_types, inputs_arity);
assert(input_types[0] == WASM_F64);
free(input_types);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_F64);
free(output_types);
wasmer_value_t input_0;
input_0.tag = WASM_F64;
input_0.value.F64 = 7.42;
wasmer_value_t inputs[] = {input_0};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %f\n", outputs[0].value.F64);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `string` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 9);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("string") - 1);
assert(memcmp(name_bytes.bytes, "string", sizeof("string") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 0);
assert(outputs_arity == 1);
wasmer_value_tag *output_types = (wasmer_value_tag *) calloc(outputs_arity, sizeof(wasmer_value_tag));
wasmer_export_func_returns(exported_function, output_types, outputs_arity);
assert(output_types[0] == WASM_I32);
free(output_types);
wasmer_value_t inputs[] = {};
wasmer_value_t output_0;
wasmer_value_t outputs[] = {output_0};
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
printf("Result: %d\n", outputs[0].value.I32);
assert(outputs[0].value.I32 == 1048576);
assert(call_result == WASMER_OK);
}
{
printf("\nCheck the `void` exported function\n");
wasmer_export_t *export = wasmer_exports_get(exports, 10);
wasmer_import_export_kind export_kind = wasmer_export_kind(export);
assert(export_kind == WASM_FUNCTION);
const wasmer_export_func_t *exported_function = wasmer_export_to_func(export);
wasmer_byte_array name_bytes = wasmer_export_name(export);
assert(name_bytes.bytes_len == sizeof("void") - 1);
assert(memcmp(name_bytes.bytes, "void", sizeof("void") - 1) == 0);
uint32_t inputs_arity;
wasmer_export_func_params_arity(exported_function, &inputs_arity);
uint32_t outputs_arity;
wasmer_export_func_returns_arity(exported_function, &outputs_arity);
assert(inputs_arity == 0);
assert(outputs_arity == 0);
wasmer_value_t inputs[] = {};
wasmer_value_t outputs[] = {};
{
wasmer_result_t call_result = wasmer_export_func_call(exported_function, inputs, inputs_arity, outputs, outputs_arity);
assert(call_result == WASMER_OK);
}
{
wasmer_result_t call_result = wasmer_export_func_call(exported_function, NULL, inputs_arity, NULL, outputs_arity);
assert(call_result == WASMER_OK);
}
}
printf("\nDestroy instance\n");
wasmer_instance_destroy(instance);
printf("Destroy exports\n");
wasmer_exports_destroy(exports);
return 0;
}

View File

@@ -0,0 +1,30 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 7;
wasmer_global_t *global = wasmer_global_new(val, true);
wasmer_value_t get_val = wasmer_global_get(global);
assert( get_val.value.I32 == 7);
wasmer_value_t val2;
val2.tag = WASM_I32;
val2.value.I32 = 14;
wasmer_global_set(global, val2);
wasmer_value_t new_get_val = wasmer_global_get(global);
assert( new_get_val.value.I32 == 14);
wasmer_global_descriptor_t desc = wasmer_global_get_descriptor(global);
assert(desc.mutable_);
assert(desc.kind == WASM_I32);
wasmer_global_destroy(global);
return 0;
}

View File

@@ -0,0 +1,132 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static bool print_str_called = false;
static int memory_len = 0;
static int ptr_len = 0;
static char actual_str[14] = {};
static int actual_context_data_value = 0;
typedef struct {
int value;
} context_data;
struct print_str_context {
int call_count;
};
void print_str(struct print_str_context *local_context, uint64_t *args)
{
local_context->call_count++;
wasmer_instance_context_t *ctx = (void *) args[0];
int32_t ptr = args[1];
int32_t len = args[2];
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
for (int32_t idx = 0; idx < len; idx++)
{
actual_str[idx] = mem_bytes[ptr + idx];
}
actual_str[13] = '\0';
printf("In print_str, memory len: %d, ptr_len: %d\n, str %s", mem_len, len, actual_str);
print_str_called = true;
memory_len = mem_len;
ptr_len = len;
actual_context_data_value = ((context_data *) wasmer_instance_context_data_get(ctx))->value;
}
int main()
{
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
struct print_str_context local_context = {
.call_count = 0
};
printf("Creating trampoline buffer\n");
wasmer_trampoline_buffer_builder_t *tbb = wasmer_trampoline_buffer_builder_new();
unsigned long print_str_idx = wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
tbb,
(wasmer_trampoline_callable_t *) print_str,
(void *) &local_context,
3
);
wasmer_trampoline_buffer_t *tb = wasmer_trampoline_buffer_builder_build(tbb);
const wasmer_trampoline_callable_t *print_str_callable = wasmer_trampoline_buffer_get_trampoline(tb, print_str_idx);
printf("Creating new func\n");
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str_callable, params_sig, 2, returns_sig, 0);
wasmer_import_t import;
char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
char *import_name = "print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
wasmer_import_t imports[] = {import};
// Read the wasm file bytes
FILE *file = fopen("assets/wasm_sample_app.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
context_data* context_data = malloc(sizeof(context_data));
int context_data_value = 42;
context_data->value = context_data_value;
wasmer_instance_context_data_set(instance, context_data);
wasmer_value_t params[] = {};
wasmer_value_t results[] = {};
wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0);
printf("Call result: %d\n", call_result);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(call_result == WASMER_OK);
assert(print_str_called);
assert(memory_len == 17);
assert(ptr_len == 13);
assert(0 == strcmp(actual_str, "Hello, World!"));
assert(context_data_value == actual_context_data_value);
assert(local_context.call_count == 1);
printf("Destroying trampoline buffer\n");
wasmer_trampoline_buffer_destroy(tb);
printf("Destroying func\n");
wasmer_import_func_destroy(func);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
free(context_data);
return 0;
}

View File

@@ -0,0 +1,128 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static bool print_str_called = false;
static int memory_len = 0;
static int ptr_len = 0;
static char actual_str[14] = {};
static int actual_context_data_value = 0;
typedef struct {
int value;
} context_data;
struct print_str_context {
int call_count;
};
void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
struct print_str_context *local_context = wasmer_trampoline_get_context();
local_context->call_count++;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
for (int32_t idx = 0; idx < len; idx++)
{
actual_str[idx] = mem_bytes[ptr + idx];
}
actual_str[13] = '\0';
printf("In print_str, memory len: %d, ptr_len: %d, str %s\n", mem_len, len, actual_str);
print_str_called = true;
memory_len = mem_len;
ptr_len = len;
actual_context_data_value = ((context_data *) wasmer_instance_context_data_get(ctx))->value;
}
int main()
{
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
struct print_str_context local_context = {
.call_count = 0
};
printf("Creating trampoline buffer\n");
wasmer_trampoline_buffer_builder_t *tbb = wasmer_trampoline_buffer_builder_new();
unsigned long print_str_idx = wasmer_trampoline_buffer_builder_add_context_trampoline(
tbb,
(wasmer_trampoline_callable_t *) print_str,
(void *) &local_context
);
wasmer_trampoline_buffer_t *tb = wasmer_trampoline_buffer_builder_build(tbb);
const wasmer_trampoline_callable_t *print_str_callable = wasmer_trampoline_buffer_get_trampoline(tb, print_str_idx);
printf("Creating new func\n");
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str_callable, params_sig, 2, returns_sig, 0);
wasmer_import_t import;
char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
char *import_name = "print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
wasmer_import_t imports[] = {import};
// Read the wasm file bytes
FILE *file = fopen("assets/wasm_sample_app.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
context_data* context_data = malloc(sizeof(context_data));
int context_data_value = 42;
context_data->value = context_data_value;
wasmer_instance_context_data_set(instance, context_data);
wasmer_value_t params[] = {};
wasmer_value_t results[] = {};
wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0);
printf("Call result: %d\n", call_result);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(call_result == WASMER_OK);
assert(print_str_called);
assert(memory_len == 17);
assert(ptr_len == 13);
assert(0 == strcmp(actual_str, "Hello, World!"));
assert(context_data_value == actual_context_data_value);
assert(local_context.call_count == 1);
printf("Destroying trampoline buffer\n");
wasmer_trampoline_buffer_destroy(tb);
printf("Destroying func\n");
wasmer_import_func_destroy(func);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
free(context_data);
return 0;
}

View File

@@ -0,0 +1,172 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
bool static print_str_called = false;
// Host function that will be imported into the Web Assembly Instance
void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
print_str_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
int main()
{
// Create a new func to hold the parameter and signature
// of our `print_str` host function
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
// Create module name for our imports
// represented in bytes for UTF-8 compatability
const char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
// Define a function import
const char *import_name = "_print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
wasmer_import_t func_import;
func_import.module_name = module_name_bytes;
func_import.import_name = import_name_bytes;
func_import.tag = WASM_FUNCTION;
func_import.value.func = func;
// Define a memory import
const char *import_memory_name = "memory";
wasmer_byte_array import_memory_name_bytes;
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
wasmer_import_t memory_import;
memory_import.module_name = module_name_bytes;
memory_import.import_name = import_memory_name_bytes;
memory_import.tag = WASM_MEMORY;
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 256;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 256;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
if (memory_result != WASMER_OK)
{
print_wasmer_error();
}
memory_import.value.memory = memory;
// Define a global import
const char *import_global_name = "__memory_base";
wasmer_byte_array import_global_name_bytes;
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
import_global_name_bytes.bytes_len = strlen(import_global_name);
wasmer_import_t global_import;
global_import.module_name = module_name_bytes;
global_import.import_name = import_global_name_bytes;
global_import.tag = WASM_GLOBAL;
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 1024;
wasmer_global_t *global = wasmer_global_new(val, false);
global_import.value.global = global;
// Define a table import
const char *import_table_name = "table";
wasmer_byte_array import_table_name_bytes;
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
import_table_name_bytes.bytes_len = strlen(import_table_name);
wasmer_import_t table_import;
table_import.module_name = module_name_bytes;
table_import.import_name = import_table_name_bytes;
table_import.tag = WASM_TABLE;
wasmer_table_t *table = NULL;
wasmer_limits_t table_descriptor;
table_descriptor.min = 256;
wasmer_limit_option_t table_max;
table_max.has_some = true;
table_max.some = 256;
table_descriptor.max = table_max;
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
if (table_result != WASMER_OK)
{
print_wasmer_error();
}
table_import.value.table = table;
// Define an empty import object
wasmer_import_object_t *import_object = wasmer_import_object_new();
// 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
FILE *file = fopen("assets/hello_wasm.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
// 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);
// 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();
}
assert(instantiate_result == WASMER_OK);
// Call the exported "hello_wasm" function of our instance
wasmer_value_t params[] = {};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_OK);
assert(print_str_called);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_import_func_destroy(func);
wasmer_global_destroy(global);
wasmer_memory_destroy(memory);
wasmer_table_destroy(table);
wasmer_instance_destroy(instance);
wasmer_import_object_destroy(import_object);
wasmer_module_destroy(module);
return 0;
}

View File

@@ -0,0 +1,80 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static const char *trap_error_message = "Hello";
void print_str(wasmer_instance_context_t *ctx, int32_t _ptr, int32_t _len)
{
wasmer_trap(ctx, trap_error_message);
}
int main()
{
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
printf("Creating new func\n");
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
char *module_name = "env";
wasmer_byte_array module_name_bytes = {
.bytes = (const uint8_t *) module_name,
.bytes_len = strlen(module_name),
};
char *import_name = "print_str";
wasmer_byte_array import_name_bytes = {
.bytes = (const uint8_t *) import_name,
.bytes_len = strlen(import_name),
};
wasmer_import_t import = {
.module_name = module_name_bytes,
.import_name = import_name_bytes,
.tag = WASM_FUNCTION,
.value.func = func,
};
wasmer_import_t imports[] = {import};
// Read the wasm file bytes
FILE *file = fopen("assets/wasm_sample_app.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_value_t params[] = {};
wasmer_value_t results[] = {};
wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_ERROR);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "Call error: \"Hello\""));
printf("Destroying func\n");
wasmer_import_func_destroy(func);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
return 0;
}

View File

@@ -0,0 +1,155 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
bool static print_str_called = false;
// Host function that will be imported into the Web Assembly Instance
void print_str(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
print_str_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
int main()
{
// Create a new func to hold the parameter and signature
// of our `print_str` host function
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0);
// Create module name for our imports
// represented in bytes for UTF-8 compatability
const char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
// Define a function import
const char *import_name = "_print_str";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
wasmer_import_t func_import;
func_import.module_name = module_name_bytes;
func_import.import_name = import_name_bytes;
func_import.tag = WASM_FUNCTION;
func_import.value.func = func;
// Define a memory import
const char *import_memory_name = "memory";
wasmer_byte_array import_memory_name_bytes;
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
wasmer_import_t memory_import;
memory_import.module_name = module_name_bytes;
memory_import.import_name = import_memory_name_bytes;
memory_import.tag = WASM_MEMORY;
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 256;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 256;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
if (memory_result != WASMER_OK)
{
print_wasmer_error();
}
memory_import.value.memory = memory;
// Define a global import
const char *import_global_name = "__memory_base";
wasmer_byte_array import_global_name_bytes;
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
import_global_name_bytes.bytes_len = strlen(import_global_name);
wasmer_import_t global_import;
global_import.module_name = module_name_bytes;
global_import.import_name = import_global_name_bytes;
global_import.tag = WASM_GLOBAL;
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 1024;
wasmer_global_t *global = wasmer_global_new(val, false);
global_import.value.global = global;
// Define a table import
const char *import_table_name = "table";
wasmer_byte_array import_table_name_bytes;
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
import_table_name_bytes.bytes_len = strlen(import_table_name);
wasmer_import_t table_import;
table_import.module_name = module_name_bytes;
table_import.import_name = import_table_name_bytes;
table_import.tag = WASM_TABLE;
wasmer_table_t *table = NULL;
wasmer_limits_t table_descriptor;
table_descriptor.min = 256;
wasmer_limit_option_t table_max;
table_max.has_some = true;
table_max.some = 256;
table_descriptor.max = table_max;
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
if (table_result != WASMER_OK)
{
print_wasmer_error();
}
table_import.value.table = table;
// Define an array containing our imports
wasmer_import_t imports[] = {func_import, global_import, memory_import, table_import};
// Read the wasm file bytes
FILE *file = fopen("assets/hello_wasm.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
// Creates a WebAssembly Instance from wasm bytes and imports
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 4);
printf("Compile result: %d\n", compile_result);
if (compile_result != WASMER_OK)
{
print_wasmer_error();
}
assert(compile_result == WASMER_OK);
// Call the exported "hello_wasm" function of our instance
wasmer_value_t params[] = {};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_OK);
assert(print_str_called);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_import_func_destroy(func);
wasmer_global_destroy(global);
wasmer_memory_destroy(memory);
wasmer_table_destroy(table);
wasmer_instance_destroy(instance);
return 0;
}

View File

@@ -0,0 +1,59 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 0);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_value_t param_one = {
.tag = WASM_I32,
.value.I32 = 7,
};
wasmer_value_t param_two = {
.tag = WASM_I32,
.value.I32 = 8,
};
wasmer_value_t params[] = {param_one, param_two};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
wasmer_result_t call_result2 = wasmer_instance_call(instance, "sum", params, 1, results, 1);
printf("Call result bad: %d\n", call_result2);
assert(call_result2 == WASMER_ERROR);
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
int error_result = wasmer_last_error_message(error_str, error_len);
assert(error_len == error_result);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "RuntimeError: expected 2 arguments, got 1: Parameters of type [I32] did not match signature [I32, I32] -> [I32]"));
free(error_str);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
return 0;
}

View File

@@ -0,0 +1,67 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
int main()
{
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor = {
.min = 10,
.max = {
.has_some = true,
.some = 15,
},
};
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
printf("Memory result: %d\n", memory_result);
assert(memory_result == WASMER_OK);
uint32_t len = wasmer_memory_length(memory);
printf("Memory pages length: %d\n", len);
assert(len == 10);
wasmer_result_t grow_result = wasmer_memory_grow(memory, 2);
assert(grow_result == WASMER_OK);
uint32_t new_len = wasmer_memory_length(memory);
printf("Memory pages length: %d\n", new_len);
assert(new_len == 12);
uint32_t bytes_len = wasmer_memory_data_length(memory);
printf("Memory bytes length: %d\n", bytes_len);
assert(bytes_len == 12 * 65536);
// Err, grow beyond max
wasmer_result_t grow_result2 = wasmer_memory_grow(memory, 10);
assert(grow_result2 == WASMER_ERROR);
int error_len = wasmer_last_error_length();
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
assert(0 == strcmp(error_str, "Failed to add pages because would exceed maximum number of pages for the memory. Left: 22, Added: 15"));
free(error_str);
wasmer_memory_t *bad_memory = NULL;
wasmer_limits_t bad_descriptor;
bad_descriptor.min = 15;
wasmer_limit_option_t max2;
max2.has_some = true;
max2.some = 10;
bad_descriptor.max = max2;
wasmer_result_t bad_memory_result = wasmer_memory_new(&bad_memory, bad_descriptor);
printf("Bad memory result: %d\n", bad_memory_result);
assert(bad_memory_result == WASMER_ERROR);
int error_len2 = wasmer_last_error_length();
char *error_str2 = malloc(error_len2);
wasmer_last_error_message(error_str2, error_len2);
printf("Error str 2: `%s`\n", error_str2);
assert(0 == strcmp(error_str2, "Unable to create because the supplied descriptor is invalid: \"Max number of memory pages is less than the minimum number of pages\""));
free(error_str2);
printf("Destroy memory\n");
wasmer_memory_destroy(memory);
return 0;
}

View File

@@ -0,0 +1,53 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_instantiate(module, &instance, imports, 0);
printf("Instantiate result: %d\n", compile_result);
assert(instantiate_result == WASMER_OK);
wasmer_export_descriptors_t *exports = NULL;
wasmer_export_descriptors(module, &exports);
int exports_len = wasmer_export_descriptors_len(exports);
printf("exports_len: %d\n", exports_len);
assert(exports_len == 1);
wasmer_export_descriptor_t *export = wasmer_export_descriptors_get(exports, 0);
wasmer_import_export_kind kind = wasmer_export_descriptor_kind(export);
assert(kind == WASM_FUNCTION);
wasmer_byte_array name_bytes = wasmer_export_descriptor_name(export);
assert(name_bytes.bytes_len == 3);
char expected[] = {'s', 'u', 'm'};
for(int idx = 0; idx < 3; idx++){
printf("%c\n", name_bytes.bytes[idx]);
assert(name_bytes.bytes[idx] == expected[idx]);
}
printf("Destroy module\n");
wasmer_module_destroy(module);
printf("Destroy exports\n");
wasmer_export_descriptors_destroy(exports);
return 0;
}

View File

@@ -0,0 +1,149 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int32_t amount;
int32_t value;
} counter_data;
typedef struct {
uint8_t* bytes;
long bytes_len;
} wasm_file_t;
wasm_file_t read_wasm_file(const char* file_name) {
wasm_file_t wasm_file;
FILE *file = fopen(file_name, "r");
fseek(file, 0, SEEK_END);
wasm_file.bytes_len = ftell(file);
wasm_file.bytes = malloc(wasm_file.bytes_len);
fseek(file, 0, SEEK_SET);
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
fclose(file);
return wasm_file;
}
void inc_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value + data->amount;
}
void mul_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value * data->amount;
}
int32_t get_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
return data->value;
}
counter_data *init_counter(int32_t value, int32_t amount) {
counter_data* counter = malloc(sizeof(counter_data));
counter->value = value;
counter->amount = amount;
return counter;
}
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
wasmer_import_t import;
wasmer_byte_array module_name_bytes;
wasmer_byte_array import_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
return import;
}
int main()
{
// Prepare Imports
wasmer_value_tag inc_params_sig[] = {};
wasmer_value_tag inc_returns_sig[] = {};
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
wasmer_value_tag mul_params_sig[] = {};
wasmer_value_tag mul_returns_sig[] = {};
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
wasmer_value_tag get_params_sig[] = {};
wasmer_value_tag get_returns_sig[] = {WASM_I32};
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
wasmer_import_t get_import = create_import("env", "get", get_func);
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("assets/inc.wasm");
// Compile module
wasmer_module_t *module = NULL;
wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len);
assert(compile_res == WASMER_OK);
// Prepare Import Object
wasmer_import_object_t *import_object = wasmer_import_object_new();
// First, we import `inc_counter` and `mul_counter`
wasmer_import_t imports[] = {inc_import, mul_import};
wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res == WASMER_OK);
// Now, we'll import `inc_counter` and `mul_counter`
wasmer_import_t more_imports[] = {get_import};
wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1);
assert(extend_res2 == WASMER_OK);
// Same `wasmer_import_object_extend` as the first, doesn't affect anything
wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res3 == WASMER_OK);
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);
wasmer_instance_context_data_set(instance, counter);
wasmer_value_t result_one;
wasmer_value_t params[] = {};
wasmer_value_t results[] = {result_one};
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
printf("Call result: %d\n", call1_result);
printf("Result: %d\n", results[0].value.I32);
wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1);
printf("Call result: %d\n", call2_result);
printf("Result: %d\n", results[0].value.I32);
// Clear resources
wasmer_import_func_destroy(inc_func);
wasmer_import_func_destroy(mul_func);
wasmer_import_func_destroy(get_func);
wasmer_instance_destroy(instance);
free(counter);
free(wasm_file.bytes);
return 0;
}

View File

@@ -0,0 +1,56 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/wasm_sample_app.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_import_descriptors_t *imports = NULL;
wasmer_import_descriptors(module, &imports);
int imports_len = wasmer_import_descriptors_len(imports);
printf("imports_len: %d\n", imports_len);
assert(imports_len == 1);
wasmer_import_descriptor_t *import = wasmer_import_descriptors_get(imports, 0);
wasmer_import_export_kind kind = wasmer_import_descriptor_kind(import);
assert(kind == WASM_FUNCTION);
wasmer_byte_array name_bytes = wasmer_import_descriptor_name(import);
assert(name_bytes.bytes_len == 9);
char expected[] = {'p', 'r', 'i', 'n', 't', '_', 's', 't', 'r'};
for(int idx = 0; idx < 9; idx++){
printf("%c\n", name_bytes.bytes[idx]);
assert(name_bytes.bytes[idx] == expected[idx]);
}
wasmer_byte_array module_name_bytes = wasmer_import_descriptor_module_name(import);
assert(module_name_bytes.bytes_len == 3);
char module_expected[] = {'e', 'n', 'v'};
for(int idx = 0; idx < 3; idx++){
printf("%c\n", module_name_bytes.bytes[idx]);
assert(module_name_bytes.bytes[idx] == module_expected[idx]);
}
printf("Destroy module\n");
wasmer_module_destroy(module);
printf("Destroy imports\n");
wasmer_import_descriptors_destroy(imports);
return 0;
}

View File

@@ -0,0 +1,98 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module_one = NULL;
wasmer_result_t compile_result = wasmer_compile(&module_one, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_serialized_module_t *serialized_module = NULL;
wasmer_result_t serialize_result = wasmer_module_serialize(&serialized_module, module_one);
printf("Serialize result: %d\n", serialize_result);
assert(serialize_result == WASMER_OK);
wasmer_byte_array serialized_module_bytes = wasmer_serialized_module_bytes(serialized_module);
printf("Serialized module pointer: %p\n", serialized_module_bytes.bytes);
printf("Serialized module length: %d\n", serialized_module_bytes.bytes_len);
assert(serialized_module_bytes.bytes != NULL);
assert(serialized_module_bytes.bytes_len > 11);
assert(serialized_module_bytes.bytes[0] == '\0');
assert(serialized_module_bytes.bytes[1] == 'w');
assert(serialized_module_bytes.bytes[2] == 'a');
assert(serialized_module_bytes.bytes[3] == 's');
assert(serialized_module_bytes.bytes[4] == 'm');
assert(serialized_module_bytes.bytes[5] == 'e');
assert(serialized_module_bytes.bytes[6] == 'r');
assert(serialized_module_bytes.bytes[7] == '-');
assert(serialized_module_bytes.bytes[8] == 'j');
assert(serialized_module_bytes.bytes[9] == 'i');
assert(serialized_module_bytes.bytes[10] == 't');
wasmer_module_t *module_two = NULL;
wasmer_result_t unserialize_result = wasmer_module_deserialize(&module_two, serialized_module);
assert(unserialize_result == WASMER_OK);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_instantiate(module_two, &instance, imports, 0);
printf("Instantiate result: %d\n", compile_result);
assert(instantiate_result == WASMER_OK);
wasmer_value_t param_one;
param_one.tag = WASM_I32;
param_one.value.I32 = 7;
wasmer_value_t param_two;
param_two.tag = WASM_I32;
param_two.value.I32 = 8;
wasmer_value_t params[] = {param_one, param_two};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
wasmer_serialized_module_t *serialized_module_two = NULL;
wasmer_result_t serialized_module_from_bytes_result = wasmer_serialized_module_from_bytes(
&serialized_module_two,
serialized_module_bytes.bytes,
serialized_module_bytes.bytes_len
);
assert(serialized_module_from_bytes_result == WASMER_OK);
wasmer_module_t *module_three = NULL;
wasmer_result_t unserialized_result_two = wasmer_module_deserialize(&module_three, serialized_module_two);
assert(unserialized_result_two == WASMER_OK);
wasmer_instance_t *instance_two = NULL;
wasmer_result_t instantiate_result_two = wasmer_module_instantiate(module_three, &instance, imports, 0);
assert(instantiate_result_two == WASMER_OK);
printf("Destroy the serialized module\n");
wasmer_serialized_module_destroy(serialized_module);
wasmer_serialized_module_destroy(serialized_module_two);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
printf("Destroy modules\n");
wasmer_module_destroy(module_one);
wasmer_module_destroy(module_two);
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
wasmer_result_t compile_result = wasmer_compile(&module, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);
wasmer_import_t imports[] = {};
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_result = wasmer_module_instantiate(module, &instance, imports, 0);
printf("Instantiate result: %d\n", compile_result);
assert(instantiate_result == WASMER_OK);
wasmer_value_t param_one;
param_one.tag = WASM_I32;
param_one.value.I32 = 7;
wasmer_value_t param_two;
param_two.tag = WASM_I32;
param_two.value.I32 = 8;
wasmer_value_t params[] = {param_one, param_two};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
wasmer_result_t call_result = wasmer_instance_call(instance, "sum", params, 2, results, 1);
printf("Call result: %d\n", call_result);
printf("Result: %d\n", results[0].value.I32);
assert(results[0].value.I32 == 15);
assert(call_result == WASMER_OK);
printf("Destroy instance\n");
wasmer_instance_destroy(instance);
printf("Destroy module\n");
wasmer_module_destroy(module);
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
wasmer_table_t *table = NULL;
wasmer_limits_t descriptor;
descriptor.min = 10;
wasmer_limit_option_t max;
// max.has_some = false;
max.has_some = true;
max.some = 15;
descriptor.max = max;
wasmer_result_t table_result = wasmer_table_new(&table, descriptor);
printf("Table result: %d\n", table_result);
assert(table_result == WASMER_OK);
uint32_t len = wasmer_table_length(table);
printf("Table length: %d\n", len);
assert(len == 10);
wasmer_result_t grow_result1 = wasmer_table_grow(table, 5);
assert(grow_result1 == WASMER_OK);
uint32_t len_grow1 = wasmer_table_length(table);
printf("Table length: %d\n", len_grow1);
assert(len_grow1 == 15);
// Try to grow beyond max
wasmer_result_t grow_result2 = wasmer_table_grow(table, 1);
assert(grow_result2 == WASMER_ERROR);
uint32_t len_grow2 = wasmer_table_length(table);
printf("Table length: %d\n", len_grow2);
assert(len_grow2 == 15);
wasmer_table_t *table_bad = NULL;
wasmer_limits_t bad_descriptor;
bad_descriptor.min = 15;
wasmer_limit_option_t max2;
max2.has_some = true;
max2.some = 10;
bad_descriptor.max = max2;
wasmer_result_t table_bad_result = wasmer_table_new(&table_bad, bad_descriptor);
printf("Table result: %d\n", table_bad_result);
assert(table_bad_result == WASMER_ERROR);
printf("Destroy table\n");
wasmer_table_destroy(table);
return 0;
}

View File

@@ -0,0 +1,22 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
int main()
{
// Read the wasm file bytes
FILE *file = fopen("assets/sum.wasm", "r");
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
bool result = wasmer_validate(bytes, len);
printf("Result: %d", result);
assert(result);
return 0;
}

View File

@@ -0,0 +1,260 @@
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
static bool host_print_called = false;
// Host function that will be imported into the Web Assembly Instance
void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
{
host_print_called = true;
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
uint32_t mem_len = wasmer_memory_length(memory);
uint8_t *mem_bytes = wasmer_memory_data(memory);
printf("%.*s", len, mem_bytes + ptr);
}
// Use the last_error API to retrieve error messages
void print_wasmer_error()
{
int error_len = wasmer_last_error_length();
printf("Error len: `%d`\n", error_len);
char *error_str = malloc(error_len);
wasmer_last_error_message(error_str, error_len);
printf("Error str: `%s`\n", error_str);
}
// 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]);
}
}
int main()
{
// Create a new func to hold the parameter and signature
// of our `host_print` host function
wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32};
wasmer_value_tag returns_sig[] = {};
wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0);
// Create module name for our imports
// represented in bytes for UTF-8 compatability
const char *module_name = "env";
wasmer_byte_array module_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
// Define a function import
const char *import_name = "host_print";
wasmer_byte_array import_name_bytes;
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
wasmer_import_t func_import;
func_import.module_name = module_name_bytes;
func_import.import_name = import_name_bytes;
func_import.tag = WASM_FUNCTION;
func_import.value.func = func;
// Define a memory import
const char *import_memory_name = "memory";
wasmer_byte_array import_memory_name_bytes;
import_memory_name_bytes.bytes = (const uint8_t *) import_memory_name;
import_memory_name_bytes.bytes_len = strlen(import_memory_name);
wasmer_import_t memory_import;
memory_import.module_name = module_name_bytes;
memory_import.import_name = import_memory_name_bytes;
memory_import.tag = WASM_MEMORY;
wasmer_memory_t *memory = NULL;
wasmer_limits_t descriptor;
descriptor.min = 256;
wasmer_limit_option_t max;
max.has_some = true;
max.some = 256;
descriptor.max = max;
wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor);
if (memory_result != WASMER_OK)
{
print_wasmer_error();
}
memory_import.value.memory = memory;
// Define a global import
const char *import_global_name = "__memory_base";
wasmer_byte_array import_global_name_bytes;
import_global_name_bytes.bytes = (const uint8_t *) import_global_name;
import_global_name_bytes.bytes_len = strlen(import_global_name);
wasmer_import_t global_import;
global_import.module_name = module_name_bytes;
global_import.import_name = import_global_name_bytes;
global_import.tag = WASM_GLOBAL;
wasmer_value_t val;
val.tag = WASM_I32;
val.value.I32 = 1024;
wasmer_global_t *global = wasmer_global_new(val, false);
global_import.value.global = global;
// Define a table import
const char *import_table_name = "table";
wasmer_byte_array import_table_name_bytes;
import_table_name_bytes.bytes = (const uint8_t *) import_table_name;
import_table_name_bytes.bytes_len = strlen(import_table_name);
wasmer_import_t table_import;
table_import.module_name = module_name_bytes;
table_import.import_name = import_table_name_bytes;
table_import.tag = WASM_TABLE;
wasmer_table_t *table = NULL;
wasmer_limits_t table_descriptor;
table_descriptor.min = 256;
wasmer_limit_option_t table_max;
table_max.has_some = true;
table_max.some = 256;
table_descriptor.max = table_max;
wasmer_result_t table_result = wasmer_table_new(&table, table_descriptor);
if (table_result != WASMER_OK)
{
print_wasmer_error();
}
table_import.value.table = table;
// Create arbitrary arguments for our program
// Set up data for our WASI import object
//
// Environment variables and program arguments are processed by the WASI
// program. They will not have any effects unless the program includes
// logic to process them.
const char *wasi_prog_name = "wasi_test_program";
const char *wasi_first_arg = "--help";
wasmer_byte_array args[] = {
{ .bytes = (const uint8_t *) wasi_prog_name,
.bytes_len = strlen(wasi_prog_name) },
{ .bytes = (const uint8_t *) wasi_first_arg,
.bytes_len = strlen(wasi_first_arg) }
};
int wasi_argc = sizeof(args) / sizeof(args[0]);
// Create arbitrary environment variables for our program;
const char *wasi_color_env = "COLOR=TRUE";
const char *wasi_app_should_log = "APP_SHOULD_LOG=FALSE";
wasmer_byte_array envs[] = {
{ .bytes = (const uint8_t *) wasi_color_env,
.bytes_len = strlen(wasi_color_env) },
{ .bytes = (const uint8_t *) wasi_app_should_log,
.bytes_len = strlen(wasi_app_should_log) }
};
int wasi_env_len = sizeof(args) / sizeof(args[0]);
// Open the host's current directory under a different name.
// WARNING: this gives the WASI module limited access to your host's file system,
// use caution when granting these permissions to untrusted Wasm modules.
const char *wasi_map_dir_alias = "the_host_current_dir";
const char *wasi_map_dir_host_path = ".";
wasmer_wasi_map_dir_entry_t mapped_dirs[] = {
{ .alias =
{ .bytes = (const uint8_t *) wasi_map_dir_alias,
.bytes_len = strlen(wasi_map_dir_alias) },
.host_file_path =
{ .bytes = (const uint8_t *) wasi_map_dir_host_path,
.bytes_len = strlen(wasi_map_dir_host_path) } }
};
int mapped_dir_len = sizeof(mapped_dirs) / sizeof(mapped_dirs[0]);
// Read the Wasm file bytes.
FILE *file = fopen("assets/extended_wasi.wasm", "r");
assert(file);
fseek(file, 0, SEEK_END);
long len = ftell(file);
uint8_t *bytes = malloc(len);
fseek(file, 0, SEEK_SET);
fread(bytes, 1, len, file);
fclose(file);
wasmer_module_t *module = NULL;
// 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();
}
assert(instantiate_result == WASMER_OK);
// Call the exported "hello_wasm" function of our instance
wasmer_value_t params[] = {};
wasmer_value_t result_one;
wasmer_value_t results[] = {result_one};
// _start runs before main for WASI programs
wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1);
printf("Call result: %d\n", call_result);
assert(call_result == WASMER_OK);
assert(host_print_called);
wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object);
puts("Functions in import object:");
while ( !wasmer_import_object_iter_at_end(func_iter) ) {
wasmer_import_t import;
wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import);
assert(result == WASMER_OK);
print_byte_array(&import.module_name);
putchar(' ');
print_byte_array(&import.import_name);
putchar('\n');
assert(import.tag == WASM_FUNCTION);
assert(import.value.func);
wasmer_import_object_imports_destroy(&import, 1);
}
wasmer_import_object_iter_destroy(func_iter);
// Use *_destroy methods to cleanup as specified in the header documentation
wasmer_import_func_destroy(func);
wasmer_global_destroy(global);
wasmer_memory_destroy(memory);
wasmer_table_destroy(table);
wasmer_instance_destroy(instance);
wasmer_import_object_destroy(import_object);
wasmer_module_destroy(module);
return 0;
}

2
lib/c-api/tests/wasm-c-api/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
out
v8

View File

@@ -0,0 +1,18 @@
FROM ubuntu:bionic
RUN apt-get update && apt-get install -y \
apt-utils \
clang \
cmake \
curl \
git \
libc++-dev \
libc++abi-dev \
libglib2.0-dev \
libgmp-dev \
ninja-build \
python
ADD . /code/wasm-c-api
WORKDIR /code/wasm-c-api
RUN make v8-checkout
RUN make -j v8
RUN make

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,111 @@
# WebAssembly C and C++ API
Work in progress! No docs yet.
### Design Goals
* Provide a "black box" API for embedding a Wasm engine in other C/C++ applications.
* Be completely agnostic to VM specifics.
* Non-goal: "white box" interoperability with embedder (such as combined GC instead of mere finalisation) -- *much* more difficult to achieve.
* Allow creation of bindings for other languages through typical C foreign function interfaces.
* Support a plain C API.
* Stick to mostly manual memory management of interface objects.
* Avoid language features that raise barrier to use.
* E.g., no exceptions or post-C++11 features in C++ API.
* E.g., no passing of structs by-value or post-C99 features in C API.
* Achieve link-time compatibility between different implementations.
* All implementation-dependent API classes are abstract and can be instantiated through factory methods only.
### Interfaces
* C++ API:
* See `include/wasm.hh` for interface.
* See `example/*.cc` for example usages.
* C API:
* See `include/wasm.h` for interface.
* See `example/*.c` for example usages.
Some random explanations:
* The VM must be initialised by creating an instance of an *engine* (`wasm::Engine`/`wasm_engine_t`) and is shut down by deleting it. Such an instance may only be created once per process.
* All runtime objects are tied to a specific *store* (`wasm::Store`/`wasm_store_t`). Multiple stores can be created, but their objects cannot interact. Every store and its objects must only be accessed in a single thread.
* To exchange module objects between threads, create a *shared* module (`wasm::Shared<Module>`/`wasm_shared_module_t`). Other objects cannot be shared in current Wasm.
* *Vector* structures (`wasm::vec<X>`/`wasm_x_vec_t`) are lightweight abstractions of a pair of a plain array and its length. The C++ API does not use `std::vector` because that does not support adopting pre-existing arrays.
* *References* point to runtime objects, but may involve internal indirections, which may or may not be cached. Thus, pointer equality on `Ref*` or subclasses cannot be used to compare identity of the underlying objects (`Ref::eq` may be added later). However, `nullptr`/`NULL` uniquely represents null references.
* The API already encompasses current proposals like [multiple return values](https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md) and [reference types](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md), but not yet [threads](https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md).
### Prototype Implementation
* This repo contains a prototype implementation based on V8 is in `src`.
* Note that this requires adding a module to V8, so it patches V8's build file.
* The C API is implemented on top of the C++ API.
* See `Makefile` for build recipe. Canonical steps to run examples:
1. `make v8-checkout`
2. `make v8`
3. `make all`
#### Limitations
V8 implementation:
* Currently requires patching V8 by adding a module.
* Host functions (`Func::make`) create a JavaScript function internally, since V8 cannot handle raw C imports yet.
* As a consequence, does not support multiple results in external calls or host functions.
* Host functions and host globals are created through auxiliary modules constructed on the fly, to work around limitations in JS API.
* `Shared<Module>` is currently implemented via serialisation, since V8 does not currently have direct support for cross-isolate sharing.
### Other Implementations
Currently, known implementations of this API are included in
* V8 natively (both C and C++)
* Wabt (only C?)
* Wasmtime (only C?)
### TODO
Possible API tweaks:
* Add `Ref::eq` (or better, a subclass `EqRef::eq`) for reference equality?
* Add a way to return error messages from `Module::make` and `Module::validate`.
* Use `restrict` in C API?
* Find a way to perform C callbacks through C++ without extra wrapper?
* Add iterators to `vec` class?

View File

@@ -0,0 +1,168 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
// Print a Wasm value
void wasm_val_print(wasm_val_t val) {
switch (val.kind) {
case WASM_I32: {
printf("%" PRIu32, val.of.i32);
} break;
case WASM_I64: {
printf("%" PRIu64, val.of.i64);
} break;
case WASM_F32: {
printf("%f", val.of.f32);
} break;
case WASM_F64: {
printf("%g", val.of.f64);
} break;
case WASM_ANYREF:
case WASM_FUNCREF: {
if (val.of.ref == NULL) {
printf("null");
} else {
printf("ref(%p)", val.of.ref);
}
} break;
}
}
// A function to be called from Wasm code.
own wasm_trap_t* print_callback(
const wasm_val_t args[], wasm_val_t results[]
) {
printf("Calling back...\n> ");
wasm_val_print(args[0]);
printf("\n");
wasm_val_copy(&results[0], &args[0]);
return NULL;
}
// A function closure.
own wasm_trap_t* closure_callback(
void* env, const wasm_val_t args[], wasm_val_t results[]
) {
int i = *(int*)env;
printf("Calling back closure...\n");
printf("> %d\n", i);
results[0].kind = WASM_I32;
results[0].of.i32 = (int32_t)i;
return NULL;
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("callback.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external print functions.
printf("Creating callback...\n");
own wasm_functype_t* print_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32());
own wasm_func_t* print_func = wasm_func_new(store, print_type, print_callback);
int i = 42;
own wasm_functype_t* closure_type = wasm_functype_new_0_1(wasm_valtype_new_i32());
own wasm_func_t* closure_func = wasm_func_new_with_env(store, closure_type, closure_callback, &i, NULL);
wasm_functype_delete(print_type);
wasm_functype_delete(closure_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = {
wasm_func_as_extern(print_func), wasm_func_as_extern(closure_func)
};
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_func_delete(print_func);
wasm_func_delete(closure_func);
// Extract export.
printf("Extracting export...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}
wasm_module_delete(module);
wasm_instance_delete(instance);
// Call.
printf("Calling export...\n");
wasm_val_t args[2];
args[0].kind = WASM_I32;
args[0].of.i32 = 3;
args[1].kind = WASM_I32;
args[1].of.i32 = 4;
wasm_val_t results[1];
if (wasm_func_call(run_func, args, results)) {
printf("> Error calling function!\n");
return 1;
}
wasm_extern_vec_delete(&exports);
// Print result.
printf("Printing result...\n");
printf("> %u\n", results[0].of.i32);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,145 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
// Print a Wasm value
auto operator<<(std::ostream& out, const wasm::Val& val) -> std::ostream& {
switch (val.kind()) {
case wasm::ValKind::I32: {
out << val.i32();
} break;
case wasm::ValKind::I64: {
out << val.i64();
} break;
case wasm::ValKind::F32: {
out << val.f32();
} break;
case wasm::ValKind::F64: {
out << val.f64();
} break;
case wasm::ValKind::ANYREF:
case wasm::ValKind::FUNCREF: {
if (val.ref() == nullptr) {
out << "null";
} else {
out << "ref(" << val.ref() << ")";
}
} break;
}
return out;
}
// A function to be called from Wasm code.
auto print_callback(
const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap> {
std::cout << "Calling back..." << std::endl << "> " << args[0] << std::endl;
results[0] = args[0].copy();
return nullptr;
}
// A function closure.
auto closure_callback(
void* env, const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap> {
auto i = *reinterpret_cast<int*>(env);
std::cout << "Calling back closure..." << std::endl;
std::cout << "> " << i << std::endl;
results[0] = wasm::Val::i32(static_cast<int32_t>(i));
return nullptr;
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("callback.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Create external print functions.
std::cout << "Creating callback..." << std::endl;
auto print_type = wasm::FuncType::make(
wasm::ownvec<wasm::ValType>::make(wasm::ValType::make(wasm::ValKind::I32)),
wasm::ownvec<wasm::ValType>::make(wasm::ValType::make(wasm::ValKind::I32))
);
auto print_func = wasm::Func::make(store, print_type.get(), print_callback);
// Creating closure.
std::cout << "Creating closure..." << std::endl;
int i = 42;
auto closure_type = wasm::FuncType::make(
wasm::ownvec<wasm::ValType>::make(),
wasm::ownvec<wasm::ValType>::make(wasm::ValType::make(wasm::ValKind::I32))
);
auto closure_func = wasm::Func::make(store, closure_type.get(), closure_callback, &i);
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {print_func.get(), closure_func.get()};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
exit(1);
}
// Extract export.
std::cout << "Extracting export..." << std::endl;
auto exports = instance->exports();
if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) {
std::cout << "> Error accessing export!" << std::endl;
exit(1);
}
auto run_func = exports[0]->func();
// Call.
std::cout << "Calling export..." << std::endl;
wasm::Val args[] = {wasm::Val::i32(3), wasm::Val::i32(4)};
wasm::Val results[1];
if (run_func->call(args, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
// Print result.
std::cout << "Printing result..." << std::endl;
std::cout << "> " << results[0].i32() << std::endl;
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,10 @@
(module
(func $print (import "" "print") (param i32) (result i32))
(func $closure (import "" "closure") (result i32))
(func (export "run") (param $x i32) (param $y i32) (result i32)
(i32.add
(call $print (i32.add (local.get $x) (local.get $y)))
(call $closure)
)
)
)

View File

@@ -0,0 +1,109 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
const int iterations = 100000;
int live_count = 0;
void finalize(void* data) {
int i = (int)data;
if (i % (iterations / 10) == 0) printf("Finalizing #%d...\n", i);
--live_count;
}
void run_in_store(wasm_store_t* store) {
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("finalize.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
exit(1);
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
exit(1);
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
exit(1);
}
wasm_byte_vec_delete(&binary);
// Instantiate.
printf("Instantiating modules...\n");
for (int i = 0; i <= iterations; ++i) {
if (i % (iterations / 10) == 0) printf("%d\n", i);
own wasm_instance_t* instance =
wasm_instance_new(store, module, NULL, NULL);
if (!instance) {
printf("> Error instantiating module %d!\n", i);
exit(1);
}
void* data = (void*)(intptr_t)i;
wasm_instance_set_host_info_with_finalizer(instance, data, &finalize);
wasm_instance_delete(instance);
++live_count;
}
wasm_module_delete(module);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
printf("Live count %d\n", live_count);
printf("Creating store 1...\n");
wasm_store_t* store1 = wasm_store_new(engine);
printf("Running in store 1...\n");
run_in_store(store1);
printf("Live count %d\n", live_count);
printf("Creating store 2...\n");
wasm_store_t* store2 = wasm_store_new(engine);
printf("Running in store 2...\n");
run_in_store(store2);
printf("Live count %d\n", live_count);
printf("Deleting store 2...\n");
wasm_store_delete(store2);
printf("Live count %d\n", live_count);
printf("Running in store 1...\n");
run_in_store(store1);
printf("Live count %d\n", live_count);
printf("Deleting store 1...\n");
wasm_store_delete(store1);
printf("Live count %d\n", live_count);
assert(live_count == 0);
// Shut down.
printf("Shutting down...\n");
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,103 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
const int iterations = 100000;
int live_count = 0;
void finalize(void* data) {
intptr_t i = reinterpret_cast<intptr_t>(data);
if (i % (iterations / 10) == 0) {
std::cout << "Finalizing #" << i << "..." << std::endl;
}
--live_count;
}
void run_in_store(wasm::Store* store) {
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("finalize.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Instantiate.
std::cout << "Instantiating modules..." << std::endl;
for (int i = 0; i <= iterations; ++i) {
if (i % (iterations / 10) == 0) std::cout << i << std::endl;
auto instance = wasm::Instance::make(store, module.get(), nullptr);
if (!instance) {
std::cout << "> Error instantiating module " << i << "!" << std::endl;
exit(1);
}
instance->set_host_info(reinterpret_cast<void*>(i), &finalize);
++live_count;
}
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
std::cout << "Live count " << live_count << std::endl;
std::cout << "Creating store 1..." << std::endl;
auto store1 = wasm::Store::make(engine.get());
std::cout << "Running in store 1..." << std::endl;
run_in_store(store1.get());
std::cout << "Live count " << live_count << std::endl;
{
std::cout << "Creating store 2..." << std::endl;
auto store2 = wasm::Store::make(engine.get());
std::cout << "Running in store 2..." << std::endl;
run_in_store(store2.get());
std::cout << "Live count " << live_count << std::endl;
std::cout << "Deleting store 2..." << std::endl;
std::cout << "Live count " << live_count << std::endl;
}
std::cout << "Running in store 1..." << std::endl;
run_in_store(store1.get());
std::cout << "Live count " << live_count << std::endl;
std::cout << "Deleting store 1..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Live count " << live_count << std::endl;
assert(live_count == 0);
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,5 @@
(module
(func (export "f"))
(func (export "g"))
(func (export "h"))
)

View File

@@ -0,0 +1,232 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) {
printf("> Error accessing global export %zu!\n", i);
exit(1);
}
return wasm_extern_as_global(exports->data[i]);
}
wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) {
printf("> Error accessing function export %zu!\n", i);
exit(1);
}
return wasm_extern_as_func(exports->data[i]);
}
#define check(val, type, expected) \
if (val.of.type != expected) { \
printf("> Error reading value\n"); \
exit(1); \
}
#define check_global(global, type, expected) \
{ \
wasm_val_t val; \
wasm_global_get(global, &val); \
check(val, type, expected); \
}
#define check_call(func, type, expected) \
{ \
wasm_val_t results[1]; \
wasm_func_call(func, NULL, results); \
check(results[0], type, expected); \
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("global.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external globals.
printf("Creating globals...\n");
own wasm_globaltype_t* const_f32_type = wasm_globaltype_new(
wasm_valtype_new(WASM_F32), WASM_CONST);
own wasm_globaltype_t* const_i64_type = wasm_globaltype_new(
wasm_valtype_new(WASM_I64), WASM_CONST);
own wasm_globaltype_t* var_f32_type = wasm_globaltype_new(
wasm_valtype_new(WASM_F32), WASM_VAR);
own wasm_globaltype_t* var_i64_type = wasm_globaltype_new(
wasm_valtype_new(WASM_I64), WASM_VAR);
wasm_val_t val_f32_1 = {.kind = WASM_F32, .of = {.f32 = 1}};
own wasm_global_t* const_f32_import =
wasm_global_new(store, const_f32_type, &val_f32_1);
wasm_val_t val_i64_2 = {.kind = WASM_I64, .of = {.i64 = 2}};
own wasm_global_t* const_i64_import =
wasm_global_new(store, const_i64_type, &val_i64_2);
wasm_val_t val_f32_3 = {.kind = WASM_F32, .of = {.f32 = 3}};
own wasm_global_t* var_f32_import =
wasm_global_new(store, var_f32_type, &val_f32_3);
wasm_val_t val_i64_4 = {.kind = WASM_I64, .of = {.i64 = 4}};
own wasm_global_t* var_i64_import =
wasm_global_new(store, var_i64_type, &val_i64_4);
wasm_globaltype_delete(const_f32_type);
wasm_globaltype_delete(const_i64_type);
wasm_globaltype_delete(var_f32_type);
wasm_globaltype_delete(var_i64_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = {
wasm_global_as_extern(const_f32_import),
wasm_global_as_extern(const_i64_import),
wasm_global_as_extern(var_f32_import),
wasm_global_as_extern(var_i64_import)
};
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_module_delete(module);
// Extract export.
printf("Extracting exports...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
size_t i = 0;
wasm_global_t* const_f32_export = get_export_global(&exports, i++);
wasm_global_t* const_i64_export = get_export_global(&exports, i++);
wasm_global_t* var_f32_export = get_export_global(&exports, i++);
wasm_global_t* var_i64_export = get_export_global(&exports, i++);
wasm_func_t* get_const_f32_import = get_export_func(&exports, i++);
wasm_func_t* get_const_i64_import = get_export_func(&exports, i++);
wasm_func_t* get_var_f32_import = get_export_func(&exports, i++);
wasm_func_t* get_var_i64_import = get_export_func(&exports, i++);
wasm_func_t* get_const_f32_export = get_export_func(&exports, i++);
wasm_func_t* get_const_i64_export = get_export_func(&exports, i++);
wasm_func_t* get_var_f32_export = get_export_func(&exports, i++);
wasm_func_t* get_var_i64_export = get_export_func(&exports, i++);
wasm_func_t* set_var_f32_import = get_export_func(&exports, i++);
wasm_func_t* set_var_i64_import = get_export_func(&exports, i++);
wasm_func_t* set_var_f32_export = get_export_func(&exports, i++);
wasm_func_t* set_var_i64_export = get_export_func(&exports, i++);
// Try cloning.
own wasm_global_t* copy = wasm_global_copy(var_f32_import);
assert(wasm_global_same(var_f32_import, copy));
wasm_global_delete(copy);
// Interact.
printf("Accessing globals...\n");
// Check initial values.
check_global(const_f32_import, f32, 1);
check_global(const_i64_import, i64, 2);
check_global(var_f32_import, f32, 3);
check_global(var_i64_import, i64, 4);
check_global(const_f32_export, f32, 5);
check_global(const_i64_export, i64, 6);
check_global(var_f32_export, f32, 7);
check_global(var_i64_export, i64, 8);
check_call(get_const_f32_import, f32, 1);
check_call(get_const_i64_import, i64, 2);
check_call(get_var_f32_import, f32, 3);
check_call(get_var_i64_import, i64, 4);
check_call(get_const_f32_export, f32, 5);
check_call(get_const_i64_export, i64, 6);
check_call(get_var_f32_export, f32, 7);
check_call(get_var_i64_export, i64, 8);
// Modify variables through API and check again.
wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 33}};
wasm_global_set(var_f32_import, &val33);
wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 34}};
wasm_global_set(var_i64_import, &val34);
wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 37}};
wasm_global_set(var_f32_export, &val37);
wasm_val_t val38 = {.kind = WASM_I64, .of = {.i64 = 38}};
wasm_global_set(var_i64_export, &val38);
check_global(var_f32_import, f32, 33);
check_global(var_i64_import, i64, 34);
check_global(var_f32_export, f32, 37);
check_global(var_i64_export, i64, 38);
check_call(get_var_f32_import, f32, 33);
check_call(get_var_i64_import, i64, 34);
check_call(get_var_f32_export, f32, 37);
check_call(get_var_i64_export, i64, 38);
// Modify variables through calls and check again.
wasm_val_t args73[] = { {.kind = WASM_F32, .of = {.f32 = 73}} };
wasm_func_call(set_var_f32_import, args73, NULL);
wasm_val_t args74[] = { {.kind = WASM_I64, .of = {.i64 = 74}} };
wasm_func_call(set_var_i64_import, args74, NULL);
wasm_val_t args77[] = { {.kind = WASM_F32, .of = {.f32 = 77}} };
wasm_func_call(set_var_f32_export, args77, NULL);
wasm_val_t args78[] = { {.kind = WASM_I64, .of = {.i64 = 78}} };
wasm_func_call(set_var_i64_export, args78, NULL);
check_global(var_f32_import, f32, 73);
check_global(var_i64_import, i64, 74);
check_global(var_f32_export, f32, 77);
check_global(var_i64_export, i64, 78);
check_call(get_var_f32_import, f32, 73);
check_call(get_var_i64_import, i64, 74);
check_call(get_var_f32_export, f32, 77);
check_call(get_var_i64_export, i64, 78);
wasm_global_delete(const_f32_import);
wasm_global_delete(const_i64_import);
wasm_global_delete(var_f32_import);
wasm_global_delete(var_i64_import);
wasm_extern_vec_delete(&exports);
wasm_instance_delete(instance);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,196 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
auto get_export_global(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Global* {
if (exports.size() <= i || !exports[i]->global()) {
std::cout << "> Error accessing global export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->global();
}
auto get_export_func(const wasm::ownvec<wasm::Extern>& exports, size_t i) -> const wasm::Func* {
if (exports.size() <= i || !exports[i]->func()) {
std::cout << "> Error accessing function export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->func();
}
template<class T, class U>
void check(T actual, U expected) {
if (actual != expected) {
std::cout << "> Error reading value, expected " << expected << ", got " << actual << std::endl;
exit(1);
}
}
auto call(const wasm::Func* func) -> wasm::Val {
wasm::Val results[1];
if (func->call(nullptr, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
return results[0].copy();
}
void call(const wasm::Func* func, wasm::Val&& arg) {
wasm::Val args[1] = {std::move(arg)};
if (func->call(args)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("global.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Create external globals.
std::cout << "Creating globals..." << std::endl;
auto const_f32_type = wasm::GlobalType::make(
wasm::ValType::make(wasm::ValKind::F32), wasm::Mutability::CONST);
auto const_i64_type = wasm::GlobalType::make(
wasm::ValType::make(wasm::ValKind::I64), wasm::Mutability::CONST);
auto var_f32_type = wasm::GlobalType::make(
wasm::ValType::make(wasm::ValKind::F32), wasm::Mutability::VAR);
auto var_i64_type = wasm::GlobalType::make(
wasm::ValType::make(wasm::ValKind::I64), wasm::Mutability::VAR);
auto const_f32_import = wasm::Global::make(store, const_f32_type.get(), wasm::Val::f32(1));
auto const_i64_import = wasm::Global::make(store, const_i64_type.get(), wasm::Val::i64(2));
auto var_f32_import = wasm::Global::make(store, var_f32_type.get(), wasm::Val::f32(3));
auto var_i64_import = wasm::Global::make(store, var_i64_type.get(), wasm::Val::i64(4));
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {
const_f32_import.get(), const_i64_import.get(),
var_f32_import.get(), var_i64_import.get()
};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
exit(1);
}
// Extract export.
std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports();
size_t i = 0;
auto const_f32_export = get_export_global(exports, i++);
auto const_i64_export = get_export_global(exports, i++);
auto var_f32_export = get_export_global(exports, i++);
auto var_i64_export = get_export_global(exports, i++);
auto get_const_f32_import = get_export_func(exports, i++);
auto get_const_i64_import = get_export_func(exports, i++);
auto get_var_f32_import = get_export_func(exports, i++);
auto get_var_i64_import = get_export_func(exports, i++);
auto get_const_f32_export = get_export_func(exports, i++);
auto get_const_i64_export = get_export_func(exports, i++);
auto get_var_f32_export = get_export_func(exports, i++);
auto get_var_i64_export = get_export_func(exports, i++);
auto set_var_f32_import = get_export_func(exports, i++);
auto set_var_i64_import = get_export_func(exports, i++);
auto set_var_f32_export = get_export_func(exports, i++);
auto set_var_i64_export = get_export_func(exports, i++);
// Try cloning.
assert(var_f32_import->copy()->same(var_f32_import.get()));
// Interact.
std::cout << "Accessing globals..." << std::endl;
// Check initial values.
check(const_f32_import->get().f32(), 1);
check(const_i64_import->get().i64(), 2);
check(var_f32_import->get().f32(), 3);
check(var_i64_import->get().i64(), 4);
check(const_f32_export->get().f32(), 5);
check(const_i64_export->get().i64(), 6);
check(var_f32_export->get().f32(), 7);
check(var_i64_export->get().i64(), 8);
check(call(get_const_f32_import).f32(), 1);
check(call(get_const_i64_import).i64(), 2);
check(call(get_var_f32_import).f32(), 3);
check(call(get_var_i64_import).i64(), 4);
check(call(get_const_f32_export).f32(), 5);
check(call(get_const_i64_export).i64(), 6);
check(call(get_var_f32_export).f32(), 7);
check(call(get_var_i64_export).i64(), 8);
// Modify variables through API and check again.
var_f32_import->set(wasm::Val::f32(33));
var_i64_import->set(wasm::Val::i64(34));
var_f32_export->set(wasm::Val::f32(37));
var_i64_export->set(wasm::Val::i64(38));
check(var_f32_import->get().f32(), 33);
check(var_i64_import->get().i64(), 34);
check(var_f32_export->get().f32(), 37);
check(var_i64_export->get().i64(), 38);
check(call(get_var_f32_import).f32(), 33);
check(call(get_var_i64_import).i64(), 34);
check(call(get_var_f32_export).f32(), 37);
check(call(get_var_i64_export).i64(), 38);
// Modify variables through calls and check again.
call(set_var_f32_import, wasm::Val::f32(73));
call(set_var_i64_import, wasm::Val::i64(74));
call(set_var_f32_export, wasm::Val::f32(77));
call(set_var_i64_export, wasm::Val::i64(78));
check(var_f32_import->get().f32(), 73);
check(var_i64_import->get().i64(), 74);
check(var_f32_export->get().f32(), 77);
check(var_i64_export->get().i64(), 78);
check(call(get_var_f32_import).f32(), 73);
check(call(get_var_i64_import).i64(), 74);
check(call(get_var_f32_export).f32(), 77);
check(call(get_var_i64_export).i64(), 78);
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,27 @@
(module
(global $f32_import (import "" "const f32") f32)
(global $i64_import (import "" "const i64") i64)
(global $mut_f32_import (import "" "var f32") (mut f32))
(global $mut_i64_import (import "" "var i64") (mut i64))
(global $f32_export (export "const f32") f32 (f32.const 5))
(global $i64_export (export "const i64") i64 (i64.const 6))
(global $mut_f32_export (export "var f32") (mut f32) (f32.const 7))
(global $mut_i64_export (export "var i64") (mut i64) (i64.const 8))
(func (export "get const f32 import") (result f32) (global.get $f32_import))
(func (export "get const i64 import") (result i64) (global.get $i64_import))
(func (export "get var f32 import") (result f32) (global.get $mut_f32_import))
(func (export "get var i64 import") (result i64) (global.get $mut_i64_import))
(func (export "get const f32 export") (result f32) (global.get $f32_export))
(func (export "get const i64 export") (result i64) (global.get $i64_export))
(func (export "get var f32 export") (result f32) (global.get $mut_f32_export))
(func (export "get var i64 export") (result i64) (global.get $mut_i64_export))
(func (export "set var f32 import") (param f32) (global.set $mut_f32_import (local.get 0)))
(func (export "set var i64 import") (param i64) (global.set $mut_i64_import (local.get 0)))
(func (export "set var f32 export") (param f32) (global.set $mut_f32_export (local.get 0)))
(func (export "set var f64 export") (param i64) (global.set $mut_i64_export (local.get 0)))
)

View File

@@ -0,0 +1,108 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
// A function to be called from Wasm code.
own wasm_trap_t* hello_callback(
const wasm_val_t args[], wasm_val_t results[]
) {
printf("Calling back...\n");
printf("> Hello World!\n");
return NULL;
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("hello.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external print functions.
printf("Creating callback...\n");
own wasm_functype_t* hello_type = wasm_functype_new_0_0();
own wasm_func_t* hello_func =
wasm_func_new(store, hello_type, hello_callback);
wasm_functype_delete(hello_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = { wasm_func_as_extern(hello_func) };
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_func_delete(hello_func);
// Extract export.
printf("Extracting export...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}
wasm_module_delete(module);
wasm_instance_delete(instance);
// Call.
printf("Calling export...\n");
if (wasm_func_call(run_func, NULL, NULL)) {
printf("> Error calling function!\n");
return 1;
}
wasm_extern_vec_delete(&exports);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,91 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
// A function to be called from Wasm code.
auto hello_callback(
const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap> {
std::cout << "Calling back..." << std::endl;
std::cout << "> Hello world!" << std::endl;
return nullptr;
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("hello.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Create external print functions.
std::cout << "Creating callback..." << std::endl;
auto hello_type = wasm::FuncType::make(
wasm::ownvec<wasm::ValType>::make(), wasm::ownvec<wasm::ValType>::make()
);
auto hello_func = wasm::Func::make(store, hello_type.get(), hello_callback);
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {hello_func.get()};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
exit(1);
}
// Extract export.
std::cout << "Extracting export..." << std::endl;
auto exports = instance->exports();
if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) {
std::cout << "> Error accessing export!" << std::endl;
exit(1);
}
auto run_func = exports[0]->func();
// Call.
std::cout << "Calling export..." << std::endl;
if (run_func->call()) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,4 @@
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)

View File

@@ -0,0 +1,269 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
// A function to be called from Wasm code.
own wasm_trap_t* callback(
const wasm_val_t args[], wasm_val_t results[]
) {
printf("Calling back...\n> ");
printf("> %p\n",
args[0].of.ref ? wasm_ref_get_host_info(args[0].of.ref) : NULL);
wasm_val_copy(&results[0], &args[0]);
return NULL;
}
wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) {
printf("> Error accessing function export %zu!\n", i);
exit(1);
}
return wasm_extern_as_func(exports->data[i]);
}
wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) {
printf("> Error accessing global export %zu!\n", i);
exit(1);
}
return wasm_extern_as_global(exports->data[i]);
}
wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) {
printf("> Error accessing table export %zu!\n", i);
exit(1);
}
return wasm_extern_as_table(exports->data[i]);
}
own wasm_ref_t* call_v_r(const wasm_func_t* func) {
printf("call_v_r... "); fflush(stdout);
wasm_val_t results[1];
if (wasm_func_call(func, NULL, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) {
printf("call_r_v... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_ANYREF;
args[0].of.ref = ref;
if (wasm_func_call(func, args, NULL)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
}
own wasm_ref_t* call_r_r(const wasm_func_t* func, wasm_ref_t* ref) {
printf("call_r_r... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_ANYREF;
args[0].of.ref = ref;
wasm_val_t results[1];
if (wasm_func_call(func, args, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) {
printf("call_ir_v... "); fflush(stdout);
wasm_val_t args[2];
args[0].kind = WASM_I32;
args[0].of.i32 = i;
args[1].kind = WASM_ANYREF;
args[1].of.ref = ref;
if (wasm_func_call(func, args, NULL)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
}
own wasm_ref_t* call_i_r(const wasm_func_t* func, int32_t i) {
printf("call_i_r... "); fflush(stdout);
wasm_val_t args[1];
args[0].kind = WASM_I32;
args[0].of.i32 = i;
wasm_val_t results[1];
if (wasm_func_call(func, args, results)) {
printf("> Error calling function!\n");
exit(1);
}
printf("okay\n");
return results[0].of.ref;
}
void check(own wasm_ref_t* actual, const wasm_ref_t* expected) {
if (actual != expected &&
!(actual && expected && wasm_ref_same(actual, expected))) {
printf("> Error reading reference, expected %p, got %p\n",
expected ? wasm_ref_get_host_info(expected) : NULL,
actual ? wasm_ref_get_host_info(actual) : NULL);
exit(1);
}
if (actual) wasm_ref_delete(actual);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("hostref.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external callback function.
printf("Creating callback...\n");
own wasm_functype_t* callback_type = wasm_functype_new_1_1(
wasm_valtype_new(WASM_ANYREF), wasm_valtype_new(WASM_ANYREF));
own wasm_func_t* callback_func =
wasm_func_new(store, callback_type, callback);
wasm_functype_delete(callback_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = { wasm_func_as_extern(callback_func) };
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_func_delete(callback_func);
wasm_module_delete(module);
// Extract export.
printf("Extracting exports...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
size_t i = 0;
wasm_global_t* global = get_export_global(&exports, i++);
wasm_table_t* table = get_export_table(&exports, i++);
wasm_func_t* global_set = get_export_func(&exports, i++);
wasm_func_t* global_get = get_export_func(&exports, i++);
wasm_func_t* table_set = get_export_func(&exports, i++);
wasm_func_t* table_get = get_export_func(&exports, i++);
wasm_func_t* func_call = get_export_func(&exports, i++);
wasm_instance_delete(instance);
// Create host references.
printf("Creating host references...\n");
own wasm_ref_t* host1 = wasm_foreign_as_ref(wasm_foreign_new(store));
own wasm_ref_t* host2 = wasm_foreign_as_ref(wasm_foreign_new(store));
wasm_ref_set_host_info(host1, (void*)1);
wasm_ref_set_host_info(host2, (void*)2);
// Some sanity checks.
check(NULL, NULL);
check(wasm_ref_copy(host1), host1);
check(wasm_ref_copy(host2), host2);
own wasm_val_t val;
val.kind = WASM_ANYREF;
val.of.ref = wasm_ref_copy(host1);
check(wasm_ref_copy(val.of.ref), host1);
own wasm_ref_t* ref = val.of.ref;
check(wasm_ref_copy(ref), host1);
wasm_val_delete(&val);
// Interact.
printf("Accessing global...\n");
check(call_v_r(global_get), NULL);
call_r_v(global_set, host1);
check(call_v_r(global_get), host1);
call_r_v(global_set, host2);
check(call_v_r(global_get), host2);
call_r_v(global_set, NULL);
check(call_v_r(global_get), NULL);
wasm_global_get(global, &val);
assert(val.kind == WASM_ANYREF);
check(val.of.ref, NULL);
val.of.ref = host2;
wasm_global_set(global, &val);
check(call_v_r(global_get), host2);
wasm_global_get(global, &val);
assert(val.kind == WASM_ANYREF);
check(val.of.ref, host2);
printf("Accessing table...\n");
check(call_i_r(table_get, 0), NULL);
check(call_i_r(table_get, 1), NULL);
call_ir_v(table_set, 0, host1);
call_ir_v(table_set, 1, host2);
check(call_i_r(table_get, 0), host1);
check(call_i_r(table_get, 1), host2);
call_ir_v(table_set, 0, NULL);
check(call_i_r(table_get, 0), NULL);
check(wasm_table_get(table, 2), NULL);
wasm_table_set(table, 2, host1);
check(call_i_r(table_get, 2), host1);
check(wasm_table_get(table, 2), host1);
printf("Accessing function...\n");
check(call_r_r(func_call, NULL), NULL);
check(call_r_r(func_call, host1), host1);
check(call_r_r(func_call, host2), host2);
wasm_ref_delete(host1);
wasm_ref_delete(host2);
wasm_extern_vec_delete(&exports);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,232 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
// A function to be called from Wasm code.
auto callback(
const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap> {
std::cout << "Calling back..." << std::endl;
std::cout << "> " << (args[0].ref() ? args[0].ref()->get_host_info() : nullptr) << std::endl;
results[0] = args[0].copy();
return nullptr;
}
auto get_export_func(const wasm::ownvec<wasm::Extern>& exports, size_t i) -> const wasm::Func* {
if (exports.size() <= i || !exports[i]->func()) {
std::cout << "> Error accessing function export " << i << "/" << exports.size() << "!" << std::endl;
exit(1);
}
return exports[i]->func();
}
auto get_export_global(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Global* {
if (exports.size() <= i || !exports[i]->global()) {
std::cout << "> Error accessing global export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->global();
}
auto get_export_table(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Table* {
if (exports.size() <= i || !exports[i]->table()) {
std::cout << "> Error accessing table export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->table();
}
void call_r_v(const wasm::Func* func, const wasm::Ref* ref) {
std::cout << "call_r_v... " << std::flush;
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
if (func->call(args, nullptr)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
}
auto call_v_r(const wasm::Func* func) -> wasm::own<wasm::Ref> {
std::cout << "call_v_r... " << std::flush;
wasm::Val results[1];
if (func->call(nullptr, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
auto call_r_r(const wasm::Func* func, const wasm::Ref* ref) -> wasm::own<wasm::Ref> {
std::cout << "call_r_r... " << std::flush;
wasm::Val args[1] = {wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
wasm::Val results[1];
if (func->call(args, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
void call_ir_v(const wasm::Func* func, int32_t i, const wasm::Ref* ref) {
std::cout << "call_ir_v... " << std::flush;
wasm::Val args[2] = {wasm::Val::i32(i), wasm::Val::ref(ref ? ref->copy() : wasm::own<wasm::Ref>())};
if (func->call(args, nullptr)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
}
auto call_i_r(const wasm::Func* func, int32_t i) -> wasm::own<wasm::Ref> {
std::cout << "call_i_r... " << std::flush;
wasm::Val args[1] = {wasm::Val::i32(i)};
wasm::Val results[1];
if (func->call(args, results)) {
std::cout << "> Error calling function!" << std::endl;
exit(1);
}
std::cout << "okay" << std::endl;
return results[0].release_ref();
}
void check(wasm::own<wasm::Ref> actual, const wasm::Ref* expected) {
if (actual.get() != expected &&
!(actual && expected && actual->same(expected))) {
std::cout << "> Error reading reference, expected "
<< (expected ? expected->get_host_info() : nullptr) << ", got "
<< (actual ? actual->get_host_info() : nullptr) << std::endl;
exit(1);
}
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("hostref.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
return;
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
return;
}
// Create external callback function.
std::cout << "Creating callback..." << std::endl;
auto callback_type = wasm::FuncType::make(
wasm::ownvec<wasm::ValType>::make(wasm::ValType::make(wasm::ValKind::ANYREF)),
wasm::ownvec<wasm::ValType>::make(wasm::ValType::make(wasm::ValKind::ANYREF))
);
auto callback_func = wasm::Func::make(store, callback_type.get(), callback);
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {callback_func.get()};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
return;
}
// Extract export.
std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports();
size_t i = 0;
auto global = get_export_global(exports, i++);
auto table = get_export_table(exports, i++);
auto global_set = get_export_func(exports, i++);
auto global_get = get_export_func(exports, i++);
auto table_set = get_export_func(exports, i++);
auto table_get = get_export_func(exports, i++);
auto func_call = get_export_func(exports, i++);
// Create host references.
std::cout << "Creating host references..." << std::endl;
auto host1 = wasm::Foreign::make(store);
auto host2 = wasm::Foreign::make(store);
host1->set_host_info(reinterpret_cast<void*>(1));
host2->set_host_info(reinterpret_cast<void*>(2));
// Some sanity checks.
check(nullptr, nullptr);
check(host1->copy(), host1.get());
check(host2->copy(), host2.get());
wasm::Val val = wasm::Val::ref(host1->copy());
check(val.ref()->copy(), host1.get());
auto ref = val.release_ref();
assert(val.ref() == nullptr);
check(ref->copy(), host1.get());
// Interact.
std::cout << "Accessing global..." << std::endl;
check(call_v_r(global_get), nullptr);
call_r_v(global_set, host1.get());
check(call_v_r(global_get), host1.get());
call_r_v(global_set, host2.get());
check(call_v_r(global_get), host2.get());
call_r_v(global_set, nullptr);
check(call_v_r(global_get), nullptr);
check(global->get().release_ref(), nullptr);
global->set(wasm::Val(host2->copy()));
check(call_v_r(global_get), host2.get());
check(global->get().release_ref(), host2.get());
std::cout << "Accessing table..." << std::endl;
check(call_i_r(table_get, 0), nullptr);
check(call_i_r(table_get, 1), nullptr);
call_ir_v(table_set, 0, host1.get());
call_ir_v(table_set, 1, host2.get());
check(call_i_r(table_get, 0), host1.get());
check(call_i_r(table_get, 1), host2.get());
call_ir_v(table_set, 0, nullptr);
check(call_i_r(table_get, 0), nullptr);
check(table->get(2), nullptr);
table->set(2, host1.get());
check(call_i_r(table_get, 2), host1.get());
check(table->get(2), host1.get());
std::cout << "Accessing function..." << std::endl;
check(call_r_r(func_call, nullptr), nullptr);
check(call_r_r(func_call, host1.get()), host1.get());
check(call_r_r(func_call, host2.get()), host2.get());
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,24 @@
(module
(import "" "f" (func $fun (param anyref) (result anyref)))
(global $glob (export "global") (mut anyref) (ref.null))
(table $tab (export "table") 10 anyref)
(func (export "global.set") (param $r anyref)
(global.set $glob (local.get $r))
)
(func (export "global.get") (result anyref)
(global.get $glob)
)
(func (export "table.set") (param $i i32) (param $r anyref)
(table.set $tab (local.get $i) (local.get $r))
)
(func (export "table.get") (param $i i32) (result anyref)
(table.get $tab (local.get $i))
)
(func (export "func.call") (param $r anyref) (result anyref)
(call $fun (local.get $r))
)
)

View File

@@ -0,0 +1,222 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
wasm_memory_t* get_export_memory(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_memory(exports->data[i])) {
printf("> Error accessing memory export %zu!\n", i);
exit(1);
}
return wasm_extern_as_memory(exports->data[i]);
}
wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) {
if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) {
printf("> Error accessing function export %zu!\n", i);
exit(1);
}
return wasm_extern_as_func(exports->data[i]);
}
void check(bool success) {
if (!success) {
printf("> Error, expected success\n");
exit(1);
}
}
void check_call(wasm_func_t* func, wasm_val_t args[], int32_t expected) {
wasm_val_t results[1];
if (wasm_func_call(func, args, results) || results[0].of.i32 != expected) {
printf("> Error on result\n");
exit(1);
}
}
void check_call0(wasm_func_t* func, int32_t expected) {
check_call(func, NULL, expected);
}
void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) {
wasm_val_t args[] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
check_call(func, args, expected);
}
void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) {
wasm_val_t args[2] = {
{.kind = WASM_I32, .of = {.i32 = arg1}},
{.kind = WASM_I32, .of = {.i32 = arg2}}
};
check_call(func, args, expected);
}
void check_ok(wasm_func_t* func, wasm_val_t args[]) {
if (wasm_func_call(func, args, NULL)) {
printf("> Error on result, expected empty\n");
exit(1);
}
}
void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
wasm_val_t args[2] = {
{.kind = WASM_I32, .of = {.i32 = arg1}},
{.kind = WASM_I32, .of = {.i32 = arg2}}
};
check_ok(func, args);
}
void check_trap(wasm_func_t* func, wasm_val_t args[]) {
wasm_val_t results[1];
own wasm_trap_t* trap = wasm_func_call(func, args, results);
if (! trap) {
printf("> Error on result, expected trap\n");
exit(1);
}
wasm_trap_delete(trap);
}
void check_trap1(wasm_func_t* func, int32_t arg) {
wasm_val_t args[1] = { {.kind = WASM_I32, .of = {.i32 = arg}} };
check_trap(func, args);
}
void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) {
wasm_val_t args[2] = {
{.kind = WASM_I32, .of = {.i32 = arg1}},
{.kind = WASM_I32, .of = {.i32 = arg2}}
};
check_trap(func, args);
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("memory.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Instantiate.
printf("Instantiating module...\n");
own wasm_instance_t* instance = wasm_instance_new(store, module, NULL, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
// Extract export.
printf("Extracting exports...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
size_t i = 0;
wasm_memory_t* memory = get_export_memory(&exports, i++);
wasm_func_t* size_func = get_export_func(&exports, i++);
wasm_func_t* load_func = get_export_func(&exports, i++);
wasm_func_t* store_func = get_export_func(&exports, i++);
wasm_module_delete(module);
// Try cloning.
own wasm_memory_t* copy = wasm_memory_copy(memory);
assert(wasm_memory_same(memory, copy));
wasm_memory_delete(copy);
// Check initial memory.
printf("Checking memory...\n");
check(wasm_memory_size(memory) == 2);
check(wasm_memory_data_size(memory) == 0x20000);
check(wasm_memory_data(memory)[0] == 0);
check(wasm_memory_data(memory)[0x1000] == 1);
check(wasm_memory_data(memory)[0x1003] == 4);
check_call0(size_func, 2);
check_call1(load_func, 0, 0);
check_call1(load_func, 0x1000, 1);
check_call1(load_func, 0x1003, 4);
check_call1(load_func, 0x1ffff, 0);
check_trap1(load_func, 0x20000);
// Mutate memory.
printf("Mutating memory...\n");
wasm_memory_data(memory)[0x1003] = 5;
check_ok2(store_func, 0x1002, 6);
check_trap2(store_func, 0x20000, 0);
check(wasm_memory_data(memory)[0x1002] == 6);
check(wasm_memory_data(memory)[0x1003] == 5);
check_call1(load_func, 0x1002, 6);
check_call1(load_func, 0x1003, 5);
// Grow memory.
printf("Growing memory...\n");
check(wasm_memory_grow(memory, 1));
check(wasm_memory_size(memory) == 3);
check(wasm_memory_data_size(memory) == 0x30000);
check_call1(load_func, 0x20000, 0);
check_ok2(store_func, 0x20000, 0);
check_trap1(load_func, 0x30000);
check_trap2(store_func, 0x30000, 0);
check(! wasm_memory_grow(memory, 1));
check(wasm_memory_grow(memory, 0));
wasm_extern_vec_delete(&exports);
wasm_instance_delete(instance);
// Create stand-alone memory.
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
printf("Creating stand-alone memory...\n");
wasm_limits_t limits = {5, 5};
own wasm_memorytype_t* memorytype = wasm_memorytype_new(&limits);
own wasm_memory_t* memory2 = wasm_memory_new(store, memorytype);
check(wasm_memory_size(memory2) == 5);
check(! wasm_memory_grow(memory2, 1));
check(wasm_memory_grow(memory2, 0));
wasm_memorytype_delete(memorytype);
wasm_memory_delete(memory2);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,172 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
auto get_export_memory(wasm::ownvec<wasm::Extern>& exports, size_t i) -> wasm::Memory* {
if (exports.size() <= i || !exports[i]->memory()) {
std::cout << "> Error accessing memory export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->memory();
}
auto get_export_func(const wasm::ownvec<wasm::Extern>& exports, size_t i) -> const wasm::Func* {
if (exports.size() <= i || !exports[i]->func()) {
std::cout << "> Error accessing function export " << i << "!" << std::endl;
exit(1);
}
return exports[i]->func();
}
template<class T, class U>
void check(T actual, U expected) {
if (actual != expected) {
std::cout << "> Error on result, expected " << expected << ", got " << actual << std::endl;
exit(1);
}
}
template<class... Args>
void check_ok(const wasm::Func* func, Args... xs) {
wasm::Val args[] = {wasm::Val::i32(xs)...};
if (func->call(args)) {
std::cout << "> Error on result, expected return" << std::endl;
exit(1);
}
}
template<class... Args>
void check_trap(const wasm::Func* func, Args... xs) {
wasm::Val args[] = {wasm::Val::i32(xs)...};
if (! func->call(args)) {
std::cout << "> Error on result, expected trap" << std::endl;
exit(1);
}
}
template<class... Args>
auto call(const wasm::Func* func, Args... xs) -> int32_t {
wasm::Val args[] = {wasm::Val::i32(xs)...};
wasm::Val results[1];
if (func->call(args, results)) {
std::cout << "> Error on result, expected return" << std::endl;
exit(1);
}
return results[0].i32();
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("memory.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
auto instance = wasm::Instance::make(store, module.get(), nullptr);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
exit(1);
}
// Extract export.
std::cout << "Extracting exports..." << std::endl;
auto exports = instance->exports();
size_t i = 0;
auto memory = get_export_memory(exports, i++);
auto size_func = get_export_func(exports, i++);
auto load_func = get_export_func(exports, i++);
auto store_func = get_export_func(exports, i++);
// Try cloning.
assert(memory->copy()->same(memory));
// Check initial memory.
std::cout << "Checking memory..." << std::endl;
check(memory->size(), 2u);
check(memory->data_size(), 0x20000u);
check(memory->data()[0], 0);
check(memory->data()[0x1000], 1);
check(memory->data()[0x1003], 4);
check(call(size_func), 2);
check(call(load_func, 0), 0);
check(call(load_func, 0x1000), 1);
check(call(load_func, 0x1003), 4);
check(call(load_func, 0x1ffff), 0);
check_trap(load_func, 0x20000);
// Mutate memory.
std::cout << "Mutating memory..." << std::endl;
memory->data()[0x1003] = 5;
check_ok(store_func, 0x1002, 6);
check_trap(store_func, 0x20000, 0);
check(memory->data()[0x1002], 6);
check(memory->data()[0x1003], 5);
check(call(load_func, 0x1002), 6);
check(call(load_func, 0x1003), 5);
// Grow memory.
std::cout << "Growing memory..." << std::endl;
check(memory->grow(1), true);
check(memory->size(), 3u);
check(memory->data_size(), 0x30000u);
check(call(load_func, 0x20000), 0);
check_ok(store_func, 0x20000, 0);
check_trap(load_func, 0x30000);
check_trap(store_func, 0x30000, 0);
check(memory->grow(1), false);
check(memory->grow(0), true);
// Create stand-alone memory.
// TODO(wasm+): Once Wasm allows multiple memories, turn this into import.
std::cout << "Creating stand-alone memory..." << std::endl;
auto memorytype = wasm::MemoryType::make(wasm::Limits(5, 5));
auto memory2 = wasm::Memory::make(store, memorytype.get());
check(memory2->size(), 5u);
check(memory2->grow(1), false);
check(memory2->grow(0), true);
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
(module
(memory (export "memory") 2 3)
(func (export "size") (result i32) (memory.size))
(func (export "load") (param i32) (result i32) (i32.load8_s (local.get 0)))
(func (export "store") (param i32 i32)
(i32.store8 (local.get 0) (local.get 1))
)
(data (i32.const 0x1000) "\01\02\03\04")
)

View File

@@ -0,0 +1,157 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "wasm.h"
#define own
// A function to be called from Wasm code.
own wasm_trap_t* callback(
const wasm_val_t args[], wasm_val_t results[]
) {
printf("Calling back...\n> ");
printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n",
args[0].of.i32, args[1].of.i64, args[2].of.i64, args[3].of.i32);
printf("\n");
wasm_val_copy(&results[0], &args[3]);
wasm_val_copy(&results[1], &args[1]);
wasm_val_copy(&results[2], &args[2]);
wasm_val_copy(&results[3], &args[0]);
return NULL;
}
// A function closure.
own wasm_trap_t* closure_callback(
void* env, const wasm_val_t args[], wasm_val_t results[]
) {
int i = *(int*)env;
printf("Calling back closure...\n");
printf("> %d\n", i);
results[0].kind = WASM_I32;
results[0].of.i32 = (int32_t)i;
return NULL;
}
int main(int argc, const char* argv[]) {
// Initialize.
printf("Initializing...\n");
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// Load binary.
printf("Loading binary...\n");
FILE* file = fopen("multi.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
}
fseek(file, 0L, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0L, SEEK_SET);
wasm_byte_vec_t binary;
wasm_byte_vec_new_uninitialized(&binary, file_size);
if (fread(binary.data, file_size, 1, file) != 1) {
printf("> Error loading module!\n");
return 1;
}
fclose(file);
// Compile.
printf("Compiling module...\n");
own wasm_module_t* module = wasm_module_new(store, &binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
}
wasm_byte_vec_delete(&binary);
// Create external print functions.
printf("Creating callback...\n");
wasm_valtype_t* types[4] = {
wasm_valtype_new_i32(), wasm_valtype_new_i64(),
wasm_valtype_new_i64(), wasm_valtype_new_i32()
};
own wasm_valtype_vec_t tuple1, tuple2;
wasm_valtype_vec_new(&tuple1, 4, types);
wasm_valtype_vec_copy(&tuple2, &tuple1);
own wasm_functype_t* callback_type = wasm_functype_new(&tuple1, &tuple2);
own wasm_func_t* callback_func =
wasm_func_new(store, callback_type, callback);
wasm_functype_delete(callback_type);
// Instantiate.
printf("Instantiating module...\n");
const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)};
own wasm_instance_t* instance =
wasm_instance_new(store, module, imports, NULL);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}
wasm_func_delete(callback_func);
// Extract export.
printf("Extracting export...\n");
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}
wasm_module_delete(module);
wasm_instance_delete(instance);
// Call.
printf("Calling export...\n");
wasm_val_t args[4];
args[0].kind = WASM_I32;
args[0].of.i32 = 1;
args[1].kind = WASM_I64;
args[1].of.i64 = 2;
args[2].kind = WASM_I64;
args[2].of.i64 = 3;
args[3].kind = WASM_I32;
args[3].of.i32 = 4;
wasm_val_t results[4];
if (wasm_func_call(run_func, args, results)) {
printf("> Error calling function!\n");
return 1;
}
wasm_extern_vec_delete(&exports);
// Print result.
printf("Printing result...\n");
printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n",
results[0].of.i32, results[1].of.i64,
results[2].of.i64, results[3].of.i32);
assert(results[0].of.i32 == 4);
assert(results[1].of.i64 == 3);
assert(results[2].of.i64 == 2);
assert(results[3].of.i32 == 1);
// Shut down.
printf("Shutting down...\n");
wasm_store_delete(store);
wasm_engine_delete(engine);
// All done.
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,118 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cinttypes>
#include "wasm.hh"
// A function to be called from Wasm code.
auto callback(
const wasm::Val args[], wasm::Val results[]
) -> wasm::own<wasm::Trap> {
std::cout << "Calling back..." << std::endl;
std::cout << "> " << args[0].i32();
std::cout << " " << args[1].i64();
std::cout << " " << args[2].i64();
std::cout << " " << args[3].i32() << std::endl;
results[0] = args[3].copy();
results[1] = args[1].copy();
results[2] = args[2].copy();
results[3] = args[0].copy();
return nullptr;
}
void run() {
// Initialize.
std::cout << "Initializing..." << std::endl;
auto engine = wasm::Engine::make();
auto store_ = wasm::Store::make(engine.get());
auto store = store_.get();
// Load binary.
std::cout << "Loading binary..." << std::endl;
std::ifstream file("multi.wasm");
file.seekg(0, std::ios_base::end);
auto file_size = file.tellg();
file.seekg(0);
auto binary = wasm::vec<byte_t>::make_uninitialized(file_size);
file.read(binary.get(), file_size);
file.close();
if (file.fail()) {
std::cout << "> Error loading module!" << std::endl;
exit(1);
}
// Compile.
std::cout << "Compiling module..." << std::endl;
auto module = wasm::Module::make(store, binary);
if (!module) {
std::cout << "> Error compiling module!" << std::endl;
exit(1);
}
// Create external print functions.
std::cout << "Creating callback..." << std::endl;
auto tuple = wasm::ownvec<wasm::ValType>::make(
wasm::ValType::make(wasm::ValKind::I32),
wasm::ValType::make(wasm::ValKind::I64),
wasm::ValType::make(wasm::ValKind::I64),
wasm::ValType::make(wasm::ValKind::I32)
);
auto callback_type =
wasm::FuncType::make(tuple.deep_copy(), tuple.deep_copy());
auto callback_func = wasm::Func::make(store, callback_type.get(), callback);
// Instantiate.
std::cout << "Instantiating module..." << std::endl;
wasm::Extern* imports[] = {callback_func.get()};
auto instance = wasm::Instance::make(store, module.get(), imports);
if (!instance) {
std::cout << "> Error instantiating module!" << std::endl;
exit(1);
}
// Extract export.
std::cout << "Extracting export..." << std::endl;
auto exports = instance->exports();
if (exports.size() == 0 || exports[0]->kind() != wasm::ExternKind::FUNC || !exports[0]->func()) {
std::cout << "> Error accessing export!" << std::endl;
exit(1);
}
auto run_func = exports[0]->func();
// Call.
std::cout << "Calling export..." << std::endl;
wasm::Val args[] = {
wasm::Val::i32(1), wasm::Val::i64(2), wasm::Val::i64(3), wasm::Val::i32(4)
};
wasm::Val results[4];
if (wasm::own<wasm::Trap> trap = run_func->call(args, results)) {
std::cout << "> Error calling function! " << trap->message().get() << std::endl;
exit(1);
}
// Print result.
std::cout << "Printing result..." << std::endl;
std::cout << "> " << results[0].i32();
std::cout << " " << results[1].i64();
std::cout << " " << results[2].i64();
std::cout << " " << results[3].i32() << std::endl;
assert(results[0].i32() == 4);
assert(results[1].i64() == 3);
assert(results[2].i64() == 2);
assert(results[3].i32() == 1);
// Shut down.
std::cout << "Shutting down..." << std::endl;
}
int main(int argc, const char* argv[]) {
run();
std::cout << "Done." << std::endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,7 @@
(module
(func $f (import "" "f") (param i32 i64 i64 i32) (result i32 i64 i64 i32))
(func $g (export "g") (param i32 i64 i64 i32) (result i32 i64 i64 i32)
(call $f (local.get 0) (local.get 2) (local.get 1) (local.get 3))
)
)

Some files were not shown because too many files have changed in this diff Show More