Better Wasmtime API for embedder (#287)
* Migrate wasm-rust-api code to wasmtime.
This commit is contained in:
committed by
Dan Gohman
parent
8ea883a603
commit
f88e92a57c
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -5,3 +5,6 @@
|
|||||||
path = lightbeam
|
path = lightbeam
|
||||||
url = https://github.com/CraneStation/lightbeam.git
|
url = https://github.com/CraneStation/lightbeam.git
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "wasmtime-api/c-examples/wasm-c-api"]
|
||||||
|
path = wasmtime-api/c-examples/wasm-c-api
|
||||||
|
url = https://github.com/WebAssembly/wasm-c-api
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ cranelift-codegen = { version = "0.40.0", features = ["enable-serde"] }
|
|||||||
cranelift-native = "0.40.0"
|
cranelift-native = "0.40.0"
|
||||||
cranelift-entity = { version = "0.40.0", features = ["enable-serde"] }
|
cranelift-entity = { version = "0.40.0", features = ["enable-serde"] }
|
||||||
cranelift-wasm = { version = "0.40.0", features = ["enable-serde"] }
|
cranelift-wasm = { version = "0.40.0", features = ["enable-serde"] }
|
||||||
|
wasmtime-api = { path = "wasmtime-api" }
|
||||||
wasmtime-debug = { path = "wasmtime-debug" }
|
wasmtime-debug = { path = "wasmtime-debug" }
|
||||||
wasmtime-environ = { path = "wasmtime-environ" }
|
wasmtime-environ = { path = "wasmtime-environ" }
|
||||||
wasmtime-interface-types = { path = "wasmtime-interface-types" }
|
wasmtime-interface-types = { path = "wasmtime-interface-types" }
|
||||||
|
|||||||
@@ -32,21 +32,24 @@
|
|||||||
|
|
||||||
use cranelift_codegen::settings;
|
use cranelift_codegen::settings;
|
||||||
use cranelift_codegen::settings::Configurable;
|
use cranelift_codegen::settings::Configurable;
|
||||||
use cranelift_native;
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use failure::{bail, format_err, Error, ResultExt};
|
use failure::{bail, Error, ResultExt};
|
||||||
use pretty_env_logger;
|
use pretty_env_logger;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Component;
|
use std::path::Component;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
use std::rc::Rc;
|
||||||
use wabt;
|
use wabt;
|
||||||
use wasi_common::preopen_dir;
|
use wasi_common::preopen_dir;
|
||||||
|
use wasmtime_api::{Config, Engine, Instance, Module, Store};
|
||||||
use wasmtime_environ::cache_conf;
|
use wasmtime_environ::cache_conf;
|
||||||
use wasmtime_interface_types::ModuleData;
|
use wasmtime_interface_types::ModuleData;
|
||||||
use wasmtime_jit::{Context, Features, InstanceHandle};
|
use wasmtime_jit::Features;
|
||||||
use wasmtime_wasi::instantiate_wasi;
|
use wasmtime_wasi::instantiate_wasi;
|
||||||
use wasmtime_wast::instantiate_spectest;
|
use wasmtime_wast::instantiate_spectest;
|
||||||
|
|
||||||
@@ -219,11 +222,12 @@ fn rmain() -> Result<(), Error> {
|
|||||||
args.flag_cache_dir.as_ref(),
|
args.flag_cache_dir.as_ref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let isa_builder = cranelift_native::builder()
|
|
||||||
.map_err(|s| format_err!("host machine is not a supported target: {}", s))?;
|
|
||||||
let mut flag_builder = settings::builder();
|
let mut flag_builder = settings::builder();
|
||||||
let mut features: Features = Default::default();
|
let mut features: Features = Default::default();
|
||||||
|
|
||||||
|
// Enable/disable producing of debug info.
|
||||||
|
let debug_info = args.flag_g;
|
||||||
|
|
||||||
// Enable verifier passes in debug mode.
|
// Enable verifier passes in debug mode.
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
flag_builder.enable("enable_verifier")?;
|
flag_builder.enable("enable_verifier")?;
|
||||||
@@ -240,14 +244,20 @@ fn rmain() -> Result<(), Error> {
|
|||||||
flag_builder.set("opt_level", "best")?;
|
flag_builder.set("opt_level", "best")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
let config = Config::new(settings::Flags::new(flag_builder), features, debug_info);
|
||||||
let mut context = Context::with_isa(isa).with_features(features);
|
let engine = Rc::new(RefCell::new(Engine::new(config)));
|
||||||
|
let store = Rc::new(RefCell::new(Store::new(engine)));
|
||||||
|
|
||||||
|
let mut module_registry = HashMap::new();
|
||||||
|
|
||||||
// Make spectest available by default.
|
// Make spectest available by default.
|
||||||
context.name_instance("spectest".to_owned(), instantiate_spectest()?);
|
module_registry.insert(
|
||||||
|
"spectest".to_owned(),
|
||||||
|
Instance::from_handle(store.clone(), instantiate_spectest()?)?,
|
||||||
|
);
|
||||||
|
|
||||||
// Make wasi available by default.
|
// Make wasi available by default.
|
||||||
let global_exports = context.get_global_exports();
|
let global_exports = store.borrow().global_exports().clone();
|
||||||
let preopen_dirs = compute_preopen_dirs(&args.flag_dir, &args.flag_mapdir);
|
let preopen_dirs = compute_preopen_dirs(&args.flag_dir, &args.flag_mapdir);
|
||||||
let argv = compute_argv(&args.arg_file, &args.arg_arg);
|
let argv = compute_argv(&args.arg_file, &args.arg_arg);
|
||||||
let environ = compute_environ(&args.flag_env);
|
let environ = compute_environ(&args.flag_env);
|
||||||
@@ -255,62 +265,98 @@ fn rmain() -> Result<(), Error> {
|
|||||||
let wasi = if args.flag_wasi_c {
|
let wasi = if args.flag_wasi_c {
|
||||||
#[cfg(feature = "wasi-c")]
|
#[cfg(feature = "wasi-c")]
|
||||||
{
|
{
|
||||||
instantiate_wasi_c("", global_exports, &preopen_dirs, &argv, &environ)?
|
instantiate_wasi_c("", global_exports.clone(), &preopen_dirs, &argv, &environ)?
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "wasi-c"))]
|
#[cfg(not(feature = "wasi-c"))]
|
||||||
{
|
{
|
||||||
bail!("wasi-c feature not enabled at build time")
|
bail!("wasi-c feature not enabled at build time")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instantiate_wasi("", global_exports, &preopen_dirs, &argv, &environ)?
|
instantiate_wasi("", global_exports.clone(), &preopen_dirs, &argv, &environ)?
|
||||||
};
|
};
|
||||||
|
|
||||||
context.name_instance("wasi_unstable".to_owned(), wasi);
|
module_registry.insert(
|
||||||
|
"wasi_unstable".to_owned(),
|
||||||
// Enable/disable producing of debug info.
|
Instance::from_handle(store.clone(), wasi)?,
|
||||||
context.set_debug_info(args.flag_g);
|
);
|
||||||
|
|
||||||
// Load the preload wasm modules.
|
// Load the preload wasm modules.
|
||||||
for filename in &args.flag_preload {
|
for filename in &args.flag_preload {
|
||||||
let path = Path::new(&filename);
|
let path = Path::new(&filename);
|
||||||
instantiate_module(&mut context, path)
|
instantiate_module(store.clone(), &module_registry, path)
|
||||||
.with_context(|_| format!("failed to process preload at `{}`", path.display()))?;
|
.with_context(|_| format!("failed to process preload at `{}`", path.display()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the main wasm module.
|
// Load the main wasm module.
|
||||||
let path = Path::new(&args.arg_file);
|
let path = Path::new(&args.arg_file);
|
||||||
handle_module(&mut context, &args, path)
|
handle_module(store, &module_registry, &args, path)
|
||||||
.with_context(|_| format!("failed to process main module `{}`", path.display()))?;
|
.with_context(|_| format!("failed to process main module `{}`", path.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate_module(
|
fn instantiate_module(
|
||||||
context: &mut Context,
|
store: Rc<RefCell<Store>>,
|
||||||
|
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<(InstanceHandle, Vec<u8>), Error> {
|
) -> Result<(Rc<RefCell<Instance>>, Rc<RefCell<Module>>, Vec<u8>), Error> {
|
||||||
// Read the wasm module binary.
|
// Read the wasm module binary.
|
||||||
let data = read_wasm(path.to_path_buf())?;
|
let data = read_wasm(path.to_path_buf())?;
|
||||||
|
|
||||||
// Compile and instantiating a wasm module.
|
let module = Rc::new(RefCell::new(Module::new(store.clone(), &data)?));
|
||||||
let handle = context.instantiate_module(None, &data)?;
|
|
||||||
Ok((handle, data))
|
// Resolve import using module_registry.
|
||||||
|
let imports = module
|
||||||
|
.borrow()
|
||||||
|
.imports()
|
||||||
|
.iter()
|
||||||
|
.map(|i| {
|
||||||
|
let module_name = i.module().to_string();
|
||||||
|
if let Some((instance, map)) = module_registry.get(&module_name) {
|
||||||
|
let field_name = i.name().to_string();
|
||||||
|
if let Some(export_index) = map.get(&field_name) {
|
||||||
|
Ok(instance.exports()[*export_index].clone())
|
||||||
|
} else {
|
||||||
|
bail!(
|
||||||
|
"Import {} was not found in module {}",
|
||||||
|
field_name,
|
||||||
|
module_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("Import module {} was not found", module_name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
let instance = Rc::new(RefCell::new(Instance::new(
|
||||||
|
store.clone(),
|
||||||
|
module.clone(),
|
||||||
|
&imports,
|
||||||
|
)?));
|
||||||
|
|
||||||
|
Ok((instance, module, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_module(context: &mut Context, args: &Args, path: &Path) -> Result<(), Error> {
|
fn handle_module(
|
||||||
let (mut instance, data) = instantiate_module(context, path)?;
|
store: Rc<RefCell<Store>>,
|
||||||
|
module_registry: &HashMap<String, (Instance, HashMap<String, usize>)>,
|
||||||
|
args: &Args,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let (instance, _module, data) = instantiate_module(store.clone(), module_registry, path)?;
|
||||||
|
|
||||||
// If a function to invoke was given, invoke it.
|
// If a function to invoke was given, invoke it.
|
||||||
if let Some(f) = &args.flag_invoke {
|
if let Some(f) = &args.flag_invoke {
|
||||||
let data = ModuleData::new(&data)?;
|
let data = ModuleData::new(&data)?;
|
||||||
invoke_export(context, &mut instance, &data, f, args)?;
|
invoke_export(store, instance, &data, f, args)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invoke_export(
|
fn invoke_export(
|
||||||
context: &mut Context,
|
store: Rc<RefCell<Store>>,
|
||||||
instance: &mut InstanceHandle,
|
instance: Rc<RefCell<Instance>>,
|
||||||
data: &ModuleData,
|
data: &ModuleData,
|
||||||
name: &str,
|
name: &str,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
@@ -318,11 +364,13 @@ fn invoke_export(
|
|||||||
use wasm_webidl_bindings::ast;
|
use wasm_webidl_bindings::ast;
|
||||||
use wasmtime_interface_types::Value;
|
use wasmtime_interface_types::Value;
|
||||||
|
|
||||||
|
let mut handle = instance.borrow().handle().clone();
|
||||||
|
|
||||||
// Use the binding information in `ModuleData` to figure out what arguments
|
// Use the binding information in `ModuleData` to figure out what arguments
|
||||||
// need to be passed to the function that we're invoking. Currently we take
|
// need to be passed to the function that we're invoking. Currently we take
|
||||||
// the CLI parameters and attempt to parse them into function arguments for
|
// the CLI parameters and attempt to parse them into function arguments for
|
||||||
// the function we'll invoke.
|
// the function we'll invoke.
|
||||||
let binding = data.binding_for_export(instance, name)?;
|
let binding = data.binding_for_export(&mut handle, name)?;
|
||||||
if binding.param_types()?.len() > 0 {
|
if binding.param_types()?.len() > 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"warning: using `--render` with a function that takes arguments \
|
"warning: using `--render` with a function that takes arguments \
|
||||||
@@ -358,8 +406,9 @@ fn invoke_export(
|
|||||||
|
|
||||||
// Invoke the function and then afterwards print all the results that came
|
// Invoke the function and then afterwards print all the results that came
|
||||||
// out, if there are any.
|
// out, if there are any.
|
||||||
|
let mut context = store.borrow().engine().borrow().create_wasmtime_context();
|
||||||
let results = data
|
let results = data
|
||||||
.invoke(context, instance, name, &values)
|
.invoke(&mut context, &mut handle, name, &values)
|
||||||
.with_context(|_| format!("failed to invoke `{}`", name))?;
|
.with_context(|_| format!("failed to invoke `{}`", name))?;
|
||||||
if results.len() > 0 {
|
if results.len() > 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
43
wasmtime-api/Cargo.toml
Normal file
43
wasmtime-api/Cargo.toml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasmtime-api"
|
||||||
|
authors = ["The Wasmtime Project Developers"]
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "High-level API to expose the Wasmtime runtime"
|
||||||
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
repository = "https://github.com/CraneStation/wasmtime"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "wasmtime_api"
|
||||||
|
crate-type = ["lib", "staticlib", "cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cranelift-codegen = "0.40.0"
|
||||||
|
cranelift-native = "0.40.0"
|
||||||
|
cranelift-entity = "0.40.0"
|
||||||
|
cranelift-wasm = "0.40.0"
|
||||||
|
cranelift-frontend = "0.40.0"
|
||||||
|
wasmtime-runtime = { path="../wasmtime-runtime" }
|
||||||
|
wasmtime-environ = { path="../wasmtime-environ" }
|
||||||
|
wasmtime-jit = { path="../wasmtime-jit" }
|
||||||
|
wasmparser = "0.36"
|
||||||
|
failure = { version = "0.1.3", default-features = false }
|
||||||
|
failure_derive = { version = "0.1.3", default-features = false }
|
||||||
|
target-lexicon = { version = "0.4.0", default-features = false }
|
||||||
|
region = "2.0.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
wasm-c-api = []
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# for wasmtime.rs
|
||||||
|
wasi-common = { git = "https://github.com/CraneStation/wasi-common", rev = "8ea7a98"}
|
||||||
|
docopt = "1.0.1"
|
||||||
|
serde = { "version" = "1.0.94", features = ["derive"] }
|
||||||
|
pretty_env_logger = "0.3.0"
|
||||||
|
wabt = "0.9.0"
|
||||||
|
wasmtime-wast = { path="../wasmtime-wast" }
|
||||||
|
wasmtime-wasi = { path="../wasmtime-wasi" }
|
||||||
|
rayon = "1.1"
|
||||||
|
file-per-thread-logger = "0.1.1"
|
||||||
3
wasmtime-api/README.md
Normal file
3
wasmtime-api/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Implementation of wasm-c-api in Rust
|
||||||
|
|
||||||
|
https://github.com/WebAssembly/wasm-c-api
|
||||||
155
wasmtime-api/c-examples/Makefile
Normal file
155
wasmtime-api/c-examples/Makefile
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
# Inherited from wasm-c-api/Makefile to just run C examples
|
||||||
|
|
||||||
|
WASM_FLAGS = -DWASM_API_DEBUG # -DWASM_API_DEBUG_LOG
|
||||||
|
C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fsanitize=address
|
||||||
|
CC_FLAGS = -std=c++11 ${C_FLAGS}
|
||||||
|
LD_FLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor
|
||||||
|
|
||||||
|
C_COMP = clang
|
||||||
|
|
||||||
|
WASMTIME_API_MODE = debug
|
||||||
|
|
||||||
|
|
||||||
|
# Base directories
|
||||||
|
WASMTIME_API_DIR = ..
|
||||||
|
WASM_DIR = wasm-c-api
|
||||||
|
EXAMPLE_DIR = ${WASM_DIR}/example
|
||||||
|
OUT_DIR = ${WASM_DIR}/out
|
||||||
|
|
||||||
|
# Example config
|
||||||
|
EXAMPLE_OUT = ${OUT_DIR}/example
|
||||||
|
EXAMPLES = \
|
||||||
|
hello \
|
||||||
|
callback \
|
||||||
|
trap \
|
||||||
|
start \
|
||||||
|
reflect \
|
||||||
|
global \
|
||||||
|
table \
|
||||||
|
memory \
|
||||||
|
hostref \
|
||||||
|
finalize \
|
||||||
|
serialize \
|
||||||
|
threads \
|
||||||
|
# multi \
|
||||||
|
|
||||||
|
# Wasm config
|
||||||
|
WASM_INCLUDE = ${WASM_DIR}/include
|
||||||
|
WASM_SRC = ${WASM_DIR}/src
|
||||||
|
WASM_OUT = ${OUT_DIR}
|
||||||
|
WASM_C_LIBS = wasm-bin wasm-rust-api
|
||||||
|
WASM_CC_LIBS = $(error unsupported C++)
|
||||||
|
|
||||||
|
|
||||||
|
# Compiler config
|
||||||
|
ifeq (${WASMTIME_API_MODE},release)
|
||||||
|
CARGO_BUILD_FLAGS = --features "wasm-c-api" --release
|
||||||
|
else
|
||||||
|
CARGO_BUILD_FLAGS = --features "wasm-c-api"
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq (${C_COMP},clang)
|
||||||
|
CC_COMP = clang++
|
||||||
|
LD_GROUP_START =
|
||||||
|
LD_GROUP_END =
|
||||||
|
else ifeq (${C_COMP},gcc)
|
||||||
|
CC_COMP = g++
|
||||||
|
LD_GROUP_START = -Wl,--start-group
|
||||||
|
LD_GROUP_END = -Wl,--end-group
|
||||||
|
else
|
||||||
|
$(error C_COMP set to unknown compiler, must be clang or gcc)
|
||||||
|
endif
|
||||||
|
|
||||||
|
WASMTIME_BIN_DIR = ${WASMTIME_API_DIR}/../target/${WASMTIME_API_MODE}
|
||||||
|
WASMTIME_C_LIB_DIR = ${WASMTIME_BIN_DIR}
|
||||||
|
WASMTIME_C_LIBS = wasmtime_api
|
||||||
|
WASMTIME_CC_LIBS = $(error unsupported c++)
|
||||||
|
|
||||||
|
WASMTIME_C_BINS = ${WASMTIME_C_LIBS:%=${WASMTIME_C_LIB_DIR}/lib%.a}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Examples
|
||||||
|
#
|
||||||
|
# To build Wasm APIs and run all examples:
|
||||||
|
# make all
|
||||||
|
#
|
||||||
|
# To run only C examples:
|
||||||
|
# make c
|
||||||
|
#
|
||||||
|
# To run only C++ examples:
|
||||||
|
# make cc
|
||||||
|
#
|
||||||
|
# To run individual C example (e.g. hello):
|
||||||
|
# make run-hello-c
|
||||||
|
#
|
||||||
|
# To run individual C++ example (e.g. hello):
|
||||||
|
# make run-hello-cc
|
||||||
|
#
|
||||||
|
|
||||||
|
.PHONY: all cc c
|
||||||
|
all: cc c
|
||||||
|
cc: ${EXAMPLES:%=run-%-cc}
|
||||||
|
c: ${EXAMPLES:%=run-%-c}
|
||||||
|
|
||||||
|
# Running a C / C++ example
|
||||||
|
run-%-c: ${EXAMPLE_OUT}/%-c ${EXAMPLE_OUT}/%.wasm
|
||||||
|
@echo ==== C ${@:run-%-c=%} ====; \
|
||||||
|
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||||
|
@echo ==== Done ====
|
||||||
|
|
||||||
|
run-%-cc: ${EXAMPLE_OUT}/%-cc ${EXAMPLE_OUT}/%.wasm
|
||||||
|
@echo ==== C++ ${@:run-%-cc=%} ====; \
|
||||||
|
cd ${EXAMPLE_OUT}; ./${@:run-%=%}
|
||||||
|
@echo ==== Done ====
|
||||||
|
|
||||||
|
# Compiling C / C++ example
|
||||||
|
${EXAMPLE_OUT}/%-c.o: ${EXAMPLE_DIR}/%.c ${WASM_INCLUDE}/wasm.h
|
||||||
|
mkdir -p ${EXAMPLE_OUT}
|
||||||
|
${C_COMP} -c ${C_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||||
|
|
||||||
|
${EXAMPLE_OUT}/%-cc.o: ${EXAMPLE_DIR}/%.cc ${WASM_INCLUDE}/wasm.hh
|
||||||
|
mkdir -p ${EXAMPLE_OUT}
|
||||||
|
${CC_COMP} -c ${CC_FLAGS} -I. -I${WASM_INCLUDE} $< -o $@
|
||||||
|
|
||||||
|
# Linking C / C++ example
|
||||||
|
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%-c}
|
||||||
|
${EXAMPLE_OUT}/%-c: ${EXAMPLE_OUT}/%-c.o ${WASMTIME_C_BINS}
|
||||||
|
${CC_COMP} ${CC_FLAGS} ${LD_FLAGS} $< -o $@ \
|
||||||
|
${LD_GROUP_START} \
|
||||||
|
${WASMTIME_C_BINS} \
|
||||||
|
${LD_GROUP_END} \
|
||||||
|
-ldl -pthread
|
||||||
|
|
||||||
|
# Installing Wasm binaries
|
||||||
|
.PRECIOUS: ${EXAMPLES:%=${EXAMPLE_OUT}/%.wasm}
|
||||||
|
${EXAMPLE_OUT}/%.wasm: ${EXAMPLE_DIR}/%.wasm
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Wasm C / C++ API
|
||||||
|
#
|
||||||
|
# To build both C / C++ APIs:
|
||||||
|
# make wasm
|
||||||
|
|
||||||
|
.PHONY: wasm wasm-c wasm-cc
|
||||||
|
wasm: wasm-c wasm-cc
|
||||||
|
wasm-c: ${WASMTIME_C_BIN}
|
||||||
|
wasm-cc: ${WASMTIME_CC_BIN}
|
||||||
|
|
||||||
|
${WASMTIME_C_BINS}: CARGO_RUN
|
||||||
|
cd ${WASMTIME_API_DIR}; cargo build --lib ${CARGO_BUILD_FLAGS}
|
||||||
|
|
||||||
|
.PHONY: CARGO_RUN
|
||||||
|
CARGO_RUN:
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Clean-up
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf ${OUT_DIR}
|
||||||
|
|
||||||
1
wasmtime-api/c-examples/wasm-c-api
Submodule
1
wasmtime-api/c-examples/wasm-c-api
Submodule
Submodule wasmtime-api/c-examples/wasm-c-api added at 6db391ee71
45
wasmtime-api/examples/gcd.rs
Normal file
45
wasmtime-api/examples/gcd.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//! Example of instantiating of the WebAssembly module and
|
||||||
|
//! invoking its exported function.
|
||||||
|
|
||||||
|
use failure::{format_err, Error};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fs::read;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wasmtime_api::*;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
let wasm = read("examples/gcd.wasm")?;
|
||||||
|
|
||||||
|
// Instantiate engine and store.
|
||||||
|
let engine = Rc::new(RefCell::new(Engine::default()));
|
||||||
|
let store = Rc::new(RefCell::new(Store::new(engine)));
|
||||||
|
|
||||||
|
// Load a module.
|
||||||
|
let module = Rc::new(RefCell::new(Module::new(store.clone(), &wasm)?));
|
||||||
|
|
||||||
|
// Find index of the `gcd` export.
|
||||||
|
let gcd_index = module
|
||||||
|
.borrow()
|
||||||
|
.exports()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, export)| export.name().to_string() == "gcd")
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
// Instantiate the module.
|
||||||
|
let instance = Rc::new(RefCell::new(Instance::new(store.clone(), module, &[])?));
|
||||||
|
|
||||||
|
// Invoke `gcd` export
|
||||||
|
let gcd = instance.borrow().exports()[gcd_index]
|
||||||
|
.borrow()
|
||||||
|
.func()
|
||||||
|
.clone();
|
||||||
|
let result = gcd
|
||||||
|
.borrow()
|
||||||
|
.call(&[Val::from(6i32), Val::from(27i32)])
|
||||||
|
.map_err(|e| format_err!("call error: {:?}", e))?;
|
||||||
|
|
||||||
|
println!("{:?}", result);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
BIN
wasmtime-api/examples/gcd.wasm
Executable file
BIN
wasmtime-api/examples/gcd.wasm
Executable file
Binary file not shown.
97
wasmtime-api/src/callable.rs
Normal file
97
wasmtime-api/src/callable.rs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
use crate::runtime::Store;
|
||||||
|
use crate::trap::Trap;
|
||||||
|
use crate::values::Val;
|
||||||
|
use core::any::Any;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use cranelift_codegen::ir;
|
||||||
|
use wasmtime_runtime::{VMContext, VMFunctionBody};
|
||||||
|
|
||||||
|
pub trait Callable: Any {
|
||||||
|
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Rc<RefCell<Trap>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct WasmtimeFn {
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
signature: ir::Signature,
|
||||||
|
body: *const VMFunctionBody,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmtimeFn {
|
||||||
|
pub fn new(
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
signature: ir::Signature,
|
||||||
|
body: *const VMFunctionBody,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
) -> WasmtimeFn {
|
||||||
|
WasmtimeFn {
|
||||||
|
store,
|
||||||
|
signature,
|
||||||
|
body,
|
||||||
|
vmctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Callable for WasmtimeFn {
|
||||||
|
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Rc<RefCell<Trap>>> {
|
||||||
|
use core::cmp::max;
|
||||||
|
use core::{mem, ptr};
|
||||||
|
|
||||||
|
let mut store = self.store.borrow_mut();
|
||||||
|
|
||||||
|
let context = store.context();
|
||||||
|
let value_size = mem::size_of::<u64>();
|
||||||
|
let mut values_vec: Vec<u64> = vec![0; max(params.len(), results.len())];
|
||||||
|
|
||||||
|
// Store the argument values into `values_vec`.
|
||||||
|
for (index, arg) in params.iter().enumerate() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = values_vec.as_mut_ptr().add(index);
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
Val::I32(x) => ptr::write(ptr as *mut i32, *x),
|
||||||
|
Val::I64(x) => ptr::write(ptr as *mut i64, *x),
|
||||||
|
Val::F32(x) => ptr::write(ptr as *mut u32, *x),
|
||||||
|
Val::F64(x) => ptr::write(ptr as *mut u64, *x),
|
||||||
|
_ => unimplemented!("WasmtimeFn arg"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the trampoline to call for this function.
|
||||||
|
let exec_code_buf = context
|
||||||
|
.compiler()
|
||||||
|
.get_published_trampoline(self.body, &self.signature, value_size)
|
||||||
|
.map_err(|_| Rc::new(RefCell::new(Trap::fake())))?; //was ActionError::Setup)?;
|
||||||
|
|
||||||
|
// Call the trampoline.
|
||||||
|
if let Err(message) = unsafe {
|
||||||
|
wasmtime_runtime::wasmtime_call_trampoline(
|
||||||
|
self.vmctx,
|
||||||
|
exec_code_buf,
|
||||||
|
values_vec.as_mut_ptr() as *mut u8,
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
return Err(Rc::new(RefCell::new(Trap::new(message))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the return values out of `values_vec`.
|
||||||
|
for (index, abi_param) in self.signature.returns.iter().enumerate() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = values_vec.as_ptr().add(index);
|
||||||
|
|
||||||
|
results[index] = match abi_param.value_type {
|
||||||
|
ir::types::I32 => Val::I32(ptr::read(ptr as *const i32)),
|
||||||
|
ir::types::I64 => Val::I64(ptr::read(ptr as *const i64)),
|
||||||
|
ir::types::F32 => Val::F32(ptr::read(ptr as *const u32)),
|
||||||
|
ir::types::F64 => Val::F64(ptr::read(ptr as *const u64)),
|
||||||
|
other => panic!("unsupported value type {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
63
wasmtime-api/src/context.rs
Normal file
63
wasmtime-api/src/context.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
use std::cell::{RefCell, RefMut};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use wasmtime_jit::{Compiler, Features};
|
||||||
|
|
||||||
|
use cranelift_codegen::settings;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Context {
|
||||||
|
compiler: Rc<RefCell<Compiler>>,
|
||||||
|
features: Features,
|
||||||
|
debug_info: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new(compiler: Compiler, features: Features, debug_info: bool) -> Context {
|
||||||
|
Context {
|
||||||
|
compiler: Rc::new(RefCell::new(compiler)),
|
||||||
|
features,
|
||||||
|
debug_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(flags: settings::Flags, features: Features, debug_info: bool) -> Context {
|
||||||
|
Context::new(create_compiler(flags), features, debug_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug_info(&self) -> bool {
|
||||||
|
self.debug_info
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compiler(&mut self) -> RefMut<Compiler> {
|
||||||
|
self.compiler.borrow_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Context {
|
||||||
|
fn hash<H>(&self, state: &mut H)
|
||||||
|
where
|
||||||
|
H: Hasher,
|
||||||
|
{
|
||||||
|
self.compiler.as_ptr().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Context {}
|
||||||
|
|
||||||
|
impl PartialEq for Context {
|
||||||
|
fn eq(&self, other: &Context) -> bool {
|
||||||
|
Rc::ptr_eq(&self.compiler, &other.compiler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_compiler(flags: settings::Flags) -> Compiler {
|
||||||
|
let isa = {
|
||||||
|
let isa_builder =
|
||||||
|
cranelift_native::builder().expect("host machine is not a supported target");
|
||||||
|
isa_builder.finish(flags)
|
||||||
|
};
|
||||||
|
|
||||||
|
Compiler::new(isa)
|
||||||
|
}
|
||||||
223
wasmtime-api/src/externals.rs
Normal file
223
wasmtime-api/src/externals.rs
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
use crate::callable::{Callable, WasmtimeFn};
|
||||||
|
use crate::runtime::Store;
|
||||||
|
use crate::trap::Trap;
|
||||||
|
use crate::types::{ExternType, FuncType, GlobalType, MemoryType};
|
||||||
|
use crate::values::Val;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
use crate::trampoline::generate_func_export;
|
||||||
|
use wasmtime_runtime::InstanceHandle;
|
||||||
|
// Externals
|
||||||
|
|
||||||
|
pub enum Extern {
|
||||||
|
Func(Rc<RefCell<Func>>),
|
||||||
|
Global(Rc<RefCell<Global>>),
|
||||||
|
Table(Rc<RefCell<Table>>),
|
||||||
|
Memory(Rc<RefCell<Memory>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extern {
|
||||||
|
pub fn func(&self) -> &Rc<RefCell<Func>> {
|
||||||
|
match self {
|
||||||
|
Extern::Func(func) => func,
|
||||||
|
_ => panic!("Extern::Func expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn global(&self) -> &Rc<RefCell<Global>> {
|
||||||
|
match self {
|
||||||
|
Extern::Global(global) => global,
|
||||||
|
_ => panic!("Extern::Global expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn table(&self) -> &Rc<RefCell<Table>> {
|
||||||
|
match self {
|
||||||
|
Extern::Table(table) => table,
|
||||||
|
_ => panic!("Extern::Table expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn memory(&self) -> &Rc<RefCell<Memory>> {
|
||||||
|
match self {
|
||||||
|
Extern::Memory(memory) => memory,
|
||||||
|
_ => panic!("Extern::Memory expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> ExternType {
|
||||||
|
match self {
|
||||||
|
Extern::Func(ft) => ExternType::ExternFunc(ft.borrow().r#type().clone()),
|
||||||
|
Extern::Memory(ft) => ExternType::ExternMemory(ft.borrow().r#type().clone()),
|
||||||
|
_ => unimplemented!("ExternType::type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_wasmtime_export(&mut self) -> wasmtime_runtime::Export {
|
||||||
|
match self {
|
||||||
|
Extern::Func(f) => {
|
||||||
|
if f.borrow().anchor.is_none() {
|
||||||
|
generate_func_export(&f).expect("generate_func_export");
|
||||||
|
}
|
||||||
|
f.borrow().anchor.as_ref().unwrap().1.clone()
|
||||||
|
}
|
||||||
|
_ => unimplemented!("get_wasmtime_export"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_wasmtime_export(
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
instance_handle: InstanceHandle,
|
||||||
|
export: wasmtime_runtime::Export,
|
||||||
|
) -> Extern {
|
||||||
|
use cranelift_wasm::GlobalInit;
|
||||||
|
match export {
|
||||||
|
wasmtime_runtime::Export::Function {
|
||||||
|
address,
|
||||||
|
vmctx,
|
||||||
|
ref signature,
|
||||||
|
} => {
|
||||||
|
let ty = FuncType::from_cranelift_signature(signature.clone());
|
||||||
|
let callable = WasmtimeFn::new(store.clone(), signature.clone(), address, vmctx);
|
||||||
|
let mut f = Func::new(store, ty, Rc::new(callable));
|
||||||
|
f.anchor = Some((instance_handle, export.clone()));
|
||||||
|
Extern::Func(Rc::new(RefCell::new(f)))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Memory {
|
||||||
|
definition: _,
|
||||||
|
vmctx: _,
|
||||||
|
memory,
|
||||||
|
} => {
|
||||||
|
let ty = MemoryType::from_cranelift_memory(memory.memory.clone());
|
||||||
|
let m = Memory::new(store, ty);
|
||||||
|
Extern::Memory(Rc::new(RefCell::new(m)))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Global {
|
||||||
|
definition: _,
|
||||||
|
vmctx: _,
|
||||||
|
global,
|
||||||
|
} => {
|
||||||
|
let ty = GlobalType::from_cranelift_global(global.clone());
|
||||||
|
let val = match global.initializer {
|
||||||
|
GlobalInit::I32Const(i) => Val::from(i),
|
||||||
|
GlobalInit::I64Const(i) => Val::from(i),
|
||||||
|
GlobalInit::F32Const(f) => Val::from_f32_bits(f),
|
||||||
|
GlobalInit::F64Const(f) => Val::from_f64_bits(f),
|
||||||
|
_ => unimplemented!("from_wasmtime_export initializer"),
|
||||||
|
};
|
||||||
|
Extern::Global(Rc::new(RefCell::new(Global::new(store, ty, val))))
|
||||||
|
}
|
||||||
|
wasmtime_runtime::Export::Table { .. } => {
|
||||||
|
// TODO Extern::Table(Rc::new(RefCell::new(Table::new(store, ty, val))))
|
||||||
|
Extern::Table(Rc::new(RefCell::new(Table)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Func {
|
||||||
|
_store: Rc<RefCell<Store>>,
|
||||||
|
callable: Rc<dyn Callable + 'static>,
|
||||||
|
r#type: FuncType,
|
||||||
|
pub(crate) anchor: Option<(InstanceHandle, wasmtime_runtime::Export)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Func {
|
||||||
|
pub fn new(
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
r#type: FuncType,
|
||||||
|
callable: Rc<dyn Callable + 'static>,
|
||||||
|
) -> Func {
|
||||||
|
Func {
|
||||||
|
_store: store,
|
||||||
|
callable,
|
||||||
|
r#type,
|
||||||
|
anchor: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> &FuncType {
|
||||||
|
&self.r#type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn param_arity(&self) -> usize {
|
||||||
|
self.r#type.params().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn result_arity(&self) -> usize {
|
||||||
|
self.r#type.results().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn callable(&self) -> &(dyn Callable + 'static) {
|
||||||
|
self.callable.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Rc<RefCell<Trap>>> {
|
||||||
|
let mut results = vec![Val::default(); self.result_arity()];
|
||||||
|
self.callable.call(params, &mut results)?;
|
||||||
|
Ok(results.into_boxed_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Global {
|
||||||
|
_store: Rc<RefCell<Store>>,
|
||||||
|
r#type: GlobalType,
|
||||||
|
val: Val,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Global {
|
||||||
|
pub fn new(store: Rc<RefCell<Store>>, r#type: GlobalType, val: Val) -> Global {
|
||||||
|
Global {
|
||||||
|
_store: store,
|
||||||
|
r#type,
|
||||||
|
val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> &GlobalType {
|
||||||
|
&self.r#type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> &Val {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, val: Val) {
|
||||||
|
self.val = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Table;
|
||||||
|
|
||||||
|
pub struct Memory {
|
||||||
|
_store: Rc<RefCell<Store>>,
|
||||||
|
r#type: MemoryType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
pub fn new(store: Rc<RefCell<Store>>, r#type: MemoryType) -> Memory {
|
||||||
|
Memory {
|
||||||
|
_store: store,
|
||||||
|
r#type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> &MemoryType {
|
||||||
|
&self.r#type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> *const u8 {
|
||||||
|
unimplemented!("Memory::data")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_size(&self) -> usize {
|
||||||
|
unimplemented!("Memory::data_size")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> u32 {
|
||||||
|
unimplemented!("Memory::size")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(&mut self, _delta: u32) -> bool {
|
||||||
|
unimplemented!("Memory::grow")
|
||||||
|
}
|
||||||
|
}
|
||||||
137
wasmtime-api/src/instance.rs
Normal file
137
wasmtime-api/src/instance.rs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
use crate::context::Context;
|
||||||
|
use crate::externals::Extern;
|
||||||
|
use crate::module::Module;
|
||||||
|
use crate::runtime::Store;
|
||||||
|
use failure::Error;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use wasmtime_jit::{instantiate, Resolver};
|
||||||
|
use wasmtime_runtime::{Export, InstanceHandle};
|
||||||
|
|
||||||
|
struct SimpleResolver {
|
||||||
|
imports: Vec<(String, String, Rc<RefCell<Extern>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver for SimpleResolver {
|
||||||
|
fn resolve(&mut self, name: &str, field: &str) -> Option<Export> {
|
||||||
|
// TODO speedup lookup
|
||||||
|
self.imports
|
||||||
|
.iter()
|
||||||
|
.find(|(n, f, _)| name == n && field == f)
|
||||||
|
.map(|(_, _, e)| e.borrow_mut().get_wasmtime_export())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instantiate_in_context(
|
||||||
|
data: &[u8],
|
||||||
|
imports: Vec<(String, String, Rc<RefCell<Extern>>)>,
|
||||||
|
mut context: Context,
|
||||||
|
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||||
|
) -> Result<(InstanceHandle, HashSet<Context>), Error> {
|
||||||
|
let mut contexts = HashSet::new();
|
||||||
|
let debug_info = context.debug_info();
|
||||||
|
let mut resolver = SimpleResolver { imports };
|
||||||
|
let instance = instantiate(
|
||||||
|
&mut context.compiler(),
|
||||||
|
data,
|
||||||
|
&mut resolver,
|
||||||
|
exports,
|
||||||
|
debug_info,
|
||||||
|
)?;
|
||||||
|
contexts.insert(context);
|
||||||
|
Ok((instance, contexts))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Instance {
|
||||||
|
instance_handle: InstanceHandle,
|
||||||
|
|
||||||
|
// We need to keep CodeMemory alive.
|
||||||
|
contexts: HashSet<Context>,
|
||||||
|
|
||||||
|
exports: Box<[Rc<RefCell<Extern>>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance {
|
||||||
|
pub fn new(
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
module: Rc<RefCell<Module>>,
|
||||||
|
externs: &[Rc<RefCell<Extern>>],
|
||||||
|
) -> Result<Instance, Error> {
|
||||||
|
let context = store.borrow_mut().context().clone();
|
||||||
|
let exports = store.borrow_mut().global_exports().clone();
|
||||||
|
let imports = module
|
||||||
|
.borrow()
|
||||||
|
.imports()
|
||||||
|
.iter()
|
||||||
|
.zip(externs.iter())
|
||||||
|
.map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let (mut instance_handle, contexts) =
|
||||||
|
instantiate_in_context(module.borrow().binary(), imports, context, exports)?;
|
||||||
|
|
||||||
|
let exports = {
|
||||||
|
let module = module.borrow();
|
||||||
|
let mut exports = Vec::with_capacity(module.exports().len());
|
||||||
|
for export in module.exports() {
|
||||||
|
let name = export.name().to_string();
|
||||||
|
let export = instance_handle.lookup(&name).expect("export");
|
||||||
|
exports.push(Rc::new(RefCell::new(Extern::from_wasmtime_export(
|
||||||
|
store.clone(),
|
||||||
|
instance_handle.clone(),
|
||||||
|
export,
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
exports.into_boxed_slice()
|
||||||
|
};
|
||||||
|
Ok(Instance {
|
||||||
|
instance_handle,
|
||||||
|
contexts,
|
||||||
|
exports,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exports(&self) -> &[Rc<RefCell<Extern>>] {
|
||||||
|
&self.exports
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_handle(
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
instance_handle: InstanceHandle,
|
||||||
|
) -> Result<(Instance, HashMap<String, usize>), Error> {
|
||||||
|
let contexts = HashSet::new();
|
||||||
|
|
||||||
|
let mut exports = Vec::new();
|
||||||
|
let mut export_names_map = HashMap::new();
|
||||||
|
let mut mutable = instance_handle.clone();
|
||||||
|
for (name, _) in instance_handle.clone().exports() {
|
||||||
|
let export = mutable.lookup(name).expect("export");
|
||||||
|
export_names_map.insert(name.to_owned(), exports.len());
|
||||||
|
exports.push(Rc::new(RefCell::new(Extern::from_wasmtime_export(
|
||||||
|
store.clone(),
|
||||||
|
instance_handle.clone(),
|
||||||
|
export.clone(),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Instance {
|
||||||
|
instance_handle,
|
||||||
|
contexts,
|
||||||
|
exports: exports.into_boxed_slice(),
|
||||||
|
},
|
||||||
|
export_names_map,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(&self) -> &InstanceHandle {
|
||||||
|
&self.instance_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_wasmtime_memory(&self) -> Option<wasmtime_runtime::Export> {
|
||||||
|
let mut instance_handle = self.instance_handle.clone();
|
||||||
|
instance_handle.lookup("memory")
|
||||||
|
}
|
||||||
|
}
|
||||||
27
wasmtime-api/src/lib.rs
Normal file
27
wasmtime-api/src/lib.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//! Wasmtime embed API. Based on wasm-c-api.
|
||||||
|
|
||||||
|
mod callable;
|
||||||
|
mod context;
|
||||||
|
mod externals;
|
||||||
|
mod instance;
|
||||||
|
mod module;
|
||||||
|
mod runtime;
|
||||||
|
mod trampoline;
|
||||||
|
mod trap;
|
||||||
|
mod types;
|
||||||
|
mod values;
|
||||||
|
|
||||||
|
#[cfg(feature = "wasm-c-api")]
|
||||||
|
pub mod wasm;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
|
|
||||||
|
pub use crate::callable::Callable;
|
||||||
|
pub use crate::externals::*;
|
||||||
|
pub use crate::instance::Instance;
|
||||||
|
pub use crate::module::Module;
|
||||||
|
pub use crate::runtime::{Config, Engine, Store};
|
||||||
|
pub use crate::trap::Trap;
|
||||||
|
pub use crate::types::*;
|
||||||
|
pub use crate::values::*;
|
||||||
179
wasmtime-api/src/module.rs
Normal file
179
wasmtime-api/src/module.rs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
use crate::runtime::Store;
|
||||||
|
use crate::types::{
|
||||||
|
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
|
||||||
|
ValType,
|
||||||
|
};
|
||||||
|
use failure::Error;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use wasmparser::{validate, ExternalKind, ImportSectionEntryType, ModuleReader, SectionCode};
|
||||||
|
|
||||||
|
fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType {
|
||||||
|
assert!(!mt.shared);
|
||||||
|
MemoryType::new(Limits::new(
|
||||||
|
mt.limits.initial,
|
||||||
|
mt.limits.maximum.unwrap_or(::std::u32::MAX),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_global_type(gt: &wasmparser::GlobalType) -> GlobalType {
|
||||||
|
let mutability = if gt.mutable {
|
||||||
|
Mutability::Var
|
||||||
|
} else {
|
||||||
|
Mutability::Const
|
||||||
|
};
|
||||||
|
GlobalType::new(into_valtype(>.content_type), mutability)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_valtype(ty: &wasmparser::Type) -> ValType {
|
||||||
|
use wasmparser::Type::*;
|
||||||
|
match ty {
|
||||||
|
I32 => ValType::I32,
|
||||||
|
I64 => ValType::I64,
|
||||||
|
F32 => ValType::F32,
|
||||||
|
F64 => ValType::F64,
|
||||||
|
_ => unimplemented!("types in into_valtype"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_func_type(mt: wasmparser::FuncType) -> FuncType {
|
||||||
|
assert!(mt.form == wasmparser::Type::Func);
|
||||||
|
let params = mt.params.iter().map(into_valtype).collect::<Vec<_>>();
|
||||||
|
let returns = mt.returns.iter().map(into_valtype).collect::<Vec<_>>();
|
||||||
|
FuncType::new(params.into_boxed_slice(), returns.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_imports_and_exports(
|
||||||
|
binary: &[u8],
|
||||||
|
) -> Result<(Box<[ImportType]>, Box<[ExportType]>), Error> {
|
||||||
|
let mut reader = ModuleReader::new(binary)?;
|
||||||
|
let mut imports = Vec::new();
|
||||||
|
let mut exports = Vec::new();
|
||||||
|
let mut memories = Vec::new();
|
||||||
|
let mut func_sig = Vec::new();
|
||||||
|
let mut sigs = Vec::new();
|
||||||
|
let mut globals = Vec::new();
|
||||||
|
while !reader.eof() {
|
||||||
|
let section = reader.read()?;
|
||||||
|
match section.code {
|
||||||
|
SectionCode::Memory => {
|
||||||
|
let section = section.get_memory_section_reader()?;
|
||||||
|
memories.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
memories.push(into_memory_type(entry?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionCode::Type => {
|
||||||
|
let section = section.get_type_section_reader()?;
|
||||||
|
sigs.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
sigs.push(into_func_type(entry?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionCode::Function => {
|
||||||
|
let section = section.get_function_section_reader()?;
|
||||||
|
sigs.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
func_sig.push(entry?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionCode::Global => {
|
||||||
|
let section = section.get_global_section_reader()?;
|
||||||
|
globals.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
globals.push(into_global_type(&entry?.ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionCode::Import => {
|
||||||
|
let section = section.get_import_section_reader()?;
|
||||||
|
imports.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
let entry = entry?;
|
||||||
|
let module = String::from(entry.module).into();
|
||||||
|
let name = String::from(entry.field).into();
|
||||||
|
let r#type = match entry.ty {
|
||||||
|
ImportSectionEntryType::Function(index) => {
|
||||||
|
func_sig.push(index);
|
||||||
|
let sig = &sigs[index as usize];
|
||||||
|
ExternType::ExternFunc(sig.clone())
|
||||||
|
}
|
||||||
|
ImportSectionEntryType::Table(_tt) => {
|
||||||
|
unimplemented!("ImportSectionEntryType::Table")
|
||||||
|
}
|
||||||
|
ImportSectionEntryType::Memory(mt) => {
|
||||||
|
let memory = into_memory_type(mt);
|
||||||
|
memories.push(memory.clone());
|
||||||
|
ExternType::ExternMemory(memory)
|
||||||
|
}
|
||||||
|
ImportSectionEntryType::Global(gt) => {
|
||||||
|
let global = into_global_type(>);
|
||||||
|
globals.push(global.clone());
|
||||||
|
ExternType::ExternGlobal(global)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
imports.push(ImportType::new(module, name, r#type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionCode::Export => {
|
||||||
|
let section = section.get_export_section_reader()?;
|
||||||
|
exports.reserve_exact(section.get_count() as usize);
|
||||||
|
for entry in section {
|
||||||
|
let entry = entry?;
|
||||||
|
let name = String::from(entry.field).into();
|
||||||
|
let r#type = match entry.kind {
|
||||||
|
ExternalKind::Function => {
|
||||||
|
let sig_index = func_sig[entry.index as usize] as usize;
|
||||||
|
let sig = &sigs[sig_index];
|
||||||
|
ExternType::ExternFunc(sig.clone())
|
||||||
|
}
|
||||||
|
ExternalKind::Table => unimplemented!("ExternalKind::Table"),
|
||||||
|
ExternalKind::Memory => {
|
||||||
|
ExternType::ExternMemory(memories[entry.index as usize].clone())
|
||||||
|
}
|
||||||
|
ExternalKind::Global => {
|
||||||
|
ExternType::ExternGlobal(globals[entry.index as usize].clone())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.push(ExportType::new(name, r#type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// skip other sections
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((imports.into_boxed_slice(), exports.into_boxed_slice()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Module {
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
binary: Box<[u8]>,
|
||||||
|
imports: Box<[ImportType]>,
|
||||||
|
exports: Box<[ExportType]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
pub fn new(store: Rc<RefCell<Store>>, binary: &[u8]) -> Result<Module, Error> {
|
||||||
|
let (imports, exports) = read_imports_and_exports(binary)?;
|
||||||
|
Ok(Module {
|
||||||
|
store,
|
||||||
|
binary: binary.into(),
|
||||||
|
imports,
|
||||||
|
exports,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub(crate) fn binary(&self) -> &[u8] {
|
||||||
|
&self.binary
|
||||||
|
}
|
||||||
|
pub fn validate(_store: &Store, binary: &[u8]) -> bool {
|
||||||
|
validate(binary, None)
|
||||||
|
}
|
||||||
|
pub fn imports(&self) -> &[ImportType] {
|
||||||
|
&self.imports
|
||||||
|
}
|
||||||
|
pub fn exports(&self) -> &[ExportType] {
|
||||||
|
&self.exports
|
||||||
|
}
|
||||||
|
}
|
||||||
114
wasmtime-api/src/runtime.rs
Normal file
114
wasmtime-api/src/runtime.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::context::{create_compiler, Context};
|
||||||
|
|
||||||
|
use cranelift_codegen::settings;
|
||||||
|
use wasmtime_jit::Features;
|
||||||
|
|
||||||
|
// Runtime Environment
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
|
||||||
|
fn default_flags() -> settings::Flags {
|
||||||
|
let flag_builder = settings::builder();
|
||||||
|
settings::Flags::new(flag_builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
flags: settings::Flags,
|
||||||
|
features: Features,
|
||||||
|
debug_info: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
debug_info: false,
|
||||||
|
features: Default::default(),
|
||||||
|
flags: default_flags(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(flags: settings::Flags, features: Features, debug_info: bool) -> Config {
|
||||||
|
Config {
|
||||||
|
flags,
|
||||||
|
features,
|
||||||
|
debug_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug_info(&self) -> bool {
|
||||||
|
self.debug_info
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flags(&self) -> &settings::Flags {
|
||||||
|
&self.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn features(&self) -> &Features {
|
||||||
|
&self.features
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Engine
|
||||||
|
|
||||||
|
pub struct Engine {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine {
|
||||||
|
pub fn new(config: Config) -> Engine {
|
||||||
|
Engine { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default() -> Engine {
|
||||||
|
Engine::new(Config::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn config(&self) -> &Config {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_wasmtime_context(&self) -> wasmtime_jit::Context {
|
||||||
|
let flags = self.config.flags().clone();
|
||||||
|
wasmtime_jit::Context::new(Box::new(create_compiler(flags)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store
|
||||||
|
|
||||||
|
pub struct Store {
|
||||||
|
engine: Rc<RefCell<Engine>>,
|
||||||
|
context: Context,
|
||||||
|
global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Store {
|
||||||
|
pub fn new(engine: Rc<RefCell<Engine>>) -> Store {
|
||||||
|
let flags = engine.borrow().config().flags().clone();
|
||||||
|
let features = engine.borrow().config().features().clone();
|
||||||
|
let debug_info = engine.borrow().config().debug_info();
|
||||||
|
Store {
|
||||||
|
engine,
|
||||||
|
context: Context::create(flags, features, debug_info),
|
||||||
|
global_exports: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn engine(&self) -> &Rc<RefCell<Engine>> {
|
||||||
|
&self.engine
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn context(&mut self) -> &mut Context {
|
||||||
|
&mut self.context
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific to wasmtime: hack to pass memory around to wasi
|
||||||
|
pub fn global_exports(
|
||||||
|
&self,
|
||||||
|
) -> &Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> {
|
||||||
|
&self.global_exports
|
||||||
|
}
|
||||||
|
}
|
||||||
83
wasmtime-api/src/trampoline/code_memory.rs
Normal file
83
wasmtime-api/src/trampoline/code_memory.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//! Memory management for executable code.
|
||||||
|
// Copy of wasmtime's wasmtime-jit/src/code_memory.rs
|
||||||
|
|
||||||
|
use core::{cmp, mem};
|
||||||
|
use region;
|
||||||
|
use std::string::String;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use wasmtime_runtime::{Mmap, VMFunctionBody};
|
||||||
|
|
||||||
|
/// Memory manager for executable code.
|
||||||
|
pub(crate) struct CodeMemory {
|
||||||
|
current: Mmap,
|
||||||
|
mmaps: Vec<Mmap>,
|
||||||
|
position: usize,
|
||||||
|
published: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeMemory {
|
||||||
|
/// Create a new `CodeMemory` instance.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
current: Mmap::new(),
|
||||||
|
mmaps: Vec::new(),
|
||||||
|
position: 0,
|
||||||
|
published: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate `size` bytes of memory which can be made executable later by
|
||||||
|
/// calling `publish()`. Note that we allocate the memory as writeable so
|
||||||
|
/// that it can be written to and patched, though we make it readonly before
|
||||||
|
/// actually executing from it.
|
||||||
|
///
|
||||||
|
/// TODO: Add an alignment flag.
|
||||||
|
fn allocate(&mut self, size: usize) -> Result<&mut [u8], String> {
|
||||||
|
if self.current.len() - self.position < size {
|
||||||
|
self.mmaps.push(mem::replace(
|
||||||
|
&mut self.current,
|
||||||
|
Mmap::with_at_least(cmp::max(0x10000, size))?,
|
||||||
|
));
|
||||||
|
self.position = 0;
|
||||||
|
}
|
||||||
|
let old_position = self.position;
|
||||||
|
self.position += size;
|
||||||
|
Ok(&mut self.current.as_mut_slice()[old_position..self.position])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert mut a slice from u8 to VMFunctionBody.
|
||||||
|
fn view_as_mut_vmfunc_slice(slice: &mut [u8]) -> &mut [VMFunctionBody] {
|
||||||
|
let byte_ptr: *mut [u8] = slice;
|
||||||
|
let body_ptr = byte_ptr as *mut [VMFunctionBody];
|
||||||
|
unsafe { &mut *body_ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate enough memory to hold a copy of `slice` and copy the data into it.
|
||||||
|
/// TODO: Reorganize the code that calls this to emit code directly into the
|
||||||
|
/// mmap region rather than into a Vec that we need to copy in.
|
||||||
|
pub fn allocate_copy_of_byte_slice(
|
||||||
|
&mut self,
|
||||||
|
slice: &[u8],
|
||||||
|
) -> Result<&mut [VMFunctionBody], String> {
|
||||||
|
let new = self.allocate(slice.len())?;
|
||||||
|
new.copy_from_slice(slice);
|
||||||
|
Ok(Self::view_as_mut_vmfunc_slice(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make all allocated memory executable.
|
||||||
|
pub fn publish(&mut self) {
|
||||||
|
self.mmaps
|
||||||
|
.push(mem::replace(&mut self.current, Mmap::new()));
|
||||||
|
self.position = 0;
|
||||||
|
|
||||||
|
for m in &mut self.mmaps[self.published..] {
|
||||||
|
if m.len() != 0 {
|
||||||
|
unsafe {
|
||||||
|
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
||||||
|
}
|
||||||
|
.expect("unable to make memory readonly and executable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.published = self.mmaps.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
289
wasmtime-api/src/trampoline/create_handle.rs
Normal file
289
wasmtime-api/src/trampoline/create_handle.rs
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
//! Support for a calling of an imported function.
|
||||||
|
|
||||||
|
use crate::trampoline::code_memory::CodeMemory;
|
||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode};
|
||||||
|
use cranelift_codegen::Context;
|
||||||
|
use cranelift_codegen::{binemit, ir, isa};
|
||||||
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex};
|
||||||
|
//use target_lexicon::HOST;
|
||||||
|
use failure::Error;
|
||||||
|
use wasmtime_environ::{Export, Module};
|
||||||
|
use wasmtime_runtime::{Imports, InstanceHandle, VMContext, VMFunctionBody};
|
||||||
|
|
||||||
|
use core::cmp;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{Func, Trap, Val};
|
||||||
|
|
||||||
|
struct TrampolineState {
|
||||||
|
func: Rc<RefCell<Func>>,
|
||||||
|
trap: Option<Rc<RefCell<Trap>>>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
code_memory: CodeMemory,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *mut i64) -> u32 {
|
||||||
|
let mut instance = InstanceHandle::from_vmctx(vmctx);
|
||||||
|
|
||||||
|
let (args, returns_len) = {
|
||||||
|
let module = instance.module_ref();
|
||||||
|
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
for i in 1..signature.params.len() {
|
||||||
|
args.push(Val::read_value_from(
|
||||||
|
values_vec.offset(i as isize - 1),
|
||||||
|
signature.params[i].value_type,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(args, signature.returns.len())
|
||||||
|
};
|
||||||
|
|
||||||
|
let func = instance
|
||||||
|
.host_state()
|
||||||
|
.downcast_mut::<TrampolineState>()
|
||||||
|
.expect("state")
|
||||||
|
.func
|
||||||
|
.borrow();
|
||||||
|
|
||||||
|
match func.call(&args) {
|
||||||
|
Ok(returns) => {
|
||||||
|
for i in 0..returns_len {
|
||||||
|
// TODO check signature.returns[i].value_type ?
|
||||||
|
returns[i].write_value_to(values_vec.offset(i as isize));
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(trap) => {
|
||||||
|
// TODO read custom exception
|
||||||
|
InstanceHandle::from_vmctx(vmctx)
|
||||||
|
.host_state()
|
||||||
|
.downcast_mut::<TrampolineState>()
|
||||||
|
.expect("state")
|
||||||
|
.trap = Some(trap);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a trampoline for invoking a Callable.
|
||||||
|
fn make_trampoline(
|
||||||
|
isa: &dyn isa::TargetIsa,
|
||||||
|
code_memory: &mut CodeMemory,
|
||||||
|
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||||
|
call_id: u32,
|
||||||
|
signature: &ir::Signature,
|
||||||
|
) -> *const VMFunctionBody {
|
||||||
|
// Mostly reverse copy of the similar method from wasmtime's
|
||||||
|
// wasmtime-jit/src/compiler.rs.
|
||||||
|
let pointer_type = isa.pointer_type();
|
||||||
|
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
||||||
|
|
||||||
|
// Add the `vmctx` parameter.
|
||||||
|
stub_sig.params.push(ir::AbiParam::special(
|
||||||
|
pointer_type,
|
||||||
|
ir::ArgumentPurpose::VMContext,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add the `call_id` parameter.
|
||||||
|
stub_sig.params.push(ir::AbiParam::new(types::I32));
|
||||||
|
|
||||||
|
// Add the `values_vec` parameter.
|
||||||
|
stub_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||||
|
|
||||||
|
// Add error/trap return.
|
||||||
|
stub_sig.returns.push(ir::AbiParam::new(types::I32));
|
||||||
|
|
||||||
|
let values_vec_len = 8 * cmp::max(signature.params.len() - 1, signature.returns.len()) as u32;
|
||||||
|
|
||||||
|
let mut context = Context::new();
|
||||||
|
context.func =
|
||||||
|
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone());
|
||||||
|
|
||||||
|
let ss = context.func.create_stack_slot(StackSlotData::new(
|
||||||
|
StackSlotKind::ExplicitSlot,
|
||||||
|
values_vec_len,
|
||||||
|
));
|
||||||
|
let value_size = 8;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||||
|
let block0 = builder.create_ebb();
|
||||||
|
|
||||||
|
builder.append_ebb_params_for_function_params(block0);
|
||||||
|
builder.switch_to_block(block0);
|
||||||
|
builder.seal_block(block0);
|
||||||
|
|
||||||
|
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
|
||||||
|
let mflags = ir::MemFlags::trusted();
|
||||||
|
for i in 1..signature.params.len() {
|
||||||
|
if i == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = builder.func.dfg.ebb_params(block0)[i];
|
||||||
|
builder.ins().store(
|
||||||
|
mflags,
|
||||||
|
val,
|
||||||
|
values_vec_ptr_val,
|
||||||
|
((i - 1) * value_size) as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
|
||||||
|
let call_id_val = builder.ins().iconst(types::I32, call_id as i64);
|
||||||
|
|
||||||
|
let callee_args = vec![vmctx_ptr_val, call_id_val, values_vec_ptr_val];
|
||||||
|
|
||||||
|
let new_sig = builder.import_signature(stub_sig.clone());
|
||||||
|
|
||||||
|
let callee_value = builder
|
||||||
|
.ins()
|
||||||
|
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
|
||||||
|
let call = builder
|
||||||
|
.ins()
|
||||||
|
.call_indirect(new_sig, callee_value, &callee_args);
|
||||||
|
|
||||||
|
let call_result = builder.func.dfg.inst_results(call)[0];
|
||||||
|
builder.ins().trapnz(call_result, TrapCode::User(0));
|
||||||
|
|
||||||
|
let mflags = ir::MemFlags::trusted();
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for (i, r) in signature.returns.iter().enumerate() {
|
||||||
|
let load = builder.ins().load(
|
||||||
|
r.value_type,
|
||||||
|
mflags,
|
||||||
|
values_vec_ptr_val,
|
||||||
|
(i * value_size) as i32,
|
||||||
|
);
|
||||||
|
results.push(load);
|
||||||
|
}
|
||||||
|
builder.ins().return_(&results);
|
||||||
|
builder.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut code_buf: Vec<u8> = Vec::new();
|
||||||
|
let mut reloc_sink = RelocSink {};
|
||||||
|
let mut trap_sink = binemit::NullTrapSink {};
|
||||||
|
let mut stackmap_sink = binemit::NullStackmapSink {};
|
||||||
|
context
|
||||||
|
.compile_and_emit(
|
||||||
|
isa,
|
||||||
|
&mut code_buf,
|
||||||
|
&mut reloc_sink,
|
||||||
|
&mut trap_sink,
|
||||||
|
&mut stackmap_sink,
|
||||||
|
)
|
||||||
|
.expect("compile_and_emit");
|
||||||
|
|
||||||
|
code_memory
|
||||||
|
.allocate_copy_of_byte_slice(&code_buf)
|
||||||
|
.expect("allocate_copy_of_byte_slice")
|
||||||
|
.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_handle(func: &Rc<RefCell<Func>>) -> Result<InstanceHandle, Error> {
|
||||||
|
let sig = func.borrow().r#type().get_cranelift_signature().clone();
|
||||||
|
|
||||||
|
let isa = {
|
||||||
|
let isa_builder =
|
||||||
|
cranelift_native::builder().expect("host machine is not a supported target");
|
||||||
|
let flag_builder = cranelift_codegen::settings::builder();
|
||||||
|
isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder))
|
||||||
|
};
|
||||||
|
|
||||||
|
let global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>> =
|
||||||
|
Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
||||||
|
let mut module = Module::new();
|
||||||
|
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
|
||||||
|
PrimaryMap::new();
|
||||||
|
let mut code_memory = CodeMemory::new();
|
||||||
|
|
||||||
|
//let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||||
|
//let call_conv = isa::CallConv::triple_default(&HOST);
|
||||||
|
|
||||||
|
let dependencies = HashSet::new();
|
||||||
|
let memories = PrimaryMap::new();
|
||||||
|
|
||||||
|
let sig_id = module.signatures.push(sig.clone());
|
||||||
|
let func_id = module.functions.push(sig_id);
|
||||||
|
module
|
||||||
|
.exports
|
||||||
|
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||||
|
let trampoline = make_trampoline(
|
||||||
|
isa.as_ref(),
|
||||||
|
&mut code_memory,
|
||||||
|
&mut fn_builder_ctx,
|
||||||
|
func_id.index() as u32,
|
||||||
|
&sig,
|
||||||
|
);
|
||||||
|
code_memory.publish();
|
||||||
|
|
||||||
|
finished_functions.push(trampoline);
|
||||||
|
|
||||||
|
let imports = Imports::new(
|
||||||
|
dependencies,
|
||||||
|
PrimaryMap::new(),
|
||||||
|
PrimaryMap::new(),
|
||||||
|
memories,
|
||||||
|
PrimaryMap::new(),
|
||||||
|
);
|
||||||
|
let data_initializers = Vec::new();
|
||||||
|
let signatures = PrimaryMap::new();
|
||||||
|
|
||||||
|
let trampoline_state = TrampolineState {
|
||||||
|
func: func.clone(),
|
||||||
|
trap: None,
|
||||||
|
code_memory,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(InstanceHandle::new(
|
||||||
|
Rc::new(module),
|
||||||
|
global_exports,
|
||||||
|
finished_functions.into_boxed_slice(),
|
||||||
|
imports,
|
||||||
|
&data_initializers,
|
||||||
|
signatures.into_boxed_slice(),
|
||||||
|
None,
|
||||||
|
Box::new(trampoline_state),
|
||||||
|
)
|
||||||
|
.expect("instance"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We don't expect trampoline compilation to produce any relocations, so
|
||||||
|
/// this `RelocSink` just asserts that it doesn't recieve any.
|
||||||
|
struct RelocSink {}
|
||||||
|
|
||||||
|
impl binemit::RelocSink for RelocSink {
|
||||||
|
fn reloc_ebb(
|
||||||
|
&mut self,
|
||||||
|
_offset: binemit::CodeOffset,
|
||||||
|
_reloc: binemit::Reloc,
|
||||||
|
_ebb_offset: binemit::CodeOffset,
|
||||||
|
) {
|
||||||
|
panic!("trampoline compilation should not produce ebb relocs");
|
||||||
|
}
|
||||||
|
fn reloc_external(
|
||||||
|
&mut self,
|
||||||
|
_offset: binemit::CodeOffset,
|
||||||
|
_reloc: binemit::Reloc,
|
||||||
|
_name: &ir::ExternalName,
|
||||||
|
_addend: binemit::Addend,
|
||||||
|
) {
|
||||||
|
panic!("trampoline compilation should not produce external symbol relocs");
|
||||||
|
}
|
||||||
|
fn reloc_jt(
|
||||||
|
&mut self,
|
||||||
|
_offset: binemit::CodeOffset,
|
||||||
|
_reloc: binemit::Reloc,
|
||||||
|
_jt: ir::JumpTable,
|
||||||
|
) {
|
||||||
|
panic!("trampoline compilation should not produce jump table relocs");
|
||||||
|
}
|
||||||
|
}
|
||||||
19
wasmtime-api/src/trampoline/mod.rs
Normal file
19
wasmtime-api/src/trampoline/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//! Utility module to create trampolines in/out WebAssembly module.
|
||||||
|
|
||||||
|
mod code_memory;
|
||||||
|
mod create_handle;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use self::create_handle::create_handle;
|
||||||
|
use super::externals::Func;
|
||||||
|
|
||||||
|
pub fn generate_func_export(f: &Rc<RefCell<Func>>) -> Result<(), Error> {
|
||||||
|
let mut instance = create_handle(f)?;
|
||||||
|
let export = instance.lookup("trampoline").expect("trampoline export");
|
||||||
|
|
||||||
|
f.borrow_mut().anchor = Some((instance, export));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
19
wasmtime-api/src/trap.rs
Normal file
19
wasmtime-api/src/trap.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#[derive(Fail, Debug)]
|
||||||
|
#[fail(display = "Wasm trap")]
|
||||||
|
pub struct Trap {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trap {
|
||||||
|
pub fn new(message: String) -> Trap {
|
||||||
|
Trap { message }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fake() -> Trap {
|
||||||
|
Trap::new("TODO trap".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> &str {
|
||||||
|
&self.message
|
||||||
|
}
|
||||||
|
}
|
||||||
326
wasmtime-api/src/types.rs
Normal file
326
wasmtime-api/src/types.rs
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
use cranelift_codegen::ir;
|
||||||
|
|
||||||
|
// Type Representations
|
||||||
|
|
||||||
|
// Type attributes
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Mutability {
|
||||||
|
Const,
|
||||||
|
Var,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Limits {
|
||||||
|
min: u32,
|
||||||
|
max: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Limits {
|
||||||
|
pub fn new(min: u32, max: u32) -> Limits {
|
||||||
|
Limits { min, max }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_least(min: u32) -> Limits {
|
||||||
|
Limits {
|
||||||
|
min,
|
||||||
|
max: ::std::u32::MAX,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ValType {
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
|
AnyRef, /* = 128 */
|
||||||
|
FuncRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValType {
|
||||||
|
pub fn is_num(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_ref(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ValType::AnyRef | ValType::FuncRef => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_cranelift_type(&self) -> ir::Type {
|
||||||
|
match self {
|
||||||
|
ValType::I32 => ir::types::I32,
|
||||||
|
ValType::I64 => ir::types::I64,
|
||||||
|
ValType::F32 => ir::types::F32,
|
||||||
|
ValType::F64 => ir::types::F64,
|
||||||
|
_ => unimplemented!("get_cranelift_type other"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_cranelift_type(ty: ir::Type) -> ValType {
|
||||||
|
match ty {
|
||||||
|
ir::types::I32 => ValType::I32,
|
||||||
|
ir::types::I64 => ValType::I64,
|
||||||
|
ir::types::F32 => ValType::F32,
|
||||||
|
ir::types::F64 => ValType::F64,
|
||||||
|
_ => unimplemented!("from_cranelift_type other"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// External Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ExternType {
|
||||||
|
ExternFunc(FuncType),
|
||||||
|
ExternGlobal(GlobalType),
|
||||||
|
ExternTable(TableType),
|
||||||
|
ExternMemory(MemoryType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternType {
|
||||||
|
pub fn func(&self) -> &FuncType {
|
||||||
|
match self {
|
||||||
|
ExternType::ExternFunc(func) => func,
|
||||||
|
_ => panic!("ExternType::ExternFunc expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn global(&self) -> &GlobalType {
|
||||||
|
match self {
|
||||||
|
ExternType::ExternGlobal(func) => func,
|
||||||
|
_ => panic!("ExternType::ExternGlobal expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn table(&self) -> &TableType {
|
||||||
|
match self {
|
||||||
|
ExternType::ExternTable(table) => table,
|
||||||
|
_ => panic!("ExternType::ExternTable expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn memory(&self) -> &MemoryType {
|
||||||
|
match self {
|
||||||
|
ExternType::ExternMemory(memory) => memory,
|
||||||
|
_ => panic!("ExternType::ExternMemory expected"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function Types
|
||||||
|
fn from_cranelift_abiparam(param: &ir::AbiParam) -> ValType {
|
||||||
|
assert!(param.purpose == ir::ArgumentPurpose::Normal);
|
||||||
|
ValType::from_cranelift_type(param.value_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FuncType {
|
||||||
|
params: Box<[ValType]>,
|
||||||
|
results: Box<[ValType]>,
|
||||||
|
signature: ir::Signature,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FuncType {
|
||||||
|
pub fn new(params: Box<[ValType]>, results: Box<[ValType]>) -> FuncType {
|
||||||
|
let signature: ir::Signature = {
|
||||||
|
use cranelift_codegen::ir::*;
|
||||||
|
use cranelift_codegen::isa::CallConv;
|
||||||
|
|
||||||
|
let mut params = params
|
||||||
|
.iter()
|
||||||
|
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let returns = results
|
||||||
|
.iter()
|
||||||
|
.map(|p| AbiParam::new(p.get_cranelift_type()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
params.insert(0, AbiParam::special(types::I64, ArgumentPurpose::VMContext));
|
||||||
|
|
||||||
|
Signature {
|
||||||
|
params,
|
||||||
|
returns,
|
||||||
|
call_conv: CallConv::SystemV,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FuncType {
|
||||||
|
params,
|
||||||
|
results,
|
||||||
|
signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn params(&self) -> &[ValType] {
|
||||||
|
&self.params
|
||||||
|
}
|
||||||
|
pub fn results(&self) -> &[ValType] {
|
||||||
|
&self.results
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_cranelift_signature(&self) -> &ir::Signature {
|
||||||
|
&self.signature
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_cranelift_signature(signature: ir::Signature) -> FuncType {
|
||||||
|
let params = signature
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.filter(|p| p.purpose == ir::ArgumentPurpose::Normal)
|
||||||
|
.map(|p| from_cranelift_abiparam(p))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let results = signature
|
||||||
|
.returns
|
||||||
|
.iter()
|
||||||
|
.map(|p| from_cranelift_abiparam(p))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
FuncType {
|
||||||
|
params: params.into_boxed_slice(),
|
||||||
|
results: results.into_boxed_slice(),
|
||||||
|
signature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GlobalType {
|
||||||
|
content: ValType,
|
||||||
|
mutability: Mutability,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalType {
|
||||||
|
pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
|
||||||
|
GlobalType {
|
||||||
|
content,
|
||||||
|
mutability,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn content(&self) -> &ValType {
|
||||||
|
&self.content
|
||||||
|
}
|
||||||
|
pub fn mutability(&self) -> Mutability {
|
||||||
|
self.mutability
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_cranelift_global(global: cranelift_wasm::Global) -> GlobalType {
|
||||||
|
let ty = ValType::from_cranelift_type(global.ty);
|
||||||
|
let mutability = if global.mutability {
|
||||||
|
Mutability::Var
|
||||||
|
} else {
|
||||||
|
Mutability::Const
|
||||||
|
};
|
||||||
|
GlobalType::new(ty, mutability)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TableType {
|
||||||
|
element: ValType,
|
||||||
|
limits: Limits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableType {
|
||||||
|
pub fn new(element: ValType, limits: Limits) -> TableType {
|
||||||
|
TableType { element, limits }
|
||||||
|
}
|
||||||
|
pub fn element(&self) -> &ValType {
|
||||||
|
&self.element
|
||||||
|
}
|
||||||
|
pub fn limits(&self) -> &Limits {
|
||||||
|
&self.limits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MemoryType {
|
||||||
|
limits: Limits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryType {
|
||||||
|
pub fn new(limits: Limits) -> MemoryType {
|
||||||
|
MemoryType { limits }
|
||||||
|
}
|
||||||
|
pub fn limits(&self) -> &Limits {
|
||||||
|
&self.limits
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_cranelift_memory(memory: cranelift_wasm::Memory) -> MemoryType {
|
||||||
|
MemoryType::new(Limits::new(
|
||||||
|
memory.minimum,
|
||||||
|
memory.maximum.unwrap_or(::std::u32::MAX),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Name(String);
|
||||||
|
|
||||||
|
impl From<String> for Name {
|
||||||
|
fn from(s: String) -> Name {
|
||||||
|
Name(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::string::ToString for Name {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.0.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ImportType {
|
||||||
|
module: Name,
|
||||||
|
name: Name,
|
||||||
|
r#type: ExternType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportType {
|
||||||
|
pub fn new(module: Name, name: Name, r#type: ExternType) -> ImportType {
|
||||||
|
ImportType {
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
r#type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn module(&self) -> &Name {
|
||||||
|
&self.module
|
||||||
|
}
|
||||||
|
pub fn name(&self) -> &Name {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
pub fn r#type(&self) -> &ExternType {
|
||||||
|
&self.r#type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export Types
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExportType {
|
||||||
|
name: Name,
|
||||||
|
r#type: ExternType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExportType {
|
||||||
|
pub fn new(name: Name, r#type: ExternType) -> ExportType {
|
||||||
|
ExportType { name, r#type }
|
||||||
|
}
|
||||||
|
pub fn name(&self) -> &Name {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
pub fn r#type(&self) -> &ExternType {
|
||||||
|
&self.r#type
|
||||||
|
}
|
||||||
|
}
|
||||||
164
wasmtime-api/src/values.rs
Normal file
164
wasmtime-api/src/values.rs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
use crate::callable::Callable;
|
||||||
|
use crate::types::ValType;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
|
use std::ptr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use cranelift_codegen::ir;
|
||||||
|
use wasmtime_jit::RuntimeValue;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyRef;
|
||||||
|
impl AnyRef {
|
||||||
|
pub fn null() -> AnyRef {
|
||||||
|
AnyRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AnyRef {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "anyref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FuncRef {
|
||||||
|
pub callable: Box<dyn Callable + 'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for FuncRef {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "funcref")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Val {
|
||||||
|
I32(i32),
|
||||||
|
I64(i64),
|
||||||
|
F32(u32),
|
||||||
|
F64(u64),
|
||||||
|
AnyRef(Rc<RefCell<AnyRef>>),
|
||||||
|
FuncRef(Rc<RefCell<FuncRef>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Val {
|
||||||
|
pub fn default() -> Val {
|
||||||
|
Val::AnyRef(Rc::new(RefCell::new(AnyRef::null())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> ValType {
|
||||||
|
match self {
|
||||||
|
Val::I32(_) => ValType::I32,
|
||||||
|
Val::I64(_) => ValType::I64,
|
||||||
|
Val::F32(_) => ValType::F32,
|
||||||
|
Val::F64(_) => ValType::F64,
|
||||||
|
Val::AnyRef(_) => ValType::AnyRef,
|
||||||
|
Val::FuncRef(_) => ValType::FuncRef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn write_value_to(&self, p: *mut i64) {
|
||||||
|
match self {
|
||||||
|
Val::I32(i) => ptr::write(p as *mut i32, *i),
|
||||||
|
Val::I64(i) => ptr::write(p as *mut i64, *i),
|
||||||
|
Val::F32(u) => ptr::write(p as *mut u32, *u),
|
||||||
|
Val::F64(u) => ptr::write(p as *mut u64, *u),
|
||||||
|
_ => unimplemented!("Val::write_value_to"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn read_value_from(p: *const i64, ty: ir::Type) -> Val {
|
||||||
|
match ty {
|
||||||
|
ir::types::I32 => Val::I32(ptr::read(p as *const i32)),
|
||||||
|
ir::types::I64 => Val::I64(ptr::read(p as *const i64)),
|
||||||
|
ir::types::F32 => Val::F32(ptr::read(p as *const u32)),
|
||||||
|
ir::types::F64 => Val::F64(ptr::read(p as *const u64)),
|
||||||
|
_ => unimplemented!("Val::read_value_from"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f32_bits(v: u32) -> Val {
|
||||||
|
Val::F32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f64_bits(v: u64) -> Val {
|
||||||
|
Val::F64(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for Val {
|
||||||
|
fn from(val: i32) -> Val {
|
||||||
|
Val::I32(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Val {
|
||||||
|
fn from(val: i64) -> Val {
|
||||||
|
Val::I64(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for Val {
|
||||||
|
fn from(val: f32) -> Val {
|
||||||
|
Val::F32(val.to_bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f64> for Val {
|
||||||
|
fn from(val: f64) -> Val {
|
||||||
|
Val::F64(val.to_bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<i32> for Val {
|
||||||
|
fn into(self) -> i32 {
|
||||||
|
if let Val::I32(i) = self {
|
||||||
|
i
|
||||||
|
} else {
|
||||||
|
panic!("Invalid conversion of {:?} to i32.", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<i64> for Val {
|
||||||
|
fn into(self) -> i64 {
|
||||||
|
if let Val::I64(i) = self {
|
||||||
|
i
|
||||||
|
} else {
|
||||||
|
panic!("Invalid conversion of {:?} to i64.", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<f32> for Val {
|
||||||
|
fn into(self) -> f32 {
|
||||||
|
if let Val::F32(i) = self {
|
||||||
|
RuntimeValue::F32(i).unwrap_f32()
|
||||||
|
} else {
|
||||||
|
panic!("Invalid conversion of {:?} to f32.", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<f64> for Val {
|
||||||
|
fn into(self) -> f64 {
|
||||||
|
if let Val::F64(i) = self {
|
||||||
|
RuntimeValue::F64(i).unwrap_f64()
|
||||||
|
} else {
|
||||||
|
panic!("Invalid conversion of {:?} to f64.", self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rc<RefCell<AnyRef>>> for Val {
|
||||||
|
fn from(val: Rc<RefCell<AnyRef>>) -> Val {
|
||||||
|
Val::AnyRef(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rc<RefCell<FuncRef>>> for Val {
|
||||||
|
fn from(val: Rc<RefCell<FuncRef>>) -> Val {
|
||||||
|
Val::FuncRef(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
728
wasmtime-api/src/wasm.rs
Normal file
728
wasmtime-api/src/wasm.rs
Normal file
@@ -0,0 +1,728 @@
|
|||||||
|
//! This file defines the extern "C" API, which is compatible with the
|
||||||
|
//! [Wasm C API](https://github.com/WebAssembly/wasm-c-api).
|
||||||
|
|
||||||
|
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
|
||||||
|
|
||||||
|
// TODO complete the C API
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
Callable, Engine, ExportType, Extern, Func, FuncType, ImportType, Instance, Module, Store,
|
||||||
|
Trap, Val, ValType,
|
||||||
|
};
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
pub type byte_t = ::std::os::raw::c_char;
|
||||||
|
pub type float32_t = f32;
|
||||||
|
pub type float64_t = f64;
|
||||||
|
pub type wasm_byte_t = byte_t;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_byte_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut wasm_byte_t,
|
||||||
|
}
|
||||||
|
pub type wasm_name_t = wasm_byte_vec_t;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_config_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_engine_t {
|
||||||
|
engine: Rc<RefCell<Engine>>,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_store_t {
|
||||||
|
store: Rc<RefCell<Store>>,
|
||||||
|
}
|
||||||
|
#[doc = ""]
|
||||||
|
pub type wasm_mutability_t = u8;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_limits_t {
|
||||||
|
pub min: u32,
|
||||||
|
pub max: u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_valtype_t {
|
||||||
|
ty: ValType,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_valtype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_valtype_t,
|
||||||
|
}
|
||||||
|
pub type wasm_valkind_t = u8;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_functype_t {
|
||||||
|
functype: FuncType,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_functype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_functype_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_globaltype_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_globaltype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_globaltype_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_tabletype_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_tabletype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_tabletype_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_memorytype_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_memorytype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_memorytype_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_externtype_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_externtype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_externtype_t,
|
||||||
|
}
|
||||||
|
pub type wasm_externkind_t = u8;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_importtype_t {
|
||||||
|
ty: ImportType,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_importtype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_importtype_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_exporttype_t {
|
||||||
|
ty: ExportType,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_exporttype_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_exporttype_t,
|
||||||
|
}
|
||||||
|
#[doc = ""]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_ref_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct wasm_val_t {
|
||||||
|
pub kind: wasm_valkind_t,
|
||||||
|
pub of: wasm_val_t__bindgen_ty_1,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union wasm_val_t__bindgen_ty_1 {
|
||||||
|
pub i32: i32,
|
||||||
|
pub i64: i64,
|
||||||
|
pub f32: float32_t,
|
||||||
|
pub f64: float64_t,
|
||||||
|
pub ref_: *mut wasm_ref_t,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_val_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut wasm_val_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_frame_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_frame_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_frame_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_instance_t {
|
||||||
|
instance: Rc<RefCell<Instance>>,
|
||||||
|
}
|
||||||
|
pub type wasm_message_t = wasm_name_t;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_trap_t {
|
||||||
|
trap: Rc<RefCell<Trap>>,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_foreign_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_module_t {
|
||||||
|
module: Rc<RefCell<Module>>,
|
||||||
|
imports: Vec<wasm_importtype_t>,
|
||||||
|
exports: Vec<wasm_exporttype_t>,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_shared_module_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_func_t {
|
||||||
|
func: Rc<RefCell<Func>>,
|
||||||
|
}
|
||||||
|
pub type wasm_func_callback_t = ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(args: *const wasm_val_t, results: *mut wasm_val_t) -> *mut wasm_trap_t,
|
||||||
|
>;
|
||||||
|
pub type wasm_func_callback_with_env_t = ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
env: *mut ::std::os::raw::c_void,
|
||||||
|
args: *const wasm_val_t,
|
||||||
|
results: *mut wasm_val_t,
|
||||||
|
) -> *mut wasm_trap_t,
|
||||||
|
>;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_global_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_table_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
pub type wasm_table_size_t = u32;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_memory_t {
|
||||||
|
_unused: [u8; 0],
|
||||||
|
}
|
||||||
|
pub type wasm_memory_pages_t = u32;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_extern_t {
|
||||||
|
ext: Rc<RefCell<Extern>>,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct wasm_extern_vec_t {
|
||||||
|
pub size: usize,
|
||||||
|
pub data: *mut *mut wasm_extern_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_byte_vec_delete(v: *mut wasm_byte_vec_t) {
|
||||||
|
let _ = Vec::from_raw_parts((*v).data, 0, (*v).size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_byte_vec_new_uninitialized(out: *mut wasm_byte_vec_t, size: usize) {
|
||||||
|
let mut buffer = vec![0; size];
|
||||||
|
let result = out.as_mut().unwrap();
|
||||||
|
result.size = buffer.capacity();
|
||||||
|
result.data = buffer.as_mut_ptr();
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_engine_delete(engine: *mut wasm_engine_t) {
|
||||||
|
let _ = Box::from_raw(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_engine_new() -> *mut wasm_engine_t {
|
||||||
|
let engine = Box::new(wasm_engine_t {
|
||||||
|
engine: Rc::new(RefCell::new(Engine::default())),
|
||||||
|
});
|
||||||
|
Box::into_raw(engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_extern_as_func(e: *mut wasm_extern_t) -> *mut wasm_func_t {
|
||||||
|
let func = (*e).ext.borrow().func().clone();
|
||||||
|
let func = Box::new(wasm_func_t { func });
|
||||||
|
Box::into_raw(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_extern_vec_delete(v: *mut wasm_extern_vec_t) {
|
||||||
|
let buffer = Vec::from_raw_parts((*v).data, (*v).size, (*v).size);
|
||||||
|
for p in buffer {
|
||||||
|
// TODO wasm_extern_delete
|
||||||
|
let _ = Box::from_raw(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_func_as_extern(f: *mut wasm_func_t) -> *mut wasm_extern_t {
|
||||||
|
let ext = Extern::Func((*f).func.clone());
|
||||||
|
let ext = Box::new(wasm_extern_t {
|
||||||
|
ext: Rc::new(RefCell::new(ext)),
|
||||||
|
});
|
||||||
|
Box::into_raw(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_func_call(
|
||||||
|
func: *const wasm_func_t,
|
||||||
|
args: *const wasm_val_t,
|
||||||
|
results: *mut wasm_val_t,
|
||||||
|
) -> *mut wasm_trap_t {
|
||||||
|
let func = (*func).func.borrow();
|
||||||
|
let mut params = Vec::with_capacity(func.param_arity());
|
||||||
|
for i in 0..func.param_arity() {
|
||||||
|
let val = &(*args.offset(i as isize));
|
||||||
|
params.push(val.val());
|
||||||
|
}
|
||||||
|
match func.call(¶ms) {
|
||||||
|
Ok(out) => {
|
||||||
|
for i in 0..func.result_arity() {
|
||||||
|
let val = &mut (*results.offset(i as isize));
|
||||||
|
*val = wasm_val_t::from_val(&out[i]);
|
||||||
|
}
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
Err(trap) => {
|
||||||
|
let trap = Box::new(wasm_trap_t { trap });
|
||||||
|
Box::into_raw(trap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl wasm_val_t {
|
||||||
|
fn default() -> wasm_val_t {
|
||||||
|
wasm_val_t {
|
||||||
|
kind: 0,
|
||||||
|
of: wasm_val_t__bindgen_ty_1 { i32: 0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_val(val: &Val) -> wasm_val_t {
|
||||||
|
match val {
|
||||||
|
Val::I32(i) => wasm_val_t {
|
||||||
|
kind: from_valtype(ValType::I32),
|
||||||
|
of: wasm_val_t__bindgen_ty_1 { i32: *i },
|
||||||
|
},
|
||||||
|
_ => unimplemented!("wasm_val_t::from_val {:?}", val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn val(&self) -> Val {
|
||||||
|
match into_valtype(self.kind) {
|
||||||
|
ValType::I32 => Val::from(unsafe { self.of.i32 }),
|
||||||
|
_ => unimplemented!("wasm_val_t::val {:?}", self.kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for wasm_func_callback_t {
|
||||||
|
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Rc<RefCell<Trap>>> {
|
||||||
|
let params = params
|
||||||
|
.iter()
|
||||||
|
.map(|p| wasm_val_t::from_val(p))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut out_results = vec![wasm_val_t::default(); results.len()];
|
||||||
|
let func = self.expect("wasm_func_callback_t fn");
|
||||||
|
let out = unsafe { func(params.as_ptr(), out_results.as_mut_ptr()) };
|
||||||
|
if out != ptr::null_mut() {
|
||||||
|
let trap: Box<wasm_trap_t> = unsafe { Box::from_raw(out) };
|
||||||
|
return Err((*trap).into());
|
||||||
|
}
|
||||||
|
for i in 0..results.len() {
|
||||||
|
results[i] = out_results[i].val();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Rc<RefCell<Trap>>> for wasm_trap_t {
|
||||||
|
fn into(self) -> Rc<RefCell<Trap>> {
|
||||||
|
self.trap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CallbackWithEnv {
|
||||||
|
callback: wasm_func_callback_with_env_t,
|
||||||
|
env: *mut ::std::os::raw::c_void,
|
||||||
|
finalizer: ::std::option::Option<unsafe extern "C" fn(env: *mut ::std::os::raw::c_void)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for CallbackWithEnv {
|
||||||
|
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Rc<RefCell<Trap>>> {
|
||||||
|
let params = params
|
||||||
|
.iter()
|
||||||
|
.map(|p| wasm_val_t::from_val(p))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut out_results = vec![wasm_val_t::default(); results.len()];
|
||||||
|
let func = self.callback.expect("wasm_func_callback_with_env_t fn");
|
||||||
|
let out = unsafe { func(self.env, params.as_ptr(), out_results.as_mut_ptr()) };
|
||||||
|
if out != ptr::null_mut() {
|
||||||
|
let trap: Box<wasm_trap_t> = unsafe { Box::from_raw(out) };
|
||||||
|
return Err((*trap).into());
|
||||||
|
}
|
||||||
|
for i in 0..results.len() {
|
||||||
|
results[i] = out_results[i].val();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CallbackWithEnv {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(finalizer) = self.finalizer {
|
||||||
|
unsafe {
|
||||||
|
finalizer(self.env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_func_new(
|
||||||
|
store: *mut wasm_store_t,
|
||||||
|
ty: *const wasm_functype_t,
|
||||||
|
callback: wasm_func_callback_t,
|
||||||
|
) -> *mut wasm_func_t {
|
||||||
|
let store = (*store).store.clone();
|
||||||
|
let ty = (*ty).functype.clone();
|
||||||
|
let callback = Rc::new(callback);
|
||||||
|
let func = Box::new(wasm_func_t {
|
||||||
|
func: Rc::new(RefCell::new(Func::new(store, ty, callback))),
|
||||||
|
});
|
||||||
|
Box::into_raw(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_func_delete(f: *mut wasm_func_t) {
|
||||||
|
let _ = Box::from_raw(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_functype_new(
|
||||||
|
params: *mut wasm_valtype_vec_t,
|
||||||
|
results: *mut wasm_valtype_vec_t,
|
||||||
|
) -> *mut wasm_functype_t {
|
||||||
|
let params = Vec::from_raw_parts((*params).data, (*params).size, (*params).size)
|
||||||
|
.into_iter()
|
||||||
|
.map(|vt| (*vt).ty.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let results = Vec::from_raw_parts((*results).data, (*results).size, (*results).size)
|
||||||
|
.into_iter()
|
||||||
|
.map(|vt| (*vt).ty.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let functype = FuncType::new(params.into_boxed_slice(), results.into_boxed_slice());
|
||||||
|
let functype = Box::new(wasm_functype_t { functype });
|
||||||
|
Box::into_raw(functype)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_functype_delete(ft: *mut wasm_functype_t) {
|
||||||
|
let _ = Box::from_raw(ft);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_instance_delete(instance: *mut wasm_instance_t) {
|
||||||
|
let _ = Box::from_raw(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_instance_new(
|
||||||
|
store: *mut wasm_store_t,
|
||||||
|
module: *const wasm_module_t,
|
||||||
|
imports: *const *const wasm_extern_t,
|
||||||
|
_result: *mut *mut wasm_trap_t,
|
||||||
|
) -> *mut wasm_instance_t {
|
||||||
|
let store = (*store).store.clone();
|
||||||
|
let mut externs: Vec<Rc<RefCell<Extern>>> = Vec::with_capacity((*module).imports.len());
|
||||||
|
for i in 0..(*module).imports.len() {
|
||||||
|
let import = *imports.offset(i as isize);
|
||||||
|
externs.push((*import).ext.clone());
|
||||||
|
}
|
||||||
|
let module = (*module).module.clone();
|
||||||
|
match Instance::new(store, module, &externs) {
|
||||||
|
Ok(instance) => {
|
||||||
|
let instance = Box::new(wasm_instance_t {
|
||||||
|
instance: Rc::new(RefCell::new(instance)),
|
||||||
|
});
|
||||||
|
Box::into_raw(instance)
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_instance_exports(
|
||||||
|
instance: *const wasm_instance_t,
|
||||||
|
out: *mut wasm_extern_vec_t,
|
||||||
|
) {
|
||||||
|
let instance = &(*instance).instance.borrow();
|
||||||
|
let exports = instance.exports();
|
||||||
|
let mut buffer = Vec::with_capacity(exports.len());
|
||||||
|
for e in exports.iter() {
|
||||||
|
let ext = Box::new(wasm_extern_t { ext: e.clone() });
|
||||||
|
buffer.push(Box::into_raw(ext));
|
||||||
|
}
|
||||||
|
(*out).size = buffer.capacity();
|
||||||
|
(*out).data = buffer.as_mut_ptr();
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_module_delete(module: *mut wasm_module_t) {
|
||||||
|
let _ = Box::from_raw(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_module_new(
|
||||||
|
store: *mut wasm_store_t,
|
||||||
|
binary: *const wasm_byte_vec_t,
|
||||||
|
) -> *mut wasm_module_t {
|
||||||
|
let binary = slice::from_raw_parts((*binary).data as *const u8, (*binary).size);
|
||||||
|
let store = (*store).store.clone();
|
||||||
|
let module = Module::new(store, binary).expect("module");
|
||||||
|
let imports = module
|
||||||
|
.imports()
|
||||||
|
.iter()
|
||||||
|
.map(|i| wasm_importtype_t { ty: i.clone() })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let exports = module
|
||||||
|
.exports()
|
||||||
|
.iter()
|
||||||
|
.map(|e| wasm_exporttype_t { ty: e.clone() })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let module = Box::new(wasm_module_t {
|
||||||
|
module: Rc::new(RefCell::new(module)),
|
||||||
|
imports,
|
||||||
|
exports,
|
||||||
|
});
|
||||||
|
Box::into_raw(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_store_delete(store: *mut wasm_store_t) {
|
||||||
|
let _ = Box::from_raw(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_store_new(engine: *mut wasm_engine_t) -> *mut wasm_store_t {
|
||||||
|
let engine = (*engine).engine.clone();
|
||||||
|
let store = Box::new(wasm_store_t {
|
||||||
|
store: Rc::new(RefCell::new(Store::new(engine))),
|
||||||
|
});
|
||||||
|
Box::into_raw(store)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_valtype_vec_new_empty(out: *mut wasm_valtype_vec_t) {
|
||||||
|
(*out).data = ptr::null_mut();
|
||||||
|
(*out).size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_func_new_with_env(
|
||||||
|
store: *mut wasm_store_t,
|
||||||
|
ty: *const wasm_functype_t,
|
||||||
|
callback: wasm_func_callback_with_env_t,
|
||||||
|
env: *mut ::std::os::raw::c_void,
|
||||||
|
finalizer: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
|
||||||
|
) -> *mut wasm_func_t {
|
||||||
|
let store = (*store).store.clone();
|
||||||
|
let ty = (*ty).functype.clone();
|
||||||
|
let callback = Rc::new(CallbackWithEnv {
|
||||||
|
callback,
|
||||||
|
env,
|
||||||
|
finalizer,
|
||||||
|
});
|
||||||
|
let func = Box::new(wasm_func_t {
|
||||||
|
func: Rc::new(RefCell::new(Func::new(store, ty, callback))),
|
||||||
|
});
|
||||||
|
Box::into_raw(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_val_copy(out: *mut wasm_val_t, source: *const wasm_val_t) {
|
||||||
|
*out = match into_valtype((*source).kind) {
|
||||||
|
ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 => (*source).clone(),
|
||||||
|
_ => unimplemented!("wasm_val_copy arg"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_valtype(kind: wasm_valkind_t) -> ValType {
|
||||||
|
match kind {
|
||||||
|
0 => ValType::I32,
|
||||||
|
1 => ValType::I64,
|
||||||
|
2 => ValType::F32,
|
||||||
|
3 => ValType::F64,
|
||||||
|
128 => ValType::AnyRef,
|
||||||
|
129 => ValType::FuncRef,
|
||||||
|
_ => panic!("unexpected kind: {}", kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_valtype(ty: ValType) -> wasm_valkind_t {
|
||||||
|
match ty {
|
||||||
|
ValType::I32 => 0,
|
||||||
|
ValType::I64 => 1,
|
||||||
|
ValType::F32 => 2,
|
||||||
|
ValType::F64 => 3,
|
||||||
|
ValType::AnyRef => 128,
|
||||||
|
ValType::FuncRef => 129,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_valtype_new(kind: wasm_valkind_t) -> *mut wasm_valtype_t {
|
||||||
|
let ty = Box::new(wasm_valtype_t {
|
||||||
|
ty: into_valtype(kind),
|
||||||
|
});
|
||||||
|
Box::into_raw(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_valtype_vec_new(
|
||||||
|
out: *mut wasm_valtype_vec_t,
|
||||||
|
size: usize,
|
||||||
|
data: *const *mut wasm_valtype_t,
|
||||||
|
) {
|
||||||
|
let slice = slice::from_raw_parts(data, size);
|
||||||
|
let mut buffer = Vec::with_capacity(size);
|
||||||
|
buffer.extend_from_slice(slice);
|
||||||
|
assert!(size == buffer.capacity());
|
||||||
|
(*out).size = size;
|
||||||
|
(*out).data = buffer.as_mut_ptr();
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_byte_vec_new(
|
||||||
|
out: *mut wasm_byte_vec_t,
|
||||||
|
size: usize,
|
||||||
|
data: *const wasm_byte_t,
|
||||||
|
) {
|
||||||
|
let slice = slice::from_raw_parts(data, size);
|
||||||
|
let mut buffer = Vec::with_capacity(size);
|
||||||
|
buffer.extend_from_slice(slice);
|
||||||
|
assert!(size == buffer.capacity());
|
||||||
|
(*out).size = size;
|
||||||
|
(*out).data = buffer.as_mut_ptr();
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_delete(_arg1: *mut wasm_frame_t) {
|
||||||
|
unimplemented!("wasm_frame_delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_func_index(_arg1: *const wasm_frame_t) -> u32 {
|
||||||
|
unimplemented!("wasm_frame_func_index")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_func_offset(_arg1: *const wasm_frame_t) -> usize {
|
||||||
|
unimplemented!("wasm_frame_func_offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_instance_t {
|
||||||
|
unimplemented!("wasm_frame_instance")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_module_offset(_arg1: *const wasm_frame_t) -> usize {
|
||||||
|
unimplemented!("wasm_frame_module_offset")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_frame_vec_delete(frames: *mut wasm_frame_vec_t) {
|
||||||
|
let frames = Vec::from_raw_parts((*frames).data, (*frames).size, (*frames).size);
|
||||||
|
for _frame in frames {
|
||||||
|
unimplemented!("wasm_frame_vec_delete for frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_trap_delete(trap: *mut wasm_trap_t) {
|
||||||
|
let _ = Box::from_raw(trap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_trap_new(
|
||||||
|
_store: *mut wasm_store_t,
|
||||||
|
message: *const wasm_message_t,
|
||||||
|
) -> *mut wasm_trap_t {
|
||||||
|
let message = slice::from_raw_parts((*message).data as *const u8, (*message).size);
|
||||||
|
if message[message.len() - 1] != 0 {
|
||||||
|
panic!("wasm_trap_new message stringz expected");
|
||||||
|
}
|
||||||
|
let message = String::from_utf8_lossy(message).to_string();
|
||||||
|
let trap = Box::new(wasm_trap_t {
|
||||||
|
trap: Rc::new(RefCell::new(Trap::new(message))),
|
||||||
|
});
|
||||||
|
Box::into_raw(trap)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_trap_message(trap: *const wasm_trap_t, out: *mut wasm_message_t) {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
buffer.extend_from_slice((*trap).trap.borrow().message().as_bytes());
|
||||||
|
buffer.reserve_exact(1);
|
||||||
|
buffer.push(0);
|
||||||
|
assert!(buffer.len() == buffer.capacity());
|
||||||
|
(*out).size = buffer.capacity();
|
||||||
|
(*out).data = buffer.as_mut_ptr() as *mut i8;
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_trap_origin(_trap: *const wasm_trap_t) -> *mut wasm_frame_t {
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasm_trap_trace(_trap: *const wasm_trap_t, out: *mut wasm_frame_vec_t) {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
(*out).size = 0;
|
||||||
|
(*out).data = buffer.as_mut_ptr();
|
||||||
|
mem::forget(buffer);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user