diff --git a/.gitmodules b/.gitmodules index 841205cbc97a..3a079448b18d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ path = lightbeam url = https://github.com/CraneStation/lightbeam.git 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 diff --git a/Cargo.toml b/Cargo.toml index fa7c07ed75a4..ce1d36219cbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ cranelift-codegen = { version = "0.40.0", features = ["enable-serde"] } cranelift-native = "0.40.0" cranelift-entity = { 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-environ = { path = "wasmtime-environ" } wasmtime-interface-types = { path = "wasmtime-interface-types" } diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 5eee7208d83a..d0b1dabc2c04 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -32,21 +32,24 @@ use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; -use cranelift_native; use docopt::Docopt; -use failure::{bail, format_err, Error, ResultExt}; +use failure::{bail, Error, ResultExt}; use pretty_env_logger; use serde::Deserialize; +use std::cell::RefCell; +use std::collections::HashMap; use std::ffi::OsStr; use std::fs::File; use std::path::Component; use std::path::{Path, PathBuf}; use std::process::exit; +use std::rc::Rc; use wabt; use wasi_common::preopen_dir; +use wasmtime_api::{Config, Engine, Instance, Module, Store}; use wasmtime_environ::cache_conf; use wasmtime_interface_types::ModuleData; -use wasmtime_jit::{Context, Features, InstanceHandle}; +use wasmtime_jit::Features; use wasmtime_wasi::instantiate_wasi; use wasmtime_wast::instantiate_spectest; @@ -219,11 +222,12 @@ fn rmain() -> Result<(), Error> { 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 features: Features = Default::default(); + // Enable/disable producing of debug info. + let debug_info = args.flag_g; + // Enable verifier passes in debug mode. if cfg!(debug_assertions) { flag_builder.enable("enable_verifier")?; @@ -240,14 +244,20 @@ fn rmain() -> Result<(), Error> { flag_builder.set("opt_level", "best")?; } - let isa = isa_builder.finish(settings::Flags::new(flag_builder)); - let mut context = Context::with_isa(isa).with_features(features); + let config = Config::new(settings::Flags::new(flag_builder), features, debug_info); + 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. - 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. - 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 argv = compute_argv(&args.arg_file, &args.arg_arg); let environ = compute_environ(&args.flag_env); @@ -255,62 +265,98 @@ fn rmain() -> Result<(), Error> { let wasi = if args.flag_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"))] { bail!("wasi-c feature not enabled at build time") } } 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); - - // Enable/disable producing of debug info. - context.set_debug_info(args.flag_g); + module_registry.insert( + "wasi_unstable".to_owned(), + Instance::from_handle(store.clone(), wasi)?, + ); // Load the preload wasm modules. for filename in &args.flag_preload { 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()))?; } // Load the main wasm module. 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()))?; Ok(()) } fn instantiate_module( - context: &mut Context, + store: Rc>, + module_registry: &HashMap)>, path: &Path, -) -> Result<(InstanceHandle, Vec), Error> { +) -> Result<(Rc>, Rc>, Vec), Error> { // Read the wasm module binary. let data = read_wasm(path.to_path_buf())?; - // Compile and instantiating a wasm module. - let handle = context.instantiate_module(None, &data)?; - Ok((handle, data)) + let module = Rc::new(RefCell::new(Module::new(store.clone(), &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::, _>>()?; + + 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> { - let (mut instance, data) = instantiate_module(context, path)?; +fn handle_module( + store: Rc>, + module_registry: &HashMap)>, + 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 let Some(f) = &args.flag_invoke { let data = ModuleData::new(&data)?; - invoke_export(context, &mut instance, &data, f, args)?; + invoke_export(store, instance, &data, f, args)?; } Ok(()) } fn invoke_export( - context: &mut Context, - instance: &mut InstanceHandle, + store: Rc>, + instance: Rc>, data: &ModuleData, name: &str, args: &Args, @@ -318,11 +364,13 @@ fn invoke_export( use wasm_webidl_bindings::ast; use wasmtime_interface_types::Value; + let mut handle = instance.borrow().handle().clone(); + // 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 // the CLI parameters and attempt to parse them into function arguments for // 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 { eprintln!( "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 // out, if there are any. + let mut context = store.borrow().engine().borrow().create_wasmtime_context(); let results = data - .invoke(context, instance, name, &values) + .invoke(&mut context, &mut handle, name, &values) .with_context(|_| format!("failed to invoke `{}`", name))?; if results.len() > 0 { eprintln!( diff --git a/wasmtime-api/Cargo.toml b/wasmtime-api/Cargo.toml new file mode 100644 index 000000000000..48d443e19681 --- /dev/null +++ b/wasmtime-api/Cargo.toml @@ -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" diff --git a/wasmtime-api/README.md b/wasmtime-api/README.md new file mode 100644 index 000000000000..b0edded9c2ba --- /dev/null +++ b/wasmtime-api/README.md @@ -0,0 +1,3 @@ +# Implementation of wasm-c-api in Rust + +https://github.com/WebAssembly/wasm-c-api diff --git a/wasmtime-api/c-examples/Makefile b/wasmtime-api/c-examples/Makefile new file mode 100644 index 000000000000..9fa9351753a8 --- /dev/null +++ b/wasmtime-api/c-examples/Makefile @@ -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} + diff --git a/wasmtime-api/c-examples/wasm-c-api b/wasmtime-api/c-examples/wasm-c-api new file mode 160000 index 000000000000..6db391ee7121 --- /dev/null +++ b/wasmtime-api/c-examples/wasm-c-api @@ -0,0 +1 @@ +Subproject commit 6db391ee7121a0695602945d11001ea3e00b0afb diff --git a/wasmtime-api/examples/gcd.rs b/wasmtime-api/examples/gcd.rs new file mode 100644 index 000000000000..36baf7f3fcd6 --- /dev/null +++ b/wasmtime-api/examples/gcd.rs @@ -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(()) +} diff --git a/wasmtime-api/examples/gcd.wasm b/wasmtime-api/examples/gcd.wasm new file mode 100755 index 000000000000..35a13435f9c4 Binary files /dev/null and b/wasmtime-api/examples/gcd.wasm differ diff --git a/wasmtime-api/src/callable.rs b/wasmtime-api/src/callable.rs new file mode 100644 index 000000000000..9da8735ec9af --- /dev/null +++ b/wasmtime-api/src/callable.rs @@ -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>>; +} + +pub(crate) struct WasmtimeFn { + store: Rc>, + signature: ir::Signature, + body: *const VMFunctionBody, + vmctx: *mut VMContext, +} + +impl WasmtimeFn { + pub fn new( + store: Rc>, + 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>> { + 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::(); + let mut values_vec: Vec = 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(()) + } +} diff --git a/wasmtime-api/src/context.rs b/wasmtime-api/src/context.rs new file mode 100644 index 000000000000..76085dd86852 --- /dev/null +++ b/wasmtime-api/src/context.rs @@ -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>, + 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 { + self.compiler.borrow_mut() + } +} + +impl Hash for Context { + fn hash(&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) +} diff --git a/wasmtime-api/src/externals.rs b/wasmtime-api/src/externals.rs new file mode 100644 index 000000000000..09cf3ec752f4 --- /dev/null +++ b/wasmtime-api/src/externals.rs @@ -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>), + Global(Rc>), + Table(Rc>), + Memory(Rc>), +} + +impl Extern { + pub fn func(&self) -> &Rc> { + match self { + Extern::Func(func) => func, + _ => panic!("Extern::Func expected"), + } + } + pub fn global(&self) -> &Rc> { + match self { + Extern::Global(global) => global, + _ => panic!("Extern::Global expected"), + } + } + pub fn table(&self) -> &Rc> { + match self { + Extern::Table(table) => table, + _ => panic!("Extern::Table expected"), + } + } + pub fn memory(&self) -> &Rc> { + 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>, + 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>, + callable: Rc, + r#type: FuncType, + pub(crate) anchor: Option<(InstanceHandle, wasmtime_runtime::Export)>, +} + +impl Func { + pub fn new( + store: Rc>, + r#type: FuncType, + callable: Rc, + ) -> 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, Rc>> { + 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>, + r#type: GlobalType, + val: Val, +} + +impl Global { + pub fn new(store: Rc>, 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>, + r#type: MemoryType, +} + +impl Memory { + pub fn new(store: Rc>, 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") + } +} diff --git a/wasmtime-api/src/instance.rs b/wasmtime-api/src/instance.rs new file mode 100644 index 000000000000..6c93f934b5fe --- /dev/null +++ b/wasmtime-api/src/instance.rs @@ -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>)>, +} + +impl Resolver for SimpleResolver { + fn resolve(&mut self, name: &str, field: &str) -> Option { + // 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>)>, + mut context: Context, + exports: Rc>>>, +) -> Result<(InstanceHandle, HashSet), 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, + + exports: Box<[Rc>]>, +} + +impl Instance { + pub fn new( + store: Rc>, + module: Rc>, + externs: &[Rc>], + ) -> Result { + 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::>(); + 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>] { + &self.exports + } + + pub fn from_handle( + store: Rc>, + instance_handle: InstanceHandle, + ) -> Result<(Instance, HashMap), 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 { + let mut instance_handle = self.instance_handle.clone(); + instance_handle.lookup("memory") + } +} diff --git a/wasmtime-api/src/lib.rs b/wasmtime-api/src/lib.rs new file mode 100644 index 000000000000..5427a4808266 --- /dev/null +++ b/wasmtime-api/src/lib.rs @@ -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::*; diff --git a/wasmtime-api/src/module.rs b/wasmtime-api/src/module.rs new file mode 100644 index 000000000000..f8a64e3a18d9 --- /dev/null +++ b/wasmtime-api/src/module.rs @@ -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::>(); + let returns = mt.returns.iter().map(into_valtype).collect::>(); + 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>, + binary: Box<[u8]>, + imports: Box<[ImportType]>, + exports: Box<[ExportType]>, +} + +impl Module { + pub fn new(store: Rc>, binary: &[u8]) -> Result { + 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 + } +} diff --git a/wasmtime-api/src/runtime.rs b/wasmtime-api/src/runtime.rs new file mode 100644 index 000000000000..6eb812e21384 --- /dev/null +++ b/wasmtime-api/src/runtime.rs @@ -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>, + context: Context, + global_exports: Rc>>>, +} + +impl Store { + pub fn new(engine: Rc>) -> 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> { + &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>>> { + &self.global_exports + } +} diff --git a/wasmtime-api/src/trampoline/code_memory.rs b/wasmtime-api/src/trampoline/code_memory.rs new file mode 100644 index 000000000000..b117f522897e --- /dev/null +++ b/wasmtime-api/src/trampoline/code_memory.rs @@ -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, + 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(); + } +} diff --git a/wasmtime-api/src/trampoline/create_handle.rs b/wasmtime-api/src/trampoline/create_handle.rs new file mode 100644 index 000000000000..d6462aa813b3 --- /dev/null +++ b/wasmtime-api/src/trampoline/create_handle.rs @@ -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>, + trap: Option>>, + #[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::() + .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::() + .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 = 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>) -> Result { + 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>>> = + Rc::new(RefCell::new(HashMap::new())); + let mut fn_builder_ctx = FunctionBuilderContext::new(); + let mut module = Module::new(); + let mut finished_functions: PrimaryMap = + 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"); + } +} diff --git a/wasmtime-api/src/trampoline/mod.rs b/wasmtime-api/src/trampoline/mod.rs new file mode 100644 index 000000000000..37d080194ad9 --- /dev/null +++ b/wasmtime-api/src/trampoline/mod.rs @@ -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>) -> Result<(), Error> { + let mut instance = create_handle(f)?; + let export = instance.lookup("trampoline").expect("trampoline export"); + + f.borrow_mut().anchor = Some((instance, export)); + Ok(()) +} diff --git a/wasmtime-api/src/trap.rs b/wasmtime-api/src/trap.rs new file mode 100644 index 000000000000..b4ebf4efe412 --- /dev/null +++ b/wasmtime-api/src/trap.rs @@ -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 + } +} diff --git a/wasmtime-api/src/types.rs b/wasmtime-api/src/types.rs new file mode 100644 index 000000000000..692ed99e1d76 --- /dev/null +++ b/wasmtime-api/src/types.rs @@ -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::>(); + let returns = results + .iter() + .map(|p| AbiParam::new(p.get_cranelift_type())) + .collect::>(); + 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::>(); + let results = signature + .returns + .iter() + .map(|p| from_cranelift_abiparam(p)) + .collect::>(); + 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 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 + } +} diff --git a/wasmtime-api/src/values.rs b/wasmtime-api/src/values.rs new file mode 100644 index 000000000000..878f3a00b244 --- /dev/null +++ b/wasmtime-api/src/values.rs @@ -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, +} + +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>), + FuncRef(Rc>), +} + +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 for Val { + fn from(val: i32) -> Val { + Val::I32(val) + } +} + +impl From for Val { + fn from(val: i64) -> Val { + Val::I64(val) + } +} + +impl From for Val { + fn from(val: f32) -> Val { + Val::F32(val.to_bits()) + } +} + +impl From for Val { + fn from(val: f64) -> Val { + Val::F64(val.to_bits()) + } +} + +impl Into for Val { + fn into(self) -> i32 { + if let Val::I32(i) = self { + i + } else { + panic!("Invalid conversion of {:?} to i32.", self); + } + } +} + +impl Into for Val { + fn into(self) -> i64 { + if let Val::I64(i) = self { + i + } else { + panic!("Invalid conversion of {:?} to i64.", self); + } + } +} + +impl Into 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 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>> for Val { + fn from(val: Rc>) -> Val { + Val::AnyRef(val) + } +} + +impl From>> for Val { + fn from(val: Rc>) -> Val { + Val::FuncRef(val) + } +} diff --git a/wasmtime-api/src/wasm.rs b/wasmtime-api/src/wasm.rs new file mode 100644 index 000000000000..bd68a05cc5d2 --- /dev/null +++ b/wasmtime-api/src/wasm.rs @@ -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>, +} +#[repr(C)] +#[derive(Clone)] +pub struct wasm_store_t { + store: Rc>, +} +#[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>, +} +pub type wasm_message_t = wasm_name_t; +#[repr(C)] +#[derive(Clone)] +pub struct wasm_trap_t { + trap: Rc>, +} +#[repr(C)] +#[derive(Clone)] +pub struct wasm_foreign_t { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Clone)] +pub struct wasm_module_t { + module: Rc>, + imports: Vec, + exports: Vec, +} +#[repr(C)] +#[derive(Clone)] +pub struct wasm_shared_module_t { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Clone)] +pub struct wasm_func_t { + func: Rc>, +} +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>, +} +#[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>> { + let params = params + .iter() + .map(|p| wasm_val_t::from_val(p)) + .collect::>(); + 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 = unsafe { Box::from_raw(out) }; + return Err((*trap).into()); + } + for i in 0..results.len() { + results[i] = out_results[i].val(); + } + Ok(()) + } +} + +impl Into>> for wasm_trap_t { + fn into(self) -> Rc> { + self.trap + } +} + +struct CallbackWithEnv { + callback: wasm_func_callback_with_env_t, + env: *mut ::std::os::raw::c_void, + finalizer: ::std::option::Option, +} + +impl Callable for CallbackWithEnv { + fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Rc>> { + let params = params + .iter() + .map(|p| wasm_val_t::from_val(p)) + .collect::>(); + 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 = 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::>(); + let results = Vec::from_raw_parts((*results).data, (*results).size, (*results).size) + .into_iter() + .map(|vt| (*vt).ty.clone()) + .collect::>(); + 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>> = 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::>(); + let exports = module + .exports() + .iter() + .map(|e| wasm_exporttype_t { ty: e.clone() }) + .collect::>(); + 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, +) -> *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); +}