WIP
This commit is contained in:
parent
7740f6b690
commit
23b10d386c
|
@ -24,8 +24,12 @@ byteorder = { version = "1.0", default-features = false }
|
||||||
hashmap_core = { version = "0.1.9", optional = true }
|
hashmap_core = { version = "0.1.9", optional = true }
|
||||||
memory_units = "0.3.0"
|
memory_units = "0.3.0"
|
||||||
libm = { version = "0.1.2", optional = true }
|
libm = { version = "0.1.2", optional = true }
|
||||||
|
wasmi-derive = { version = "0.1", path = "derive" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.1"
|
assert_matches = "1.1"
|
||||||
rand = "0.4.2"
|
rand = "0.4.2"
|
||||||
wabt = "0.6"
|
wabt = "0.6"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["derive"]
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "wasmi-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sergey Pepyakin <sergei@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.6"
|
||||||
|
syn = { version = "0.15.0", features = ['full'] }
|
||||||
|
proc-macro2 = "0.4.9"
|
|
@ -0,0 +1,124 @@
|
||||||
|
use crate::model::{ExtDefinition, ExternalFunc};
|
||||||
|
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||||
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn codegen(ext_def: &ExtDefinition, to: &mut TokenStream) {
|
||||||
|
let mut externals = TokenStream::new();
|
||||||
|
let mut module_resolver = TokenStream::new();
|
||||||
|
|
||||||
|
// TODO: Come up with a name.
|
||||||
|
let mut new_name = "_WASMI_IMPLS_".to_string();
|
||||||
|
new_name.push_str("NAME".to_string().trim_start_matches("r#"));
|
||||||
|
let dummy_const = Ident::new(&new_name, Span::call_site());
|
||||||
|
|
||||||
|
derive_externals(ext_def, &mut externals);
|
||||||
|
derive_module_resolver(ext_def, &mut module_resolver);
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
const #dummy_const: () = {
|
||||||
|
extern crate wasmi as _wasmi;
|
||||||
|
|
||||||
|
use _wasmi::{
|
||||||
|
Trap, RuntimeValue, RuntimeArgs, Externals,
|
||||||
|
derive_support::WasmResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
#externals
|
||||||
|
#module_resolver
|
||||||
|
};
|
||||||
|
}).to_tokens(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_dispatch_func_arm(func: &ExternalFunc) -> TokenStream {
|
||||||
|
let index = func.index as usize;
|
||||||
|
let name = Ident::new(&func.name, Span::call_site());
|
||||||
|
let return_ty_span = func.return_ty.clone().unwrap_or_else(|| Span::call_site());
|
||||||
|
|
||||||
|
let mut args = vec![];
|
||||||
|
let mut unmarshall_args = TokenStream::new();
|
||||||
|
for (i, arg_span) in func.args.iter().cloned().enumerate() {
|
||||||
|
let mut arg_name = "arg".to_string();
|
||||||
|
arg_name.push_str(&i.to_string());
|
||||||
|
let arg_name = Ident::new(&arg_name, arg_span.clone());
|
||||||
|
|
||||||
|
(quote_spanned! {arg_span=>
|
||||||
|
let #arg_name =
|
||||||
|
args.next()
|
||||||
|
.and_then(|rt_val| rt_val.try_into())
|
||||||
|
.unwrap();
|
||||||
|
}).to_tokens(&mut unmarshall_args);
|
||||||
|
|
||||||
|
args.push(quote_spanned! {arg_span=> #arg_name });
|
||||||
|
}
|
||||||
|
|
||||||
|
let prologue = quote! {
|
||||||
|
let mut args = args.as_ref().iter();
|
||||||
|
#unmarshall_args
|
||||||
|
};
|
||||||
|
let epilogue = quote_spanned! {return_ty_span=>
|
||||||
|
WasmResult::to_wasm_result(r)
|
||||||
|
};
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
#index => {
|
||||||
|
#prologue
|
||||||
|
let r = self.#name( #(#args),* );
|
||||||
|
#epilogue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// let body = $crate::wasm_utils::constrain_closure::<
|
||||||
|
// <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _
|
||||||
|
// >(|| {
|
||||||
|
// unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*)
|
||||||
|
// });
|
||||||
|
// let r = body()?;
|
||||||
|
// return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_externals(ext_def: &ExtDefinition, to: &mut TokenStream) {
|
||||||
|
let (impl_generics, ty_generics, where_clause) = ext_def.generics.split_for_impl();
|
||||||
|
let ty = &ext_def.ty;
|
||||||
|
|
||||||
|
let mut match_arms = vec![];
|
||||||
|
for func in &ext_def.funcs {
|
||||||
|
match_arms.push(gen_dispatch_func_arm(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
(quote::quote! {
|
||||||
|
impl #impl_generics Externals for #ty #where_clause {
|
||||||
|
fn invoke_index(
|
||||||
|
&mut self,
|
||||||
|
index: usize,
|
||||||
|
args: RuntimeArgs,
|
||||||
|
) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
match index {
|
||||||
|
#(#match_arms),*
|
||||||
|
_ => panic!("fn with index {} is undefined", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}).to_tokens(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_module_resolver(ext_def: &ExtDefinition, to: &mut TokenStream) {
|
||||||
|
(quote::quote! {
|
||||||
|
impl #impl_generics ModuleImportResolver for #ty #where_clause {
|
||||||
|
fn invoke_index(
|
||||||
|
&mut self,
|
||||||
|
index: usize,
|
||||||
|
args: RuntimeArgs,
|
||||||
|
) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
match index {
|
||||||
|
#(#match_arms),*
|
||||||
|
_ => panic!("fn with index {} is undefined", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}).to_tokens(to);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod model;
|
||||||
|
mod parser;
|
||||||
|
mod codegen;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn derive_externals(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
let mut input: proc_macro2::TokenStream = input.into();
|
||||||
|
|
||||||
|
let ext_def = parser::parse(input.clone()).unwrap();
|
||||||
|
codegen::codegen(&ext_def, &mut input);
|
||||||
|
|
||||||
|
// We need to generate two types:
|
||||||
|
// - Externals
|
||||||
|
// - ModuleImportResolver
|
||||||
|
|
||||||
|
// - for each of declared method collect it's name and it's signature.
|
||||||
|
// - assign a method index for each method
|
||||||
|
// - generate a switch for `Externals` that takes the input `index` and jumps
|
||||||
|
// on the corresponding match arm, which the wrapper.
|
||||||
|
// The wrapper decodes arguments, calls to the function and handles the result.
|
||||||
|
// - generate a switch / ifs chain for `ModuleImportResolver`. In each arm it checks if the function
|
||||||
|
// has an appropriate arguments, and if so allocates a host function with the corresponding index.
|
||||||
|
//
|
||||||
|
// and we will then need to return both the original implementation and the generated implementation
|
||||||
|
// of externals.
|
||||||
|
|
||||||
|
// println!("{:?}", quote::quote! { #input }.to_string());
|
||||||
|
let input = input.into();
|
||||||
|
input
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
pub enum ValueType {
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Signature {
|
||||||
|
pub params: Vec<ValueType>,
|
||||||
|
pub return_ty: Option<ValueType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Param {
|
||||||
|
span: proc_macro2::Span,
|
||||||
|
generated_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExternalFunc {
|
||||||
|
/// Assigned index of this function.
|
||||||
|
pub index: u32,
|
||||||
|
pub name: String,
|
||||||
|
// TODO: Rename args to params
|
||||||
|
pub args: Vec<Param>,
|
||||||
|
pub return_ty: Option<proc_macro2::Span>,
|
||||||
|
// TODO: remove
|
||||||
|
pub arity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The core structure that contains the list of all functions
|
||||||
|
/// and the data required for implementing a trait.
|
||||||
|
pub struct ExtDefinition {
|
||||||
|
/// List of all external functions.
|
||||||
|
pub funcs: Vec<ExternalFunc>,
|
||||||
|
/// The generics required to implement a trait for this type.
|
||||||
|
pub generics: syn::Generics,
|
||||||
|
/// The type declaration to implement to implement a trait, most typically
|
||||||
|
/// represented by a structure.
|
||||||
|
pub ty: Box<syn::Type>,
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::model::{self, ExtDefinition, ExternalFunc};
|
||||||
|
use syn::{ItemImpl, ImplItem, ImplItemMethod, FnArg, ReturnType};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
/// Parse an incoming stream of tokens into a list of external functions.
|
||||||
|
pub fn parse(input: proc_macro2::TokenStream) -> Result<ExtDefinition, ()> {
|
||||||
|
let item_impl = syn::parse2::<syn::ItemImpl>(input).map_err(|_| ())?;
|
||||||
|
|
||||||
|
let mut funcs = vec![];
|
||||||
|
|
||||||
|
for item in item_impl.items {
|
||||||
|
match item {
|
||||||
|
ImplItem::Method(ImplItemMethod {
|
||||||
|
sig,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let index = funcs.len() as u32;
|
||||||
|
|
||||||
|
// self TODO: handle this properly
|
||||||
|
let args = sig.decl.inputs.iter().skip(1).enumerate().map(|input| {
|
||||||
|
input.span()
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let return_ty = match sig.decl.output {
|
||||||
|
ReturnType::Default => None,
|
||||||
|
ReturnType::Type(_, ty) => Some(ty.span()),
|
||||||
|
};
|
||||||
|
|
||||||
|
funcs.push(ExternalFunc {
|
||||||
|
index,
|
||||||
|
name: sig.ident.to_string(),
|
||||||
|
args,
|
||||||
|
return_ty,
|
||||||
|
arity: sig.decl.inputs.len() - 1, // self TODO: handle this properly
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExtDefinition {
|
||||||
|
funcs,
|
||||||
|
generics: item_impl.generics.clone(),
|
||||||
|
ty: item_impl.self_ty.clone(),
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
use {ValueType, RuntimeValue, Trap};
|
||||||
|
|
||||||
|
pub trait ConvertibleToWasm {
|
||||||
|
const VALUE_TYPE: ValueType;
|
||||||
|
type NativeType;
|
||||||
|
fn to_runtime_value(self) -> RuntimeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } }
|
||||||
|
impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } }
|
||||||
|
impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self) } }
|
||||||
|
impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self as i64) } }
|
||||||
|
impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } }
|
||||||
|
impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as u32 as i32) } }
|
||||||
|
impl<T> ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } }
|
||||||
|
impl<T> ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } }
|
||||||
|
|
||||||
|
pub trait WasmResult {
|
||||||
|
fn to_wasm_result(self) -> Result<Option<RuntimeValue>, Trap>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmResult for () {
|
||||||
|
fn to_wasm_result(self) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ConvertibleToWasm, E: Into<Trap>> WasmResult for Result<R, E> {
|
||||||
|
fn to_wasm_result(self) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
self
|
||||||
|
.map(|v| Some(v.to_runtime_value()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Into<Trap>> WasmResult for Result<(), E> {
|
||||||
|
fn to_wasm_result(self) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
self
|
||||||
|
.map(|_| None)
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ConvertibleToWasm> WasmResult for R {
|
||||||
|
fn to_wasm_result(self) -> Result<Option<RuntimeValue>, Trap> {
|
||||||
|
Ok(Some(self.to_runtime_value()))
|
||||||
|
}
|
||||||
|
}
|
|
@ -396,6 +396,9 @@ mod types;
|
||||||
mod validation;
|
mod validation;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
// TODO: feature
|
||||||
|
pub mod derive_support;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
extern crate wasmi_derive;
|
||||||
|
extern crate wasmi;
|
||||||
|
|
||||||
|
use wasmi_derive::derive_externals;
|
||||||
|
use wasmi::HostError;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct NoInfoError;
|
||||||
|
impl HostError for NoInfoError {}
|
||||||
|
impl fmt::Display for NoInfoError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "NoInfoError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NonStaticExternals<'a> {
|
||||||
|
state: &'a mut usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_externals]
|
||||||
|
impl<'a> NonStaticExternals<'a> {
|
||||||
|
pub fn hello(&self, a: u32, b: u32) -> u32 {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment(&mut self) {
|
||||||
|
*self.state += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn traps(&self) -> Result<(), NoInfoError> {
|
||||||
|
Err(NoInfoError)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fart(&self, inbound_fart: Fart) -> Result<Fart, NoInfoError> {
|
||||||
|
Ok(inbound_fart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Fart;
|
Loading…
Reference in New Issue