mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 11:21:07 +00:00
Introduce first groundwork for Wasm executor (#27)
* Introduce first groundwork for Wasm executor. * Remove old Rust-runtime code. * Avoid commiting compled files. * Add runtime precompile. * Rename so module makes more sense. * Further renaming. * Ensure tests work. * Allow bringing in of externalities. - Add util functions/macros. - Add uncompacted runtime. - Add some external crates from pwasm-std for managing allocs/memory stuff. * Nice macros for imports. * Allow passing in of data through allocators. Make memcpy and malloc work. Basic allocator. * Can now pass in bytes to WasmExecutor. * Additional cleanup. * Switch usages of `OutData` to `u64` No need to be able to return bytes anymore. * convert to safe but extremely verbose type conversion. @rphmeier any more concise way of doing this? * Remove StaticExternalities distinction. * Remove another unused use. * Refactor wasm utils out * Remove extraneous copies that weren't really testing anything. * Try to use wasm 0.15 * Make it work! * Call-time externalities working. * Add basic externalities. * Fix grumbles and note unwraps to be sorted. * Test storage externality. Unforunately had to change signatures of externalities to avoid immutable function returning a reference. Not sure what to do about this... * Fix nits. * Compile collation logic. * Move back to refs. Yey. * Remove "object" id for storage access. * Fix test. * Fix up rest of tests. * remove unwrap. * Expose set/get code in externalities Also improve tests and add nice wrappers in rust-wasm. * Add validator set. * Introduce validator set into externalities and test. * Add another external function. * Remove code and validators; use storage for everything. * Introduce validators function. * Tests (and a fix) for the validators getter. * Allow calls into runtime to return data. * Remove unneeded trace. * Make runtime printing a bit nicer. * Create separate runtimes for testing and polkadot. * Remove commented code. * Use new path. * Refactor into shared support module. * Fix warning. * Remove unwraps. * Make macro a little less unhygenic. * Add wasm files.
This commit is contained in:
committed by
Robert Habermeier
parent
45c3e40a62
commit
a670208a33
@@ -0,0 +1,207 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Rust implementation of Polkadot contracts.
|
||||
|
||||
use std::sync::{Arc};
|
||||
use std::collections::HashMap;
|
||||
pub use std::result;
|
||||
pub use parity_wasm::builder;
|
||||
pub use parity_wasm::elements::{ValueType, Module};
|
||||
pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor,
|
||||
UserDefinedElements, env_native_module, DummyUserError, ExecutionParams, UserError};
|
||||
use parity_wasm::interpreter;
|
||||
|
||||
pub type Error = interpreter::Error<DummyUserError>;
|
||||
pub type MemoryInstance = interpreter::MemoryInstance<DummyUserError>;
|
||||
pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>;
|
||||
|
||||
pub 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) } }
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! convert_args {
|
||||
() => ([]);
|
||||
( $( $t:ty ),* ) => ( [ $( { use $crate::wasm_utils::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] );
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! convert_fn {
|
||||
( $name:ident ( $( $params:ty ),* ) ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) );
|
||||
( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some({ use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE }) ) );
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! reverse_params {
|
||||
// Entry point, use brackets to recursively reverse above.
|
||||
($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => (
|
||||
reverse_params!($body $self $context [ $( $names : $params ),* ]);
|
||||
);
|
||||
($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({
|
||||
$(
|
||||
let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error.into()),
|
||||
};
|
||||
)*
|
||||
$body
|
||||
});
|
||||
($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => (
|
||||
reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*);
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! marshall {
|
||||
( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
||||
let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*);
|
||||
Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() }))
|
||||
});
|
||||
( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||
reverse_params!($body, $self, $context, $( $names : $params ),*);
|
||||
Ok(None)
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dispatch {
|
||||
( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => (
|
||||
fn execute(&mut self, name: &str, context: $crate::wasm_utils::CallerContext)
|
||||
-> $crate::wasm_utils::result::Result<Option<$crate::wasm_utils::RuntimeValue>, $crate::wasm_utils::Error> {
|
||||
let $objectname = self;
|
||||
match name {
|
||||
$(
|
||||
stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body),
|
||||
)*
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! signatures {
|
||||
( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => (
|
||||
const SIGNATURES: &'static [$crate::wasm_utils::UserFunctionDescriptor] = &[
|
||||
$(
|
||||
convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ),
|
||||
)*
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
pub trait IntoUserDefinedElements {
|
||||
fn into_user_defined_elements(&mut self) -> UserDefinedElements<DummyUserError>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_function_executor {
|
||||
( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => (
|
||||
impl $( $pre ) + $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname {
|
||||
dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*);
|
||||
}
|
||||
impl $( $pre ) + $structname {
|
||||
signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*);
|
||||
}
|
||||
impl $( $pre ) + $crate::wasm_utils::IntoUserDefinedElements for $structname {
|
||||
fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> {
|
||||
$crate::wasm_utils::UserDefinedElements {
|
||||
executor: Some(self),
|
||||
globals: HashMap::new(), // TODO: provide
|
||||
functions: ::std::borrow::Cow::from(Self::SIGNATURES),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummyUserFunctionExecutor;
|
||||
impl<E: UserError> interpreter::UserFunctionExecutor<E> for DummyUserFunctionExecutor {
|
||||
fn execute(&mut self, _name: &str, _context: interpreter::CallerContext<E>) ->
|
||||
result::Result<Option<interpreter::RuntimeValue>, interpreter::Error<E>>
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AddModuleWithoutFullDependentInstance {
|
||||
fn add_module_by_sigs(
|
||||
&self,
|
||||
name: &str,
|
||||
module: Module,
|
||||
functions: HashMap<&str, &'static [UserFunctionDescriptor]>,
|
||||
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>>;
|
||||
|
||||
fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error>;
|
||||
}
|
||||
|
||||
impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance<DummyUserError> {
|
||||
fn add_module_by_sigs(
|
||||
&self,
|
||||
name: &str,
|
||||
module: Module,
|
||||
functions: HashMap<&str, &'static [UserFunctionDescriptor]>
|
||||
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>> {
|
||||
let mut dufe = vec![DummyUserFunctionExecutor; functions.len()];
|
||||
let dufe_refs = dufe.iter_mut().collect::<Vec<_>>();
|
||||
let fake_module_map = functions.into_iter()
|
||||
.zip(dufe_refs.into_iter())
|
||||
.map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error<DummyUserError>> {
|
||||
let fake_module = Arc::new(
|
||||
interpreter::env_native_module(
|
||||
self.module(dep_mod_name).ok_or(DummyUserError)?, UserDefinedElements {
|
||||
executor: Some(dufe),
|
||||
globals: HashMap::new(),
|
||||
functions: ::std::borrow::Cow::from(functions),
|
||||
}
|
||||
)?
|
||||
);
|
||||
let fake_module: Arc<interpreter::ModuleInstanceInterface<_>> = fake_module;
|
||||
Ok((dep_mod_name.into(), fake_module))
|
||||
})
|
||||
.collect::<result::Result<HashMap<_, _>, interpreter::Error<DummyUserError>>>()?;
|
||||
self.add_module(name, module, Some(&fake_module_map))
|
||||
}
|
||||
|
||||
fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error> {
|
||||
Ok(interpreter::ExecutionParams::with_external(
|
||||
externals_name.into(),
|
||||
Arc::new(
|
||||
interpreter::env_native_module(
|
||||
self.module(externals_name).ok_or(DummyUserError)?,
|
||||
externals.into_user_defined_elements()
|
||||
)?
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user