mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 21:58:20 +00:00
10
.github/workflows/main.yaml
vendored
10
.github/workflows/main.yaml
vendored
@@ -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
60
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ bytesize = "1.0.0"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"lib/c-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
29
Makefile
29
Makefile
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
1
lib/c-api/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
doc/html/
|
||||||
52
lib/c-api/Cargo.toml
Normal file
52
lib/c-api/Cargo.toml
Normal 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
140
lib/c-api/README.md
Normal 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
106
lib/c-api/build.rs
Normal 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");
|
||||||
|
}
|
||||||
14
lib/c-api/crates/wasm-c-api/Cargo.toml
Normal file
14
lib/c-api/crates/wasm-c-api/Cargo.toml
Normal 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 }
|
||||||
1
lib/c-api/crates/wasm-c-api/src/lib.rs
Normal file
1
lib/c-api/crates/wasm-c-api/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
107
lib/c-api/doc/index.md
Normal file
107
lib/c-api/doc/index.md
Normal 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
194
lib/c-api/doc/theme/css/wasmer.css
vendored
Normal 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
10
lib/c-api/doc/theme/footer.html
vendored
Normal 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
53
lib/c-api/doc/theme/header.html
vendored
Normal 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
338
lib/c-api/doxyfile
Normal 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
113
lib/c-api/src/error.rs
Normal 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
573
lib/c-api/src/export.rs
Normal 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(¶ms[..]);
|
||||||
|
|
||||||
|
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
71
lib/c-api/src/global.rs
Normal 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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
145
lib/c-api/src/import/emscripten.rs
Normal file
145
lib/c-api/src/import/emscripten.rs
Normal 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
769
lib/c-api/src/import/mod.rs
Normal 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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
239
lib/c-api/src/import/wasi.rs
Normal file
239
lib/c-api/src/import/wasi.rs
Normal 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
558
lib/c-api/src/instance.rs
Normal 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(¶ms[..]);
|
||||||
|
|
||||||
|
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
192
lib/c-api/src/lib.rs
Normal 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
212
lib/c-api/src/memory.rs
Normal 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
313
lib/c-api/src/module.rs
Normal 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
97
lib/c-api/src/table.rs
Normal 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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
92
lib/c-api/src/trampoline.rs
Normal file
92
lib/c-api/src/trampoline.rs
Normal 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
165
lib/c-api/src/value.rs
Normal 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
34
lib/c-api/tests/.gitignore
vendored
Normal 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-*
|
||||||
179
lib/c-api/tests/CMakeLists.txt
Normal file
179
lib/c-api/tests/CMakeLists.txt
Normal 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
|
||||||
|
#)
|
||||||
3
lib/c-api/tests/assets/README.md
Normal file
3
lib/c-api/tests/assets/README.md
Normal 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.
|
||||||
10
lib/c-api/tests/assets/emscripten_hello_world.c
Normal file
10
lib/c-api/tests/assets/emscripten_hello_world.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/assets/emscripten_hello_world.wasm
Normal file
BIN
lib/c-api/tests/assets/emscripten_hello_world.wasm
Normal file
Binary file not shown.
37
lib/c-api/tests/assets/exports.rs
Normal file
37
lib/c-api/tests/assets/exports.rs
Normal 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() {}
|
||||||
BIN
lib/c-api/tests/assets/exports.wasm
Normal file
BIN
lib/c-api/tests/assets/exports.wasm
Normal file
Binary file not shown.
31
lib/c-api/tests/assets/extended_wasi.rs
Normal file
31
lib/c-api/tests/assets/extended_wasi.rs
Normal 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) };
|
||||||
|
}
|
||||||
BIN
lib/c-api/tests/assets/extended_wasi.wasm
Executable file
BIN
lib/c-api/tests/assets/extended_wasi.wasm
Executable file
Binary file not shown.
BIN
lib/c-api/tests/assets/hello_wasm.wasm
Normal file
BIN
lib/c-api/tests/assets/hello_wasm.wasm
Normal file
Binary file not shown.
BIN
lib/c-api/tests/assets/inc.wasm
Normal file
BIN
lib/c-api/tests/assets/inc.wasm
Normal file
Binary file not shown.
12
lib/c-api/tests/assets/inc.wast
Normal file
12
lib/c-api/tests/assets/inc.wast
Normal 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))
|
||||||
4
lib/c-api/tests/assets/return_hello.rs
Normal file
4
lib/c-api/tests/assets/return_hello.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn return_hello() -> *const u8 {
|
||||||
|
b"Hello, World!\0"[..].as_ptr()
|
||||||
|
}
|
||||||
BIN
lib/c-api/tests/assets/return_hello.wasm
Normal file
BIN
lib/c-api/tests/assets/return_hello.wasm
Normal file
Binary file not shown.
BIN
lib/c-api/tests/assets/sum.wasm
Normal file
BIN
lib/c-api/tests/assets/sum.wasm
Normal file
Binary file not shown.
BIN
lib/c-api/tests/assets/wasm_sample_app.wasm
Executable file
BIN
lib/c-api/tests/assets/wasm_sample_app.wasm
Executable file
Binary file not shown.
60
lib/c-api/tests/runtime_c_api_tests.rs
Normal file
60
lib/c-api/tests/runtime_c_api_tests.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
137
lib/c-api/tests/test-context.c
Normal file
137
lib/c-api/tests/test-context.c
Normal 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;
|
||||||
|
}
|
||||||
138
lib/c-api/tests/test-emscripten-import-object.c
Normal file
138
lib/c-api/tests/test-emscripten-import-object.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
74
lib/c-api/tests/test-exported-memory.c
Normal file
74
lib/c-api/tests/test-exported-memory.c
Normal 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;
|
||||||
|
}
|
||||||
445
lib/c-api/tests/test-exports.c
Normal file
445
lib/c-api/tests/test-exports.c
Normal 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;
|
||||||
|
}
|
||||||
30
lib/c-api/tests/test-globals.c
Normal file
30
lib/c-api/tests/test-globals.c
Normal 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;
|
||||||
|
}
|
||||||
132
lib/c-api/tests/test-import-function-callinfo.c
Normal file
132
lib/c-api/tests/test-import-function-callinfo.c
Normal 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;
|
||||||
|
}
|
||||||
128
lib/c-api/tests/test-import-function.c
Normal file
128
lib/c-api/tests/test-import-function.c
Normal 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;
|
||||||
|
}
|
||||||
172
lib/c-api/tests/test-import-object.c
Normal file
172
lib/c-api/tests/test-import-object.c
Normal 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;
|
||||||
|
}
|
||||||
80
lib/c-api/tests/test-import-trap.c
Normal file
80
lib/c-api/tests/test-import-trap.c
Normal 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;
|
||||||
|
}
|
||||||
155
lib/c-api/tests/test-imports.c
Normal file
155
lib/c-api/tests/test-imports.c
Normal 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;
|
||||||
|
}
|
||||||
59
lib/c-api/tests/test-instantiate.c
Normal file
59
lib/c-api/tests/test-instantiate.c
Normal 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;
|
||||||
|
}
|
||||||
67
lib/c-api/tests/test-memory.c
Normal file
67
lib/c-api/tests/test-memory.c
Normal 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;
|
||||||
|
}
|
||||||
53
lib/c-api/tests/test-module-exports.c
Normal file
53
lib/c-api/tests/test-module-exports.c
Normal 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;
|
||||||
|
}
|
||||||
149
lib/c-api/tests/test-module-import-instantiate.c
Normal file
149
lib/c-api/tests/test-module-import-instantiate.c
Normal 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;
|
||||||
|
}
|
||||||
56
lib/c-api/tests/test-module-imports.c
Normal file
56
lib/c-api/tests/test-module-imports.c
Normal 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;
|
||||||
|
}
|
||||||
98
lib/c-api/tests/test-module-serialize.c
Normal file
98
lib/c-api/tests/test-module-serialize.c
Normal 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;
|
||||||
|
}
|
||||||
51
lib/c-api/tests/test-module.c
Normal file
51
lib/c-api/tests/test-module.c
Normal 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;
|
||||||
|
}
|
||||||
51
lib/c-api/tests/test-tables.c
Normal file
51
lib/c-api/tests/test-tables.c
Normal 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;
|
||||||
|
}
|
||||||
22
lib/c-api/tests/test-validate.c
Normal file
22
lib/c-api/tests/test-validate.c
Normal 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;
|
||||||
|
}
|
||||||
260
lib/c-api/tests/test-wasi-import-object.c
Normal file
260
lib/c-api/tests/test-wasi-import-object.c
Normal 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
2
lib/c-api/tests/wasm-c-api/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
out
|
||||||
|
v8
|
||||||
18
lib/c-api/tests/wasm-c-api/Dockerfile
Normal file
18
lib/c-api/tests/wasm-c-api/Dockerfile
Normal 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
|
||||||
202
lib/c-api/tests/wasm-c-api/LICENSE
Normal file
202
lib/c-api/tests/wasm-c-api/LICENSE
Normal 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.
|
||||||
|
|
||||||
111
lib/c-api/tests/wasm-c-api/README.md
Normal file
111
lib/c-api/tests/wasm-c-api/README.md
Normal 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?
|
||||||
168
lib/c-api/tests/wasm-c-api/example/callback.c
Normal file
168
lib/c-api/tests/wasm-c-api/example/callback.c
Normal 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;
|
||||||
|
}
|
||||||
145
lib/c-api/tests/wasm-c-api/example/callback.cc
Normal file
145
lib/c-api/tests/wasm-c-api/example/callback.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/callback.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/callback.wasm
Normal file
Binary file not shown.
10
lib/c-api/tests/wasm-c-api/example/callback.wat
Normal file
10
lib/c-api/tests/wasm-c-api/example/callback.wat
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
109
lib/c-api/tests/wasm-c-api/example/finalize.c
Normal file
109
lib/c-api/tests/wasm-c-api/example/finalize.c
Normal 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;
|
||||||
|
}
|
||||||
103
lib/c-api/tests/wasm-c-api/example/finalize.cc
Normal file
103
lib/c-api/tests/wasm-c-api/example/finalize.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/finalize.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/finalize.wasm
Normal file
Binary file not shown.
5
lib/c-api/tests/wasm-c-api/example/finalize.wat
Normal file
5
lib/c-api/tests/wasm-c-api/example/finalize.wat
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
(module
|
||||||
|
(func (export "f"))
|
||||||
|
(func (export "g"))
|
||||||
|
(func (export "h"))
|
||||||
|
)
|
||||||
232
lib/c-api/tests/wasm-c-api/example/global.c
Normal file
232
lib/c-api/tests/wasm-c-api/example/global.c
Normal 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;
|
||||||
|
}
|
||||||
196
lib/c-api/tests/wasm-c-api/example/global.cc
Normal file
196
lib/c-api/tests/wasm-c-api/example/global.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/global.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/global.wasm
Normal file
Binary file not shown.
27
lib/c-api/tests/wasm-c-api/example/global.wat
Normal file
27
lib/c-api/tests/wasm-c-api/example/global.wat
Normal 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)))
|
||||||
|
)
|
||||||
108
lib/c-api/tests/wasm-c-api/example/hello.c
Normal file
108
lib/c-api/tests/wasm-c-api/example/hello.c
Normal 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;
|
||||||
|
}
|
||||||
91
lib/c-api/tests/wasm-c-api/example/hello.cc
Normal file
91
lib/c-api/tests/wasm-c-api/example/hello.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/hello.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/hello.wasm
Normal file
Binary file not shown.
4
lib/c-api/tests/wasm-c-api/example/hello.wat
Normal file
4
lib/c-api/tests/wasm-c-api/example/hello.wat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
(module
|
||||||
|
(func $hello (import "" "hello"))
|
||||||
|
(func (export "run") (call $hello))
|
||||||
|
)
|
||||||
269
lib/c-api/tests/wasm-c-api/example/hostref.c
Normal file
269
lib/c-api/tests/wasm-c-api/example/hostref.c
Normal 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;
|
||||||
|
}
|
||||||
232
lib/c-api/tests/wasm-c-api/example/hostref.cc
Normal file
232
lib/c-api/tests/wasm-c-api/example/hostref.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/hostref.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/hostref.wasm
Normal file
Binary file not shown.
24
lib/c-api/tests/wasm-c-api/example/hostref.wat
Normal file
24
lib/c-api/tests/wasm-c-api/example/hostref.wat
Normal 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))
|
||||||
|
)
|
||||||
|
)
|
||||||
222
lib/c-api/tests/wasm-c-api/example/memory.c
Normal file
222
lib/c-api/tests/wasm-c-api/example/memory.c
Normal 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;
|
||||||
|
}
|
||||||
172
lib/c-api/tests/wasm-c-api/example/memory.cc
Normal file
172
lib/c-api/tests/wasm-c-api/example/memory.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/memory.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/memory.wasm
Normal file
Binary file not shown.
11
lib/c-api/tests/wasm-c-api/example/memory.wat
Normal file
11
lib/c-api/tests/wasm-c-api/example/memory.wat
Normal 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")
|
||||||
|
)
|
||||||
157
lib/c-api/tests/wasm-c-api/example/multi.c
Normal file
157
lib/c-api/tests/wasm-c-api/example/multi.c
Normal 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;
|
||||||
|
}
|
||||||
118
lib/c-api/tests/wasm-c-api/example/multi.cc
Normal file
118
lib/c-api/tests/wasm-c-api/example/multi.cc
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
lib/c-api/tests/wasm-c-api/example/multi.wasm
Normal file
BIN
lib/c-api/tests/wasm-c-api/example/multi.wasm
Normal file
Binary file not shown.
7
lib/c-api/tests/wasm-c-api/example/multi.wat
Normal file
7
lib/c-api/tests/wasm-c-api/example/multi.wat
Normal 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
Reference in New Issue
Block a user