Skip to content

Instantly share code, notes, and snippets.

@pepyakin
Last active January 3, 2018 14:39
Show Gist options
  • Save pepyakin/578a971d71e13424fb9adfa388d22877 to your computer and use it in GitHub Desktop.
Save pepyakin/578a971d71e13424fb9adfa388d22877 to your computer and use it in GitHub Desktop.
// #![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