Better Wasmtime API for embedder (#287)

* Migrate wasm-rust-api code to wasmtime.
This commit is contained in:
Yury Delendik
2019-08-21 02:27:42 -05:00
committed by Dan Gohman
parent 8ea883a603
commit f88e92a57c
23 changed files with 2797 additions and 29 deletions

3
.gitmodules vendored
View File

@@ -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

View File

@@ -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" }

View File

@@ -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
View 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
View File

@@ -0,0 +1,3 @@
# Implementation of wasm-c-api in Rust
https://github.com/WebAssembly/wasm-c-api

View 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}

View 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

Binary file not shown.

View 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(())
}
}

View 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)
}

View 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")
}
}

View 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
View 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
View 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(&gt.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(&gt);
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
View 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
}
}

View 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();
}
}

View 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");
}
}

View 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
View 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
View 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
View 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
View 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(&params) {
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);
}