Last active
January 3, 2018 14:39
-
-
Save pepyakin/578a971d71e13424fb9adfa388d22877 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// #![feature(log_syntax)] | |
// #![feature(trace_macros)] | |
// trace_macros!(true); | |
#[derive(Debug, PartialEq, Eq)] | |
pub struct FuncInstance; | |
pub struct MemoryInstance; | |
struct GlobalInstance; | |
struct TableInstance; | |
#[derive(Debug, PartialEq, Eq)] | |
pub struct Error; | |
pub struct FunctionType { | |
params: Vec<ValueType>, | |
return_type: Option<ValueType>, | |
} | |
impl FunctionType { | |
fn params(&self) -> &[ValueType] { | |
&self.params | |
} | |
fn return_type(&self) -> Option<ValueType> { | |
self.return_type.as_ref().cloned() | |
} | |
} | |
#[derive(Copy, Clone, Debug, PartialEq)] | |
pub enum RuntimeValue { | |
I32(i32), | |
I64(i64), | |
F32(f32), | |
F64(f64), | |
} | |
impl From<RuntimeValue> for i32 { | |
fn from(v: RuntimeValue) -> Self { | |
match v { | |
RuntimeValue::I32(v) => v as i32, | |
_ => panic!(), | |
} | |
} | |
} | |
impl From<RuntimeValue> for u32 { | |
fn from(v: RuntimeValue) -> Self { | |
match v { | |
RuntimeValue::I32(v) => v as u32, | |
_ => panic!(), | |
} | |
} | |
} | |
impl From<RuntimeValue> for u64 { | |
fn from(v: RuntimeValue) -> Self { | |
match v { | |
RuntimeValue::I64(v) => v as u64, | |
_ => panic!(), | |
} | |
} | |
} | |
#[derive(Copy, Clone, PartialEq, Eq)] | |
pub enum ValueType { | |
I32, | |
I64, | |
F32, | |
F64, | |
} | |
#[derive(Clone, Debug, PartialEq, Eq)] | |
enum FuncRef { | |
Direct(Rc<FuncInstance>), | |
Host(usize), | |
} | |
enum MemRef { | |
Direct(Rc<MemoryInstance>), | |
Host(usize), | |
} | |
use std::rc::Rc; | |
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 f32 { | |
type NativeType = f32; | |
const VALUE_TYPE: ValueType = ValueType::F32; | |
fn to_runtime_value(self) -> RuntimeValue { | |
RuntimeValue::F32(self) | |
} | |
} | |
impl ConvertibleToWasm for f64 { | |
type NativeType = f64; | |
const VALUE_TYPE: ValueType = ValueType::F64; | |
fn to_runtime_value(self) -> RuntimeValue { | |
RuntimeValue::F64(self) | |
} | |
} | |
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) | |
} | |
} | |
trait Externals { | |
fn invoke_index( | |
&mut self, | |
index: u32, | |
args: &[RuntimeValue], | |
) -> Result<Option<RuntimeValue>, Error>; | |
fn check_signature(&self, index: usize, signature: &FunctionType) -> bool; | |
fn memory_by_index(&self, index: usize) -> &MemoryInstance; | |
fn table_by_index(&self, index: usize) -> &TableInstance; | |
fn global_by_index(&self, index: usize) -> &GlobalInstance; | |
} | |
trait ImportResolver { | |
fn resolve_func(&self, name: &str, signature: &FunctionType) -> FuncRef; | |
fn resolve_memory(&self, name: &str) -> MemRef; | |
} | |
pub struct Data<'a> { | |
acc: &'a mut u32, | |
} | |
// MACRO DEFS | |
macro_rules! resolve_fn { | |
(@iter $index:expr, $name_var:ident,) => (); | |
(@iter $index:expr, $name_var:ident, $name:ident $($names:ident)*) => ( | |
if $name_var == stringify!($name) { | |
return FuncRef::Host($index); | |
} | |
resolve_fn!(@iter $index + 1, $name_var, $($names)*) | |
); | |
($name_var:ident, $($names:ident),*) => ( | |
resolve_fn!(@iter 0, $name_var, $($names)*); | |
); | |
} | |
#[macro_export] | |
macro_rules! convert_args { | |
() => ([]); | |
( $( $t:ty ),* ) => ( [ $( { use $crate::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); | |
} | |
#[macro_export] | |
macro_rules! signature_equals { | |
( $signature:ident, ( $( $params: ty ),* ) ) => ( | |
{ | |
$signature.params() == &convert_args!($($params),*) && $signature.return_type() == None | |
} | |
); | |
( $signature:ident, ( $( $params: ty ),* ) -> $returns: ty ) => ( | |
{ | |
$signature.params() == &convert_args!($($params),*) && $signature.return_type() == Some({ use $crate::ConvertibleToWasm; <$returns>::VALUE_TYPE }) | |
} | |
); | |
} | |
#[macro_export] | |
macro_rules! check_signature { | |
( @iter $index:expr, $index_ident:ident, $signature:ident, ) => ({ | |
panic!("fn with index {} is undefined", $index); | |
}); | |
( @iter $index:expr, $index_ident:ident, $signature:ident, ( ( $( $params:ty ),* ) $( -> $returns:ty )* ) $(, $tail:tt)* ) => ( | |
if $index_ident == $index { | |
return signature_equals!($signature, ( $( $params ),* ) $( -> $returns )* ); | |
} | |
check_signature!(@iter $index + 1, $index_ident, $signature, $($tail),* ); | |
); | |
( $index_ident:ident, $signature:ident, $( ( $( $params:ty ),* ) $( -> $returns:ty )*),* ) => ( | |
check_signature!(@iter 0, $index_ident, $signature, $( ( ( $( $params ),* ) $( -> $returns )* ) ),* ); | |
); | |
} | |
#[macro_export] | |
macro_rules! signatures { | |
( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( | |
fn check_signature(&self, index: usize, signature: &FunctionType) -> bool { | |
check_signature!(index, signature, $( ( $( $params ),* ) $( -> $returns )* ),*); | |
} | |
); | |
} | |
#[macro_export] | |
macro_rules! unmarshall_args { | |
( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({ | |
$( | |
let $names : <$params as $crate::ConvertibleToWasm>::NativeType = $args_iter.next().unwrap().into(); | |
)* | |
$body | |
}) | |
} | |
#[macro_export] | |
macro_rules! marshall { | |
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ | |
let mut body = || { unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) }; | |
let r: <$returns as $crate::ConvertibleToWasm>::NativeType = body(); | |
return Ok(Some({ use $crate::ConvertibleToWasm; r.to_runtime_value() })) | |
}); | |
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ | |
let mut body = || { unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*) }; | |
body(); | |
return Ok(None) | |
}) | |
} | |
macro_rules! dispatch_fn { | |
( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => { | |
panic!("fn with index {} is undefined", $index); | |
}; | |
( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident, $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*) => ( | |
if $index_ident == $index { | |
{ marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) } | |
} | |
dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*) | |
); | |
( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => ( | |
dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*); | |
); | |
} | |
#[macro_export] | |
macro_rules! dispatch { | |
( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( | |
fn invoke_index( | |
&mut self, | |
index: u32, | |
args: &[$crate::RuntimeValue], | |
) -> Result<Option<$crate::RuntimeValue>, $crate::Error> { | |
let mut $objectname = self; | |
let mut args = args.iter().cloned(); | |
dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); | |
} | |
); | |
} | |
#[macro_export] | |
macro_rules! impl_function_executor { | |
( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => ( | |
struct Resolver; | |
impl $( $pre ) + $structname { | |
fn resolver() -> Resolver { | |
Resolver | |
} | |
} | |
impl ImportResolver for Resolver { | |
fn resolve_func(&self, name: &str, _signature: &FunctionType) -> FuncRef { | |
resolve_fn!(name, $( $name ),*); | |
panic!(); | |
} | |
fn resolve_memory(&self, name: &str) -> MemRef { | |
unimplemented!() | |
} | |
} | |
impl $( $pre ) + $crate::Externals for $structname { | |
dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); | |
signatures!( $( $name ( $( $params ),* ) $( -> $returns )* ),* ); | |
fn memory_by_index(&self, index: usize) -> &MemoryInstance { | |
unimplemented!() | |
} | |
fn table_by_index(&self, index: usize) -> &TableInstance { | |
unimplemented!() | |
} | |
fn global_by_index(&self, index: usize) -> &GlobalInstance { | |
unimplemented!() | |
} | |
} | |
); | |
} | |
// TODO: | |
// - impl memory lookup | |
// - clean up | |
impl_function_executor!( | |
state: Data<'a>, | |
add(a: u32, b: u32) -> u32 => { a + b }, | |
inc(n: u32) -> u64 => { *state.acc += n; *state.acc as u64 }, | |
dummy() => { println!("3") }, | |
is_negative(n: i32) -> u32 => { | |
// Test for an early return in function with return value. | |
if n < 0 { | |
return 1; | |
} | |
0 | |
}, | |
print_div(a: u32, b: u32) => { | |
// Test for an early return in function without return value. | |
if b == 0 { | |
return; | |
} | |
println!("{}", a / b); | |
} | |
=> <'a> | |
); | |
pub fn check_sig(data: &Data, index: usize, sig: &FunctionType) -> bool { | |
data.check_signature(index, sig) | |
} | |
#[test] | |
fn resolver() { | |
let resolver = Data::resolver(); | |
assert_eq!( | |
resolver.resolve_func( | |
"add", | |
&FunctionType { | |
params: vec![ValueType::I32, ValueType::I32], | |
return_type: Some(ValueType::I32), | |
}, | |
), | |
FuncRef::Host(0) | |
); | |
assert_eq!( | |
resolver.resolve_func( | |
"dummy", | |
&FunctionType { | |
params: vec![], | |
return_type: None, | |
} | |
), | |
FuncRef::Host(2) | |
); | |
} | |
#[test] | |
fn signature() { | |
// signature checking is required before indirect call, | |
// and will be done at the runtime. | |
let mut acc = 0; | |
let data = Data { acc: &mut acc }; | |
assert_eq!( | |
data.check_signature( | |
0, | |
&FunctionType { | |
params: vec![ValueType::I32, ValueType::I32], | |
return_type: Some(ValueType::I32), | |
} | |
), | |
true | |
); | |
assert_eq!( | |
data.check_signature( | |
2, | |
&FunctionType { | |
params: vec![], | |
return_type: None, | |
} | |
), | |
true | |
); | |
assert_eq!( | |
data.check_signature( | |
1, | |
&FunctionType { | |
params: vec![], | |
return_type: None, | |
} | |
), | |
false | |
); | |
} | |
#[test] | |
fn invoke() { | |
let mut acc = 1; | |
let mut data = Data { acc: &mut acc }; | |
assert_eq!( | |
data.invoke_index(0, &[3.to_runtime_value(), 5.to_runtime_value()]), | |
Ok(Some(RuntimeValue::I32(8))) | |
); | |
assert_eq!( | |
data.invoke_index(1, &[5.to_runtime_value()]), | |
Ok(Some(RuntimeValue::I64(6))) | |
); | |
assert_eq!(*data.acc, 6); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment