mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-14 15:31:44 +00:00
simple user function
This commit is contained in:
@ -262,6 +262,12 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
|
|||||||
self.module.import.entries_mut().len() as u32 - 1
|
self.module.import.entries_mut().len() as u32 - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push export entry to module.
|
||||||
|
pub fn push_export(&mut self, export: elements::ExportEntry) -> u32 {
|
||||||
|
self.module.export.entries_mut().push(export);
|
||||||
|
self.module.export.entries_mut().len() as u32 - 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Add new function using dedicated builder
|
/// Add new function using dedicated builder
|
||||||
pub fn function(self) -> FunctionBuilder<Self> {
|
pub fn function(self) -> FunctionBuilder<Self> {
|
||||||
FunctionBuilder::with_callback(self)
|
FunctionBuilder::with_callback(self)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use builder::module;
|
use builder::{module, function, export};
|
||||||
use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalType,
|
use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalType,
|
||||||
ValueType, InitExpr, Opcode, Opcodes};
|
ValueType, InitExpr, Opcode, Opcodes};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
@ -73,13 +73,27 @@ const INDEX_FUNC_MAX: u32 = 10000;
|
|||||||
pub type UserFunctions = HashMap<String, UserFunction>;
|
pub type UserFunctions = HashMap<String, UserFunction>;
|
||||||
|
|
||||||
/// User function closure
|
/// User function closure
|
||||||
pub type UserFunctionClosure = Box<Fn(&CallerContext) -> Result<Option<RuntimeValue>, Error>>;
|
pub type UserFunctionClosure = Box<UserFunctionInterface>;
|
||||||
|
|
||||||
|
/// User-defined function execution interface
|
||||||
|
pub trait UserFunctionInterface {
|
||||||
|
fn call(&mut self, context: CallerContext) -> Result<Option<RuntimeValue>, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UserFunctionInterface for T where T: FnMut(CallerContext) -> Result<Option<RuntimeValue>, Error> {
|
||||||
|
fn call(&mut self, context: CallerContext) -> Result<Option<RuntimeValue>, Error> {
|
||||||
|
(&mut *self)(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Signature of user-defined env function
|
/// Signature of user-defined env function
|
||||||
pub struct UserFunction {
|
pub struct UserFunction {
|
||||||
params: Vec<ValueType>,
|
/// User function parameters (for signature matching)
|
||||||
result: Option<ValueType>,
|
pub params: Vec<ValueType>,
|
||||||
closure: UserFunctionClosure,
|
/// User function return type (for signature matching)
|
||||||
|
pub result: Option<ValueType>,
|
||||||
|
/// Executor of the function
|
||||||
|
pub closure: UserFunctionClosure,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Environment parameters.
|
/// Environment parameters.
|
||||||
@ -92,7 +106,7 @@ pub struct EnvParams {
|
|||||||
pub allow_memory_growth: bool,
|
pub allow_memory_growth: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserFunctionsInternals = HashMap<u32, UserFunctionClosure>;
|
type UserFunctionsInternals = Vec<::std::cell::RefCell<UserFunctionClosure>>;
|
||||||
|
|
||||||
pub struct EnvModuleInstance {
|
pub struct EnvModuleInstance {
|
||||||
_params: EnvParams,
|
_params: EnvParams,
|
||||||
@ -168,20 +182,26 @@ impl ModuleInstanceInterface for EnvModuleInstance {
|
|||||||
.map(|g| g.get())
|
.map(|g| g.get())
|
||||||
.map(Some),
|
.map(Some),
|
||||||
INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())),
|
INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())),
|
||||||
idx @ _ if idx == INDEX_FUNC_MAX + 1 => outer.value_stack.pop().map(|_| None), // TODO: `gas(i32) -> None` function
|
idx if idx > INDEX_FUNC_MAX && idx <= INDEX_FUNC_MAX + self.user_functions.len() as u32 => {
|
||||||
idx @ _ if idx == INDEX_FUNC_MAX + 2 => Ok(Some(RuntimeValue::I32(0))), // TODO: `_storage_size() -> i32` function
|
// user-defined function
|
||||||
idx @ _ if idx == INDEX_FUNC_MAX + 3 => outer.value_stack.pop_triple().map(|_| Some(RuntimeValue::I32(0))), // TODO: `_storage_size(i32,i32,i32) -> i32` function
|
let user_index = idx - (INDEX_FUNC_MAX+1);
|
||||||
_ => Err(Error::Trap(format!("trying to call function with index {} in emv module", index))),
|
let func = self.user_functions.get(user_index as usize).ok_or(Error::Trap(format!("Trying to invoke user-defined function {}", user_index)))?;
|
||||||
|
func.borrow_mut().call(outer)
|
||||||
|
},
|
||||||
|
// idx @ _ if idx == INDEX_FUNC_MAX + 1 => outer.value_stack.pop().map(|_| None), // TODO: `gas(i32) -> None` function
|
||||||
|
// idx @ _ if idx == INDEX_FUNC_MAX + 2 => Ok(Some(RuntimeValue::I32(0))), // TODO: `_storage_size() -> i32` function
|
||||||
|
// idx @ _ if idx == INDEX_FUNC_MAX + 3 => outer.value_stack.pop_triple().map(|_| Some(RuntimeValue::I32(0))), // TODO: `_storage_size(i32,i32,i32) -> i32` function
|
||||||
|
_ => Err(Error::Trap(format!("trying to call function with index {} in env module", index))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn env_module(user_functions: UserFunctions) -> Result<EnvModuleInstance, Error> {
|
pub fn env_module(user_functions: UserFunctions) -> Result<EnvModuleInstance, Error> {
|
||||||
let mut env_params = EnvParams::default();
|
let env_params = EnvParams::default();
|
||||||
debug_assert!(env_params.total_stack < env_params.total_memory);
|
debug_assert!(env_params.total_stack < env_params.total_memory);
|
||||||
debug_assert!((env_params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0);
|
debug_assert!((env_params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0);
|
||||||
debug_assert!((env_params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0);
|
debug_assert!((env_params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0);
|
||||||
let mut module = module()
|
let mut builder = module()
|
||||||
// memory regions
|
// memory regions
|
||||||
.memory()
|
.memory()
|
||||||
.with_min(env_params.total_memory / LINEAR_MEMORY_PAGE_SIZE)
|
.with_min(env_params.total_memory / LINEAR_MEMORY_PAGE_SIZE)
|
||||||
@ -232,33 +252,31 @@ pub fn env_module(user_functions: UserFunctions) -> Result<EnvModuleInstance, Er
|
|||||||
.signature().return_type().i32().build()
|
.signature().return_type().i32().build()
|
||||||
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
|
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
|
||||||
.build()
|
.build()
|
||||||
.with_export(ExportEntry::new("getTotalMemory".into(), Internal::Function(INDEX_FUNC_GET_TOTAL_MEMORY)))
|
.with_export(ExportEntry::new("getTotalMemory".into(), Internal::Function(INDEX_FUNC_GET_TOTAL_MEMORY)));
|
||||||
.build();
|
|
||||||
// non-standard functions (TODO: pass as parameters to EnvModuleInstance)
|
|
||||||
// .function()
|
|
||||||
// .signature().param().i32().build()
|
|
||||||
// .body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
|
|
||||||
// .build()
|
|
||||||
// .with_export(ExportEntry::new("gas".into(), Internal::Function(INDEX_FUNC_MAX + 1)))
|
|
||||||
// .function()
|
|
||||||
// .signature().return_type().i32().build()
|
|
||||||
// .body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
|
|
||||||
// .build()
|
|
||||||
// .with_export(ExportEntry::new("_storage_size".into(), Internal::Function(INDEX_FUNC_MAX + 2)))
|
|
||||||
// .function()
|
|
||||||
// .signature().param().i32().param().i32().param().i32().return_type().i32().build()
|
|
||||||
// .body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
|
|
||||||
// .build()
|
|
||||||
// .with_export(ExportEntry::new("_storage_write".into(), Internal::Function(INDEX_FUNC_MAX + 3)))
|
|
||||||
// .build();
|
|
||||||
|
|
||||||
let mut funcs = user_functions;
|
let mut funcs = user_functions;
|
||||||
let internals = HashMap::new();
|
let mut internals = UserFunctionsInternals::new();
|
||||||
|
let mut index = INDEX_FUNC_MAX + 1;
|
||||||
for (func_name, func) in funcs.drain() {
|
for (func_name, func) in funcs.drain() {
|
||||||
|
let _location = builder.push_function(
|
||||||
|
function()
|
||||||
|
.signature().with_params(func.params).with_return_type(func.result).build()
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
let _export_idx = builder.push_export(
|
||||||
|
export()
|
||||||
|
.field(&func_name)
|
||||||
|
.internal().func(index)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
internals.push(::std::cell::RefCell::new(func.closure));
|
||||||
|
|
||||||
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnvModuleInstance::new(env_params, internals, module)
|
EnvModuleInstance::new(env_params, internals, builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EnvParams {
|
impl Default for EnvParams {
|
||||||
|
@ -65,6 +65,7 @@ mod variable;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex};
|
pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, CallerContext};
|
||||||
pub use self::program::ProgramInstance;
|
pub use self::program::ProgramInstance;
|
||||||
pub use self::value::RuntimeValue;
|
pub use self::value::RuntimeValue;
|
||||||
|
pub use self::env::{UserFunctions, UserFunction};
|
@ -62,8 +62,11 @@ pub struct ModuleInstance {
|
|||||||
|
|
||||||
/// Caller context.
|
/// Caller context.
|
||||||
pub struct CallerContext<'a> {
|
pub struct CallerContext<'a> {
|
||||||
|
/// Value stack limit
|
||||||
pub value_stack_limit: usize,
|
pub value_stack_limit: usize,
|
||||||
|
/// Frame stack limit
|
||||||
pub frame_stack_limit: usize,
|
pub frame_stack_limit: usize,
|
||||||
|
/// Stack of the input parameters
|
||||||
pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
|
pub value_stack: &'a mut StackWithLimit<RuntimeValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,6 +318,7 @@ impl ModuleInstanceInterface for ModuleInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CallerContext<'a> {
|
impl<'a> CallerContext<'a> {
|
||||||
|
/// Top most args
|
||||||
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>) -> Self {
|
pub fn topmost(args: &'a mut StackWithLimit<RuntimeValue>) -> Self {
|
||||||
CallerContext {
|
CallerContext {
|
||||||
value_stack_limit: 1024,
|
value_stack_limit: 1024,
|
||||||
@ -323,6 +327,7 @@ impl<'a> CallerContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nested context
|
||||||
pub fn nested(outer: &'a mut FunctionContext) -> Self {
|
pub fn nested(outer: &'a mut FunctionContext) -> Self {
|
||||||
CallerContext {
|
CallerContext {
|
||||||
value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(),
|
value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(),
|
||||||
|
@ -27,6 +27,7 @@ impl ProgramInstance {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create new program instance with predefined user-defined functions
|
||||||
pub fn with_functions(funcs: env::UserFunctions) -> Result<Self, Error> {
|
pub fn with_functions(funcs: env::UserFunctions) -> Result<Self, Error> {
|
||||||
Ok(ProgramInstance {
|
Ok(ProgramInstance {
|
||||||
essence: Arc::new(ProgramInstanceEssence::with_functions(funcs)?),
|
essence: Arc::new(ProgramInstanceEssence::with_functions(funcs)?),
|
||||||
@ -58,6 +59,7 @@ impl ProgramInstanceEssence {
|
|||||||
ProgramInstanceEssence::with_functions(HashMap::with_capacity(0))
|
ProgramInstanceEssence::with_functions(HashMap::with_capacity(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create new program essence with provided user-defined functions
|
||||||
pub fn with_functions(funcs: env::UserFunctions) -> Result<Self, Error> {
|
pub fn with_functions(funcs: env::UserFunctions) -> Result<Self, Error> {
|
||||||
let mut modules = HashMap::new();
|
let mut modules = HashMap::new();
|
||||||
let env_module: Arc<ModuleInstanceInterface> = Arc::new(env_module(funcs)?);
|
let env_module: Arc<ModuleInstanceInterface> = Arc::new(env_module(funcs)?);
|
||||||
|
@ -4,7 +4,7 @@ use builder::module;
|
|||||||
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
|
use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType,
|
||||||
InitExpr, ValueType, Opcodes, Opcode};
|
InitExpr, ValueType, Opcodes, Opcode};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::module::ModuleInstanceInterface;
|
use interpreter::module::{ModuleInstanceInterface, CallerContext};
|
||||||
use interpreter::program::ProgramInstance;
|
use interpreter::program::ProgramInstance;
|
||||||
use interpreter::value::RuntimeValue;
|
use interpreter::value::RuntimeValue;
|
||||||
|
|
||||||
@ -119,3 +119,42 @@ fn global_get_set() {
|
|||||||
assert_eq!(module.execute_index(1, vec![]).unwrap_err(), Error::Variable("trying to update immutable variable".into()));
|
assert_eq!(module.execute_index(1, vec![]).unwrap_err(), Error::Variable("trying to update immutable variable".into()));
|
||||||
assert_eq!(module.execute_index(2, vec![]).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into()));
|
assert_eq!(module.execute_index(2, vec![]).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_user_functions() {
|
||||||
|
use interpreter::{UserFunction, UserFunctions};
|
||||||
|
|
||||||
|
let module = module()
|
||||||
|
.with_import(ImportEntry::new("env".into(), "custom_alloc".into(), External::Function(0)))
|
||||||
|
.function()
|
||||||
|
.signature().return_type().i32().build()
|
||||||
|
.body().with_opcodes(Opcodes::new(vec![
|
||||||
|
Opcode::I32Const(32),
|
||||||
|
Opcode::Call(0),
|
||||||
|
Opcode::End,
|
||||||
|
])).build()
|
||||||
|
.build()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut top = 0i32;
|
||||||
|
let mut user_functions = UserFunctions::new();
|
||||||
|
user_functions.insert(
|
||||||
|
"custom_alloc".to_owned(),
|
||||||
|
UserFunction {
|
||||||
|
params: vec![ValueType::I32],
|
||||||
|
result: Some(ValueType::I32),
|
||||||
|
closure: Box::new(move |context: CallerContext| {
|
||||||
|
let prev = top;
|
||||||
|
top = top + context.value_stack.pop_as::<i32>()?;
|
||||||
|
Ok(Some(prev.into()))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let program = ProgramInstance::with_functions(user_functions).unwrap();
|
||||||
|
let module_instance = program.add_module("main", module).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(0));
|
||||||
|
assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(32));
|
||||||
|
assert_eq!(module_instance.execute_index(1, vec![]).unwrap().unwrap(), RuntimeValue::I32(64));
|
||||||
|
}
|
Reference in New Issue
Block a user