Skip to content

Commit

Permalink
Implement return/argument for child runtimes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Wilson committed Nov 10, 2019
1 parent 8a1fbb0 commit 446e68d
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 31 deletions.
50 changes: 50 additions & 0 deletions src/env/child/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,54 @@ impl<'a> ChildRuntime<'a> {

Ok(Some(retcode.into()))
}

/// Copies the argument data from the most recent call into memory at the
/// given offtet and length. Returns the actual length of the argument data.
///
/// # Signature
///
/// ```text
/// eth2_argument(dest_offset: u32, dest_length: u32) -> u32
/// ```
fn ext_argument(&self, args: RuntimeArgs) -> ExtResult {
let memory = self.memory();

let dest_ptr: u32 = args.nth(0);
let dest_len: u32 = args.nth(1);

let call_stack = self.call_stack.borrow();
let top = call_stack
.last()
.expect("eth2_argument requires a call stack");

let len = top.transfer_argument(&memory, dest_ptr, dest_len).unwrap();

Ok(Some(len.into()))
}

/// Copies data from the given offset and length into the buffer allocated
/// by the caller. Returns the total size of the caller's buffer.
///
/// # Signature
///
/// ```text
/// eth2_return(offset: u32, length: u32) -> u32
/// ```
fn ext_return(&self, args: RuntimeArgs) -> ExtResult {
let memory = self.memory();

let src_ptr: u32 = args.nth(0);
let src_len: u32 = args.nth(1);

let call_stack = self.call_stack.borrow();
let top = call_stack
.last()
.expect("eth2_return requires a call stack");

let len = top.transfer_return(&memory, src_ptr, src_len).unwrap();

Ok(Some(len.into()))
}
}

#[derive(Debug)]
Expand All @@ -115,6 +163,8 @@ impl<'a, 'b> Externals for ChildExternals<'a, 'b> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
externals::CALL => self.0.ext_call(args),
externals::ARGUMENT => self.0.ext_argument(args),
externals::RETURN => self.0.ext_return(args),
_ => panic!("unknown function index"),
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/env/child/resolver.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod externals {
pub const CALL: usize = 1;
pub const ARGUMENT: usize = 2;
pub const RETURN: usize = 3;
}

