2019-09-19 23:05:17 +02:00
use crate ::instructions ::{
stack ::{ Stack , Stackable } ,
2019-09-20 14:31:15 +02:00
wasm ::{ self , Type , Value } ,
2019-09-20 14:07:56 +02:00
Instruction ,
2019-09-20 11:37:38 +02:00
} ;
2019-09-20 14:07:56 +02:00
use std ::{ convert ::TryFrom , marker ::PhantomData } ;
2019-09-20 12:02:11 +02:00
2019-09-20 11:37:38 +02:00
struct Runtime < ' invocation , ' instance , Instance , Export >
2019-09-20 00:06:15 +02:00
where
2019-09-20 11:37:38 +02:00
Export : wasm ::Export + ' instance ,
Instance : wasm ::Instance < Export > + ' instance ,
2019-09-20 00:06:15 +02:00
{
2019-09-20 14:07:56 +02:00
invocation_inputs : & ' invocation [ Value ] ,
stack : Stack < Value > ,
2019-09-20 00:06:15 +02:00
wasm_instance : & ' instance Instance ,
2019-09-20 11:37:38 +02:00
wasm_exports : PhantomData < Export > ,
2019-09-20 00:06:15 +02:00
}
2019-09-19 00:18:36 +02:00
2019-09-20 14:07:56 +02:00
type ExecutableInstruction < Instance , Export > =
Box < dyn Fn ( & mut Runtime < Instance , Export > ) -> Result < ( ) , String > > ;
2019-09-20 11:37:38 +02:00
pub struct Interpreter < Instance , Export >
2019-09-20 00:06:15 +02:00
where
2019-09-20 11:37:38 +02:00
Export : wasm ::Export ,
Instance : wasm ::Instance < Export > ,
2019-09-20 00:06:15 +02:00
{
2019-09-20 12:02:11 +02:00
executable_instructions : Vec < ExecutableInstruction < Instance , Export > > ,
2019-09-19 00:18:36 +02:00
}
2019-09-20 11:37:38 +02:00
impl < Instance , Export > Interpreter < Instance , Export >
2019-09-20 00:06:15 +02:00
where
2019-09-20 11:37:38 +02:00
Export : wasm ::Export ,
Instance : wasm ::Instance < Export > ,
2019-09-20 00:06:15 +02:00
{
2019-09-20 12:02:11 +02:00
fn iter ( & self ) -> impl Iterator < Item = & ExecutableInstruction < Instance , Export > > + '_ {
2019-09-19 23:05:17 +02:00
self . executable_instructions . iter ( )
}
2019-09-20 11:37:38 +02:00
pub fn run (
2019-09-20 00:06:15 +02:00
& self ,
2019-09-20 14:07:56 +02:00
invocation_inputs : & [ Value ] ,
2019-09-20 00:06:15 +02:00
wasm_instance : & Instance ,
2019-09-20 14:07:56 +02:00
) -> Result < Stack < Value > , String > {
2019-09-20 00:06:15 +02:00
let mut runtime = Runtime {
invocation_inputs ,
stack : Stack ::new ( ) ,
wasm_instance ,
2019-09-20 11:37:38 +02:00
wasm_exports : PhantomData ,
2019-09-20 00:06:15 +02:00
} ;
2019-09-19 23:05:17 +02:00
for executable_instruction in self . iter ( ) {
2019-09-20 00:06:15 +02:00
match executable_instruction ( & mut runtime ) {
2019-09-19 23:05:17 +02:00
Ok ( _ ) = > continue ,
Err ( message ) = > return Err ( message ) ,
}
}
2019-09-20 00:06:15 +02:00
Ok ( runtime . stack )
2019-09-19 00:18:36 +02:00
}
}
2019-09-20 11:37:38 +02:00
impl < ' binary_input , Instance , Export > TryFrom < & Vec < Instruction < ' binary_input > > >
for Interpreter < Instance , Export >
2019-09-20 00:06:15 +02:00
where
2019-09-20 11:37:38 +02:00
Export : wasm ::Export ,
Instance : wasm ::Instance < Export > ,
2019-09-20 00:06:15 +02:00
{
type Error = String ;
2019-09-19 00:18:36 +02:00
fn try_from ( instructions : & Vec < Instruction > ) -> Result < Self , Self ::Error > {
let executable_instructions = instructions
. iter ( )
2019-09-20 00:06:15 +02:00
. map (
2019-09-20 12:02:11 +02:00
| instruction | -> ExecutableInstruction < Instance , Export > {
2019-09-20 00:06:15 +02:00
match instruction {
Instruction ::ArgumentGet ( index ) = > {
let index = index . to_owned ( ) ;
let instruction_name : String = instruction . into ( ) ;
2019-09-20 11:37:38 +02:00
Box ::new ( move | runtime : & mut Runtime < Instance , Export > | -> Result < ( ) , _ > {
2019-09-20 00:06:15 +02:00
let invocation_inputs = runtime . invocation_inputs ;
if index > = ( invocation_inputs . len ( ) as u64 ) {
return Err ( format! (
2019-09-20 11:37:38 +02:00
" `{}` cannot access argument #{} because it doesn't exist. " ,
2019-09-20 00:06:15 +02:00
instruction_name , index
) ) ;
}
runtime . stack . push ( invocation_inputs [ index as usize ] ) ;
Ok ( ( ) )
} )
}
Instruction ::CallExport ( export_name ) = > {
let export_name = ( * export_name ) . to_owned ( ) ;
2019-09-20 11:37:38 +02:00
let instruction_name : String = instruction . into ( ) ;
2019-09-20 00:06:15 +02:00
2019-09-20 11:37:38 +02:00
Box ::new ( move | runtime : & mut Runtime < Instance , Export > | -> Result < ( ) , _ > {
let instance = runtime . wasm_instance ;
match instance . export ( & export_name ) {
Some ( export ) = > {
let inputs_cardinality = export . inputs_cardinality ( ) ;
match runtime . stack . pop ( inputs_cardinality ) {
Some ( inputs ) = > {
2019-09-20 14:31:15 +02:00
let input_types = inputs
. iter ( )
. map ( | input | input . into ( ) )
. collect ::< Vec < Type > > ( ) ;
if input_types ! = export . inputs ( ) {
return Err ( format! (
" `{}` cannot call the exported function `{}` because the value types in the stack mismatch the function signature (expects {:?}). " ,
instruction_name ,
export_name ,
export . inputs ( ) ,
) )
}
2019-09-20 11:37:38 +02:00
match export . call ( & inputs ) {
Ok ( outputs ) = > {
for output in outputs . iter ( ) {
2019-09-20 14:07:56 +02:00
runtime . stack . push ( * output ) ;
2019-09-20 11:37:38 +02:00
}
Ok ( ( ) )
} ,
Err ( _ ) = > Err ( " failed " . into ( ) ) ,
}
}
None = > Err ( format! (
2019-09-20 14:31:15 +02:00
" `{}` cannot call the exported function `{}` because there is no enought data in the stack for the arguments (needs {}). " ,
2019-09-20 11:37:38 +02:00
instruction_name ,
export_name ,
inputs_cardinality ,
) )
}
2019-09-20 14:07:56 +02:00
}
2019-09-20 11:37:38 +02:00
None = > Err ( format! (
" `{}` cannot call the exported function `{}` because it doesn't exist. " ,
instruction_name ,
export_name ,
) )
}
2019-09-20 00:06:15 +02:00
} )
}
Instruction ::ReadUtf8 = > {
2019-09-20 11:37:38 +02:00
Box ::new ( | _runtime : & mut Runtime < Instance , Export > | -> Result < ( ) , _ > {
2019-09-20 00:06:15 +02:00
println! ( " read utf8 " ) ;
Ok ( ( ) )
} )
}
Instruction ::Call ( index ) = > {
let index = index . to_owned ( ) ;
2019-09-20 11:37:38 +02:00
Box ::new ( move | _runtime : & mut Runtime < Instance , Export > | -> Result < ( ) , _ > {
2019-09-20 00:06:15 +02:00
println! ( " call {} " , index ) ;
Ok ( ( ) )
} )
}
_ = > unimplemented! ( ) ,
2019-09-19 00:18:36 +02:00
}
2019-09-20 00:06:15 +02:00
} ,
)
2019-09-19 00:25:28 +02:00
. collect ( ) ;
2019-09-19 00:18:36 +02:00
Ok ( Interpreter {
2019-09-19 23:05:17 +02:00
executable_instructions ,
2019-09-19 00:18:36 +02:00
} )
}
}
#[ cfg(test) ]
mod tests {
use super ::Interpreter ;
2019-09-20 14:07:56 +02:00
use crate ::instructions ::{
stack ::Stackable ,
wasm ::{ self , Type , Value } ,
Instruction ,
} ;
2019-09-20 00:06:15 +02:00
use std ::{ collections ::HashMap , convert ::TryInto } ;
2019-09-20 11:37:38 +02:00
struct Export {
2019-09-20 14:07:56 +02:00
inputs : Vec < Type > ,
outputs : Vec < Type > ,
function : fn ( arguments : & [ Value ] ) -> Result < Vec < Value > , ( ) > ,
2019-09-20 11:37:38 +02:00
}
impl wasm ::Export for Export {
fn inputs_cardinality ( & self ) -> usize {
self . inputs . len ( ) as usize
}
fn outputs_cardinality ( & self ) -> usize {
self . outputs . len ( )
}
2019-09-20 14:07:56 +02:00
fn inputs ( & self ) -> & [ Type ] {
2019-09-20 11:37:38 +02:00
& self . inputs
}
2019-09-20 14:07:56 +02:00
fn outputs ( & self ) -> & [ Type ] {
2019-09-20 11:37:38 +02:00
& self . outputs
}
2019-09-20 14:07:56 +02:00
fn call ( & self , arguments : & [ Value ] ) -> Result < Vec < Value > , ( ) > {
2019-09-20 11:37:38 +02:00
( self . function ) ( arguments )
}
}
2019-09-20 00:06:15 +02:00
struct Instance {
2019-09-20 11:37:38 +02:00
exports : HashMap < String , Export > ,
2019-09-20 00:06:15 +02:00
}
impl Instance {
fn new ( ) -> Self {
Self {
exports : {
let mut hashmap = HashMap ::new ( ) ;
2019-09-20 11:37:38 +02:00
hashmap . insert (
" sum " . into ( ) ,
Export {
2019-09-20 14:07:56 +02:00
inputs : vec ! [ Type ::I32 , Type ::I32 ] ,
outputs : vec ! [ Type ::I32 ] ,
function : | arguments : & [ Value ] | {
2019-09-20 11:37:38 +02:00
let a : i32 = ( & arguments [ 0 ] ) . try_into ( ) . unwrap ( ) ;
let b : i32 = ( & arguments [ 1 ] ) . try_into ( ) . unwrap ( ) ;
2019-09-20 14:07:56 +02:00
Ok ( vec! [ Value ::I32 ( a + b ) ] )
2019-09-20 11:37:38 +02:00
} ,
} ,
) ;
2019-09-20 00:06:15 +02:00
hashmap
} ,
}
}
}
2019-09-20 11:37:38 +02:00
impl wasm ::Instance < Export > for Instance {
fn export ( & self , export_name : & str ) -> Option < & Export > {
self . exports . get ( export_name )
2019-09-20 00:06:15 +02:00
}
}
2019-09-19 00:18:36 +02:00
#[ test ]
fn test_interpreter_from_instructions ( ) {
let instructions = vec! [
Instruction ::ArgumentGet ( 0 ) ,
Instruction ::ArgumentGet ( 0 ) ,
2019-09-20 00:06:15 +02:00
Instruction ::CallExport ( " foo " ) ,
2019-09-19 00:18:36 +02:00
Instruction ::ReadUtf8 ,
Instruction ::Call ( 7 ) ,
] ;
2019-09-20 11:37:38 +02:00
let interpreter : Interpreter < ( ) , ( ) > = ( & instructions ) . try_into ( ) . unwrap ( ) ;
2019-09-19 23:05:17 +02:00
assert_eq! ( interpreter . executable_instructions . len ( ) , 5 ) ;
}
#[ test ]
fn test_interpreter_argument_get ( ) {
2019-09-20 11:37:38 +02:00
let interpreter : Interpreter < Instance , Export > =
2019-09-20 00:06:15 +02:00
( & vec! [ Instruction ::ArgumentGet ( 0 ) ] ) . try_into ( ) . unwrap ( ) ;
2019-09-20 11:37:38 +02:00
2019-09-20 14:07:56 +02:00
let invocation_inputs = vec! [ Value ::I32 ( 42 ) ] ;
2019-09-20 00:06:15 +02:00
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
2019-09-19 23:05:17 +02:00
assert! ( run . is_ok ( ) ) ;
let stack = run . unwrap ( ) ;
2019-09-19 00:18:36 +02:00
2019-09-20 14:07:56 +02:00
assert_eq! ( stack . as_slice ( ) , & [ Value ::I32 ( 42 ) ] ) ;
2019-09-19 00:18:36 +02:00
}
2019-09-20 00:06:15 +02:00
#[ test ]
fn test_interpreter_argument_get_invalid_index ( ) {
2019-09-20 11:37:38 +02:00
let interpreter : Interpreter < Instance , Export > =
2019-09-20 00:06:15 +02:00
( & vec! [ Instruction ::ArgumentGet ( 1 ) ] ) . try_into ( ) . unwrap ( ) ;
2019-09-20 11:37:38 +02:00
2019-09-20 14:07:56 +02:00
let invocation_inputs = vec! [ Value ::I32 ( 42 ) ] ;
2019-09-20 00:06:15 +02:00
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
assert! ( run . is_err ( ) ) ;
let error = run . unwrap_err ( ) ;
assert_eq! (
error ,
2019-09-20 11:37:38 +02:00
String ::from ( " `arg.get 1` cannot access argument #1 because it doesn't exist. " )
2019-09-20 00:06:15 +02:00
) ;
}
#[ test ]
fn test_interpreter_argument_get_argument_get ( ) {
2019-09-20 11:37:38 +02:00
let interpreter : Interpreter < Instance , Export > =
2019-09-20 00:06:15 +02:00
( & vec! [ Instruction ::ArgumentGet ( 0 ) , Instruction ::ArgumentGet ( 1 ) ] )
. try_into ( )
. unwrap ( ) ;
2019-09-20 11:37:38 +02:00
2019-09-20 14:07:56 +02:00
let invocation_inputs = vec! [ Value ::I32 ( 7 ) , Value ::I32 ( 42 ) ] ;
2019-09-20 00:06:15 +02:00
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
assert! ( run . is_ok ( ) ) ;
let stack = run . unwrap ( ) ;
2019-09-20 14:07:56 +02:00
assert_eq! ( stack . as_slice ( ) , & [ Value ::I32 ( 7 ) , Value ::I32 ( 42 ) ] ) ;
2019-09-20 00:06:15 +02:00
}
#[ test ]
fn test_interpreter_call_export ( ) {
2019-09-20 11:37:38 +02:00
let interpreter : Interpreter < Instance , Export > = ( & vec! [
Instruction ::ArgumentGet ( 1 ) ,
Instruction ::ArgumentGet ( 0 ) ,
Instruction ::CallExport ( " sum " ) ,
] )
. try_into ( )
. unwrap ( ) ;
2019-09-20 14:07:56 +02:00
let invocation_inputs = vec! [ Value ::I32 ( 3 ) , Value ::I32 ( 4 ) ] ;
2019-09-20 11:37:38 +02:00
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
2019-09-20 00:06:15 +02:00
assert! ( run . is_ok ( ) ) ;
let stack = run . unwrap ( ) ;
2019-09-20 14:07:56 +02:00
assert_eq! ( stack . as_slice ( ) , & [ Value ::I32 ( 7 ) ] ) ;
2019-09-20 11:37:38 +02:00
}
#[ test ]
fn test_interpreter_call_export_invalid_export_name ( ) {
let interpreter : Interpreter < Instance , Export > =
( & vec! [ Instruction ::CallExport ( " bar " ) ] ) . try_into ( ) . unwrap ( ) ;
let invocation_inputs = vec! [ ] ;
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
assert! ( run . is_err ( ) ) ;
let error = run . unwrap_err ( ) ;
assert_eq! (
error ,
String ::from ( r # "`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."# )
) ;
2019-09-20 00:06:15 +02:00
}
2019-09-20 14:31:15 +02:00
#[ test ]
fn test_interpreter_call_export_too_small_stack ( ) {
let interpreter : Interpreter < Instance , Export > = ( & vec! [
Instruction ::ArgumentGet ( 0 ) ,
Instruction ::CallExport ( " sum " ) ,
// ^^^ `sum` expects 2 values in the stack, only one is present
] )
. try_into ( )
. unwrap ( ) ;
let invocation_inputs = vec! [ Value ::I32 ( 3 ) , Value ::I32 ( 4 ) ] ;
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
assert! ( run . is_err ( ) ) ;
let error = run . unwrap_err ( ) ;
assert_eq! (
error ,
String ::from ( r # "`call-export "sum"` cannot call the exported function `sum` because there is no enought data in the stack for the arguments (needs 2)."# )
) ;
}
#[ test ]
fn test_interpreter_call_export_invalid_types_in_the_stack ( ) {
let interpreter : Interpreter < Instance , Export > = ( & vec! [
Instruction ::ArgumentGet ( 1 ) ,
Instruction ::ArgumentGet ( 0 ) ,
Instruction ::CallExport ( " sum " ) ,
] )
. try_into ( )
. unwrap ( ) ;
let invocation_inputs = vec! [ Value ::I32 ( 3 ) , Value ::I64 ( 4 ) ] ;
// ^^^ mismatch with `sum` signature
let instance = Instance ::new ( ) ;
let run = interpreter . run ( & invocation_inputs , & instance ) ;
assert! ( run . is_err ( ) ) ;
let error = run . unwrap_err ( ) ;
assert_eq! (
error ,
String ::from ( r # "`call-export "sum"` cannot call the exported function `sum` because the value types in the stack mismatch the function signature (expects [I32, I32])."# )
) ;
}
2019-09-19 00:18:36 +02:00
}