Skip to content

Commit

Permalink
[contracts] API host functions: remove seal_ name prefix + enable a…
Browse files Browse the repository at this point in the history
…liasing (#12126)

* works but ugly

* refactored + renamed host fns

* fixed tests

* fix benchmarks

* updated marco docs

* Update frame/contracts/proc-macro/src/lib.rs

Co-authored-by: Alexander Theißen <[email protected]>

* fix for the duplicated prefixed alias bug + test

* refactored a bit

* fix warnings + try to make macro rustdoc compile

* fmt after clearing

* examples update + nocompile

* add seal_ prefixes to unstable host functions

* updated after a review

Co-authored-by: Alexander Theißen <[email protected]>
  • Loading branch information
agryaznov and athei authored Sep 7, 2022
1 parent dcfaa41 commit 3f70bce
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 162 deletions.
4 changes: 2 additions & 2 deletions frame/contracts/fixtures/call_runtime.wat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;; This passes its input to `seal_call_runtime` and returns the return value to its caller.
(module
(import "__unstable__" "seal_call_runtime" (func $seal_call_runtime (param i32 i32) (result i32)))
(import "__unstable__" "call_runtime" (func $call_runtime (param i32 i32) (result i32)))
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
(import "env" "memory" (memory 1 1))
Expand All @@ -17,7 +17,7 @@
)
;; Just use the call passed as input and store result to memory
(i32.store (i32.const 0)
(call $seal_call_runtime
(call $call_runtime
(i32.const 4) ;; Pointer where the call is stored
(i32.load (i32.const 0)) ;; Size of the call
)
Expand Down
72 changes: 59 additions & 13 deletions frame/contracts/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,22 +174,16 @@ impl ToTokens for HostFn {
}

impl HostFn {
pub fn try_from(item: syn::Item) -> syn::Result<Self> {
pub fn try_from(item: syn::ItemFn) -> syn::Result<Self> {
let err = |span, msg| {
let msg = format!("Invalid host function definition. {}", msg);
syn::Error::new(span, msg)
};
let msg = "only #[version(<u8>)] or #[unstable] attribute is allowed.";
let span = item.span();
let item = match item {
syn::Item::Fn(i_fn) => Ok(i_fn),
_ => Err(err(span, msg)),
}?;

let mut attrs = item.attrs.clone();
attrs.retain(|a| !(a.path.is_ident("doc") || a.path.is_ident("prefixed_alias")));
let name = item.sig.ident.to_string();
let attrs: Vec<&syn::Attribute> =
item.attrs.iter().filter(|m| !m.path.is_ident("doc")).collect();

let module = match attrs.len() {
0 => Ok("seal0".to_string()),
1 => {
Expand Down Expand Up @@ -306,6 +300,7 @@ impl HostFn {
}
}
}

impl EnvDef {
pub fn try_from(item: syn::ItemMod) -> syn::Result<Self> {
let span = item.span();
Expand All @@ -316,9 +311,32 @@ impl EnvDef {
.ok_or(err("Invalid environment definition, expected `mod` to be inlined."))?
.1;

let extract_fn = |i: &syn::Item| match i {
syn::Item::Fn(i_fn) => Some(i_fn.clone()),
_ => None,
};

let selector = |a: &syn::Attribute| a.path.is_ident("prefixed_alias");

let aliases = items
.iter()
.filter_map(extract_fn)
.filter(|i| i.attrs.iter().any(selector))
.map(|mut i| {
i.attrs.retain(|i| !selector(i));
i.sig.ident = syn::Ident::new(
&format!("seal_{}", &i.sig.ident.to_string()),
i.sig.ident.span(),
);
i
})
.map(|i| HostFn::try_from(i));

let host_funcs = items
.iter()
.map(|i| HostFn::try_from(i.clone()))
.filter_map(extract_fn)
.map(|i| HostFn::try_from(i))
.chain(aliases)
.collect::<Result<Vec<_>, _>>()?;

Ok(Self { host_funcs })
Expand Down Expand Up @@ -484,7 +502,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream {
/// ```nocompile
/// #[define_env]
/// pub mod some_env {
/// fn some_host_fn(ctx: Runtime<E: Ext>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> {
/// fn some_host_fn(ctx: Runtime<E>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
/// }
Expand All @@ -499,17 +517,45 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream {
/// #[define_env]
/// pub mod some_env {
/// #[version(1)]
/// fn some_host_fn(ctx: Runtime<E: Ext>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnCode, TrapReason> {
/// fn some_host_fn(ctx: Runtime<E>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnCode, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
///
/// #[unstable]
/// fn some_host_fn(ctx: Runtime<E>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<u32, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
/// }
/// ```
///
/// In legacy versions of pallet_contracts, it was a naming convention that all host functions had
/// to be named with the `seal_` prefix. For the sake of backwards compatibility, each host function
/// now can get a such prefix-named alias function generated by marking it by the
/// `#[prefixed_alias]` attribute:
///
/// ## Example
///
/// ```nocompile
/// #[define_env]
/// pub mod some_env {
/// #[version(1)]
/// #[prefixed_alias]
/// fn some_host_fn(ctx: Runtime<E>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnCode, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
///
/// #[unstable]
/// fn some_host_fn(ctx: Runtime<E: Ext>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<u32, TrapReason> {
/// fn some_host_fn(ctx: Runtime<E>, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<u32, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
/// }
/// ```
///
/// In this example, the following host functions will be generated by the macro:
/// - `some_host_fn()` in module `seal1`,
/// - `seal_some_host_fn()` in module `seal1`,
/// - `some_host_fn()` in module `__unstable__`.
///
/// Only following return types are allowed for the host functions defined with the macro:
/// - `Result<(), TrapReason>`,
/// - `Result<ReturnCode, TrapReason>`,
Expand Down
22 changes: 11 additions & 11 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_set_storage",
name: "set_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -968,7 +968,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_set_storage",
name: "set_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1016,7 +1016,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_set_storage",
name: "set_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1068,7 +1068,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_clear_storage",
name: "clear_storage",
params: vec![ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1115,7 +1115,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_clear_storage",
name: "clear_storage",
params: vec![ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1163,7 +1163,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_get_storage",
name: "get_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1217,7 +1217,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_get_storage",
name: "get_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1272,7 +1272,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_contains_storage",
name: "contains_storage",
params: vec![ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1319,7 +1319,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_contains_storage",
name: "contains_storage",
params: vec![ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1367,7 +1367,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_take_storage",
name: "take_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down Expand Up @@ -1421,7 +1421,7 @@ benchmarks! {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
module: "__unstable__",
name: "seal_take_storage",
name: "take_storage",
params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
return_type: Some(ValueType::I32),
}],
Expand Down
Loading

0 comments on commit 3f70bce

Please sign in to comment.