use wasmi::{
Expand All @@ -15,6 +17,16 @@ impl<'a> ModuleImportResolver for ChildModuleImportResolver {
_signature: &Signature,
) -> Result<FuncRef, InterpreterError> {
let func_ref = match field_name {
"eth2_return" => FuncInstance::alloc_host(
// eth2_return(offset: u32, length: u32) -> u32
Signature::new(&[ValueType::I32; 2][..], Some(ValueType::I32)),
externals::RETURN,
),
"eth2_argument" => FuncInstance::alloc_host(
// eth2_argument(offset: u32, length: u32) -> u32
Signature::new(&[ValueType::I32; 2][..], Some(ValueType::I32)),
externals::ARGUMENT,
),
"eth2_call" => FuncInstance::alloc_host(
// eth2_call(name, name_len, arg, arg_len, ret, ret_len)
Signature::new(&[ValueType::I32; 6][..], Some(ValueType::I32)),
Expand Down
40 changes: 39 additions & 1 deletion src/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod root;

use typed_builder::TypedBuilder;

use wasmi::{MemoryRef, RuntimeValue, Trap};
use wasmi::{MemoryInstance, MemoryRef, RuntimeValue, Trap};

pub type ExtResult = Result<Option<RuntimeValue>, Trap>;

Expand All @@ -17,3 +17,41 @@ struct StackFrame {
return_offset: u32,
return_length: u32,
}

impl StackFrame {
pub fn transfer_argument(
&self,
dest: &MemoryRef,
dest_ptr: u32,
dest_len: u32,
) -> Result<u32, wasmi::Error> {
let len = std::cmp::min(dest_len, self.argument_length);

MemoryInstance::transfer(
&self.memory,
self.argument_offset as usize,
dest,
dest_ptr as usize,
len as usize,
)
.map(|_| self.argument_length)
}

pub fn transfer_return(
&self,
src: &MemoryRef,
src_ptr: u32,
src_len: u32,
) -> Result<u32, wasmi::Error> {
let len = std::cmp::min(src_len, self.return_length);

MemoryInstance::transfer(
src,
src_ptr as usize,
&self.memory,
self.return_offset as usize,
len as usize,
)
.map(|_| self.return_length)
}
}
30 changes: 6 additions & 24 deletions src/env/root/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use std::rc::{Rc, Weak};
use super::{ExtResult, StackFrame};

use wasmi::{
Externals, FuncInstance, ImportsBuilder, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap,
Externals, FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Trap,
};

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -123,18 +123,9 @@ impl<'a> RootRuntime<'a> {
.last()
.expect("eth2_return requires a call stack");

let len = std::cmp::min(src_len, top.return_length);
let len = top.transfer_return(&memory, src_ptr, src_len).unwrap();

MemoryInstance::transfer(
&memory,
src_ptr as usize,
&top.memory,
top.return_offset as usize,
len as usize,
)
.unwrap();

Ok(Some(top.return_length.into()))
Ok(Some(len.into()))
}

/// Copies the argument data from the most recent call into memory at the
Expand All @@ -156,18 +147,9 @@ impl<'a> RootRuntime<'a> {
.last()
.expect("eth2_argument requires a call stack");

let len = std::cmp::min(dest_len, top.argument_length);

MemoryInstance::transfer(
&top.memory,
top.argument_offset as usize,
&memory,
dest_ptr as usize,
len as usize,
)
.unwrap();
let len = top.transfer_argument(&memory, dest_ptr, dest_len).unwrap();

Ok(Some(top.argument_length.into()))
Ok(Some(len.into()))
}

fn ext_expose(&self, args: RuntimeArgs) -> ExtResult {
Expand Down
44 changes: 38 additions & 6 deletions tests/child_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn compile_wat(child_code: &str) -> Vec<u8> {
(memory (export "memory") 1)
(data (i32.const 0) "some_func")
(data (i32.const 10) "main")
(data (i32.const 14) "{}")
(data (i32.const 22) "{}")
(func $some_func
(export "some_func")
(result i32)
Expand All @@ -49,19 +49,25 @@ fn compile_wat(child_code: &str) -> Vec<u8> {
(func $main (export "main")
(call $expose (i32.const 0) (i32.const 9))
(call $load (i32.const 0) (i32.const 14) (i32.const {}))
(call $load (i32.const 0) (i32.const 22) (i32.const {}))
(i32.store (i32.const 14) (i32.const 1234))
(drop
(call
$call
(i32.const 0) (; Slot ;)
(i32.const 10) (; Name Offset ;)
(i32.const 4) (; Name Length ;)
(i32.const 0) (; Argument Offset ;)
(i32.const 0) (; Argument Length ;)
(i32.const 0) (; Return Offset ;)
(i32.const 0) (; Return Length ;)
(i32.const 14) (; Argument Offset ;)
(i32.const 4) (; Argument Length ;)
(i32.const 18) (; Return Offset ;)
(i32.const 4) (; Return Length ;)
)
)
(; Check the returned buffer from the child runtime ;)
(if
(i32.ne (i32.load (i32.const 18)) (i32.const 4321))
(then (unreachable)))
)
)
"#,
Expand All @@ -75,6 +81,22 @@ fn compile_wat(child_code: &str) -> Vec<u8> {
fn call() {
let child_code = r#"
(module
(import
"env"
"eth2_return"
(func
$eth2_return
(param i32)
(param i32)
(result i32)))
(import
"env"
"eth2_argument"
(func
$eth2_argument
(param i32)
(param i32)
(result i32)))
(import
"env"
"eth2_call"
Expand All @@ -90,6 +112,16 @@ fn call() {
(memory (export "memory") 1)
(data (i32.const 0) "some_func")
(func $main (export "main") (result i32) (local $x i32)
(; Check that the argument provided by the caller is 1234 ;)
(drop (call $eth2_argument (i32.const 10) (i32.const 4)))
(if
(i32.ne (i32.load (i32.const 10)) (i32.const 1234))
(then (unreachable)))
(; Return a value to the caller ;)
(i32.store (i32.const 10) (i32.const 4321))
(drop (call $eth2_return (i32.const 10) (i32.const 4)))
(i32.store (i32.const 10) (i32.const 9999))
(set_local $x
(call
Expand Down

0 comments on commit 446e68d

Please sign in to comment.