From 04769eab3f7758c34c8ba591a0b9fbafd14e506d Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 29 Dec 2022 19:20:33 +0200 Subject: [PATCH 01/14] macro to expand traits for host functions documentation --- frame/contracts/proc-macro/src/lib.rs | 63 ++++++++++++++++++++++++--- frame/contracts/src/wasm/runtime.rs | 2 +- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index ded5880a2df4d..c296855407fd1 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -30,9 +30,9 @@ use alloc::{ vec::Vec, }; use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{TokenStream as TokenStream2, Span}; use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, FnArg, Ident}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, FnArg, Ident, punctuated::Punctuated, token::Comma}; /// This derives `Debug` for a struct where each field must be of some numeric type. /// It interprets each field as its represents some weight and formats it as times so that @@ -383,16 +383,64 @@ fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { matches!(*pat.ty, syn::Type::Infer(_)) } +/// Expands documentation +fn expand_docs(def: &mut EnvDef) -> TokenStream2 { + let mut modules = def.host_funcs.iter().map(|f| { + f.module.clone() + }).collect::>(); + + modules.sort(); + modules.dedup(); + + let doc_selector = |a: &syn::Attribute| a.path.is_ident("doc"); + + let docs = modules.iter().map(|m| { + let funcs = def.host_funcs.iter_mut().map(|f| { + if *m == f.module { + // Remove auxiliary args: `ctx: _` and `memory: _` + let args = f.item.sig.inputs.iter().skip(2).map(|p| p.clone()).collect::>(); + f.item.sig.inputs = args; + let func_decl = f.item.sig.to_token_stream(); + let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { + let docs = d.to_token_stream(); + quote! { #docs } + }); + quote! { + #( #func_docs )* + #func_decl; + } + } else { + quote! { } + } + }); + + let mut name = String::from("Docs_"); + name.push_str(m); + let module = Ident::new(&name, Span::call_site()); + + quote! { + trait #module { + #( #funcs )* + } + } + }); + quote! { + #( #docs )* + } +} + /// Expands environment definiton. /// Should generate source code for: /// - implementations of the host functions to be added to the wasm runtime environment (see /// `expand_impls()`). -fn expand_env(def: &mut EnvDef) -> TokenStream2 { +fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { let impls = expand_impls(def); + let docs = docs.then_some(expand_docs(def)).unwrap_or(TokenStream2::new()); quote! { pub struct Env; #impls + #docs } } @@ -581,8 +629,10 @@ fn expand_functions( /// when only checking whether a code can be instantiated without actually executing any code. #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { - if !attr.is_empty() { - let msg = "Invalid `define_env` attribute macro: expected no attributes: `#[define_env]`."; + if !attr.is_empty() && !(attr.to_string() == "doc".to_string()) { + let msg = r#"Invalid `define_env` attribute macro: expected either no attributes or a single `doc` attibute: + - `#[define_env]` + - `#[define_env(doc)]`"#; let span = TokenStream2::from(attr).span(); return syn::Error::new(span, msg).to_compile_error().into() } @@ -590,7 +640,8 @@ pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { let item = syn::parse_macro_input!(item as syn::ItemMod); match EnvDef::try_from(item) { - Ok(mut def) => expand_env(&mut def).into(), + Ok(mut def) => + expand_env(&mut def, !attr.is_empty()).into(), Err(e) => e.to_compile_error().into(), } } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 7d67adc3ded33..d1e8380d4760c 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env] +#[define_env(doc)] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From e7f6b7896d8fd5c00e2c559d3e0b4b1483e7930a Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 29 Dec 2022 19:52:49 +0200 Subject: [PATCH 02/14] other way: same Doc trait in seal modules --- frame/contracts/proc-macro/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c296855407fd1..c7abd5d3e2dd2 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -398,8 +398,7 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let funcs = def.host_funcs.iter_mut().map(|f| { if *m == f.module { // Remove auxiliary args: `ctx: _` and `memory: _` - let args = f.item.sig.inputs.iter().skip(2).map(|p| p.clone()).collect::>(); - f.item.sig.inputs = args; + f.item.sig.inputs = f.item.sig.inputs.iter().skip(2).map(|p| p.clone()).collect::>(); let func_decl = f.item.sig.to_token_stream(); let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { let docs = d.to_token_stream(); @@ -414,19 +413,20 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { } }); - let mut name = String::from("Docs_"); - name.push_str(m); - let module = Ident::new(&name, Span::call_site()); + let module = Ident::new(m, Span::call_site()); quote! { - trait #module { - #( #funcs )* + pub mod #module { + use crate::wasm::runtime::{TrapReason, ReturnCode}; + pub trait Docs { + #( #funcs )* + } } } }); quote! { #( #docs )* - } + } } /// Expands environment definiton. From 606d58dc38ac172189ebb015642f3905bdd10ec6 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 29 Dec 2022 20:36:15 +0200 Subject: [PATCH 03/14] added docs for macro, and remove `doc` attribute --- frame/contracts/proc-macro/src/lib.rs | 27 +++++++++++++++++++-------- frame/contracts/src/wasm/runtime.rs | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c7abd5d3e2dd2..dd11e3eabbaa5 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -383,17 +383,15 @@ fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { matches!(*pat.ty, syn::Type::Infer(_)) } -/// Expands documentation +/// Expands documentation for host functions. fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let mut modules = def.host_funcs.iter().map(|f| { f.module.clone() }).collect::>(); - modules.sort(); modules.dedup(); let doc_selector = |a: &syn::Attribute| a.path.is_ident("doc"); - let docs = modules.iter().map(|m| { let funcs = def.host_funcs.iter_mut().map(|f| { if *m == f.module { @@ -416,11 +414,13 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let module = Ident::new(m, Span::call_site()); quote! { - pub mod #module { - use crate::wasm::runtime::{TrapReason, ReturnCode}; - pub trait Docs { - #( #funcs )* - } + /// Documentation for the seal module host functions. + mod #module { + use crate::wasm::runtime::{TrapReason, ReturnCode}; + /// Dumb trait for generating host functions docs. + trait Docs { + #( #funcs )* + } } } }); @@ -627,6 +627,17 @@ fn expand_functions( /// /// The implementation on `()` can be used in places where no `Ext` exists, yet. This is useful /// when only checking whether a code can be instantiated without actually executing any code. +/// +/// # Generating Documentation +/// +/// Passing `doc` attribute to the macro (like `#[define_env(doc)]`) will make it also expand additional `pallet_contracts::wasm::runtime::seal0`, `pallet_contracts::wasm::runtime::seal1`, `...` modules +/// each having its `Doc` trait containing methods holding documentation for every defined host function. +/// +/// To build up these docs, run: +/// +/// ```nocompile +/// cargo doc --no-deps --document-private-items +/// ``` #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { if !attr.is_empty() && !(attr.to_string() == "doc".to_string()) { diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index d1e8380d4760c..7d67adc3ded33 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env(doc)] +#[define_env] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From 6cf881d33e619368b2a2d104bffa8cd5f7e7213e Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 29 Dec 2022 22:02:31 +0200 Subject: [PATCH 04/14] fmt --- frame/contracts/proc-macro/src/lib.rs | 37 +++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index dd11e3eabbaa5..1cd35bbff1cd6 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -30,9 +30,12 @@ use alloc::{ vec::Vec, }; use proc_macro::TokenStream; -use proc_macro2::{TokenStream as TokenStream2, Span}; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, FnArg, Ident, punctuated::Punctuated, token::Comma}; +use syn::{ + parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DeriveInput, + FnArg, Ident, +}; /// This derives `Debug` for a struct where each field must be of some numeric type. /// It interprets each field as its represents some weight and formats it as times so that @@ -385,9 +388,7 @@ fn is_valid_special_arg(idx: usize, arg: &FnArg) -> bool { /// Expands documentation for host functions. fn expand_docs(def: &mut EnvDef) -> TokenStream2 { - let mut modules = def.host_funcs.iter().map(|f| { - f.module.clone() - }).collect::>(); + let mut modules = def.host_funcs.iter().map(|f| f.module.clone()).collect::>(); modules.sort(); modules.dedup(); @@ -396,7 +397,14 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let funcs = def.host_funcs.iter_mut().map(|f| { if *m == f.module { // Remove auxiliary args: `ctx: _` and `memory: _` - f.item.sig.inputs = f.item.sig.inputs.iter().skip(2).map(|p| p.clone()).collect::>(); + f.item.sig.inputs = f + .item + .sig + .inputs + .iter() + .skip(2) + .map(|p| p.clone()) + .collect::>(); let func_decl = f.item.sig.to_token_stream(); let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { let docs = d.to_token_stream(); @@ -407,7 +415,7 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { #func_decl; } } else { - quote! { } + quote! {} } }); @@ -425,8 +433,8 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { } }); quote! { - #( #docs )* - } + #( #docs )* + } } /// Expands environment definiton. @@ -630,13 +638,15 @@ fn expand_functions( /// /// # Generating Documentation /// -/// Passing `doc` attribute to the macro (like `#[define_env(doc)]`) will make it also expand additional `pallet_contracts::wasm::runtime::seal0`, `pallet_contracts::wasm::runtime::seal1`, `...` modules -/// each having its `Doc` trait containing methods holding documentation for every defined host function. +/// Passing `doc` attribute to the macro (like `#[define_env(doc)]`) will make it also expand +/// additional `pallet_contracts::wasm::runtime::seal0`, `pallet_contracts::wasm::runtime::seal1`, +/// `...` modules each having its `Doc` trait containing methods holding documentation for every +/// defined host function. /// /// To build up these docs, run: /// /// ```nocompile -/// cargo doc --no-deps --document-private-items +/// cargo doc --no-deps --document-private-items /// ``` #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { @@ -651,8 +661,7 @@ pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { let item = syn::parse_macro_input!(item as syn::ItemMod); match EnvDef::try_from(item) { - Ok(mut def) => - expand_env(&mut def, !attr.is_empty()).into(), + Ok(mut def) => expand_env(&mut def, !attr.is_empty()).into(), Err(e) => e.to_compile_error().into(), } } From c558c5ecabb1042075e3c596705ffbce10b4b458 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Tue, 3 Jan 2023 14:59:36 +0200 Subject: [PATCH 05/14] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- frame/contracts/proc-macro/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 1cd35bbff1cd6..cccd3793b08db 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -422,11 +422,13 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let module = Ident::new(m, Span::call_site()); quote! { - /// Documentation for the seal module host functions. + /// Documentation of the API that can be imported by contracts setting `#module` as module + /// name for an import definition. mod #module { use crate::wasm::runtime::{TrapReason, ReturnCode}; - /// Dumb trait for generating host functions docs. - trait Docs { + /// Every function in this trait represents one function that can be imported by a contract. + /// The functions identifier is to be set as the name in the import definition. + trait Api { #( #funcs )* } } From cd2007b517c0515bb7c388e90989b39b81de7475 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 17:03:36 +0200 Subject: [PATCH 06/14] make docs to be generated into re-exported `api_doc` module; fix unrelated elder docs; --- frame/contracts/proc-macro/src/lib.rs | 22 ++++++++++++++++------ frame/contracts/src/exec.rs | 2 +- frame/contracts/src/lib.rs | 1 + frame/contracts/src/wasm/mod.rs | 7 +++++-- frame/contracts/src/wasm/runtime.rs | 8 ++++---- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index cccd3793b08db..da43bd771c1a4 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -420,22 +420,32 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { }); let module = Ident::new(m, Span::call_site()); + let module_doc = format!("Documentation of the API available to contracts by importing `{}` module.", module); quote! { - /// Documentation of the API that can be imported by contracts setting `#module` as module - /// name for an import definition. - mod #module { + #[doc = #module_doc] + pub mod #module { use crate::wasm::runtime::{TrapReason, ReturnCode}; /// Every function in this trait represents one function that can be imported by a contract. - /// The functions identifier is to be set as the name in the import definition. - trait Api { + /// The function's identifier is to be set as the name in the import definition. + pub trait Api { #( #funcs )* } } } }); quote! { - #( #docs )* + pub use docs as api_doc; + /// Contains the documentation of the API available to contracts. + /// + /// This module is not meant to be used by code. It is meant to be consumed by humans through rustdoc. + /// + /// Every function described in this module's sub module's traits uses this sub module's identifier + /// as its imported module name. The identifier of the function is the function's imported name. + /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). + pub mod docs { + #( #docs )* + } } } diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index df1dd24856a8a..8ae70b804d69b 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -462,7 +462,7 @@ enum FrameArgs<'a, T: Config, E> { /// If `None` the contract info needs to be reloaded from storage. cached_info: Option>, /// This frame was created by `seal_delegate_call` and hence uses different code than - /// what is stored at [`Self::dest`]. Its caller ([`Frame::delegated_caller`]) is the + /// what is stored at [`Self::Call::dest`]. Its caller ([`DelegatedCall::caller`]) is the /// account which called the caller contract delegated_call: Option>, }, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 778ceec961500..8e1bf63993b2f 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -92,6 +92,7 @@ mod schedule; mod storage; mod wasm; +pub use wasm::api_doc; pub mod chain_extension; pub mod weights; diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 553bae59e78b9..d02f695820a81 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -28,6 +28,8 @@ pub use crate::wasm::{ prepare::TryInstantiate, runtime::{CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, }; +pub use crate::wasm::runtime::api_doc; + use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter, @@ -144,7 +146,7 @@ impl PrefabWasmModule { /// Create the module by checking and instrumenting `original_code`. /// /// This does **not** store the module. For this one need to either call [`Self::store`] - /// or [`::execute`]. + /// or [`::execute`][`Executable::execute`]. pub fn from_code( original_code: Vec, schedule: &Schedule, @@ -164,7 +166,8 @@ impl PrefabWasmModule { /// Store the code without instantiating it. /// - /// Otherwise the code is stored when [`::execute`] is called. + /// Otherwise the code is stored when [`::execute`][`Executable::execute`] + /// is called. pub fn store(self) -> DispatchResult { code_cache::store(self, false) } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 7d67adc3ded33..61bb8966dbc80 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -52,7 +52,7 @@ pub trait Environment { /// Type of a storage key. #[allow(dead_code)] enum KeyType { - /// Deprecated fix sized key [0;32]. + /// Deprecated fix sized key `[u8;32]`. Fix, /// Variable sized key used in transparent hashing, /// cannot be larger than MaxStorageKeyLen. @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env] +#[define_env(doc)] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// @@ -1323,7 +1323,7 @@ pub mod env { /// /// # Parameters /// - /// - flags: See [`CallFlags`] for a documenation of the supported flags. + /// - flags: See `crate::wasm::runtime::CallFlags` for a documenation of the supported flags. /// - callee_ptr: a pointer to the address of the callee contract. Should be decodable as an /// `T::AccountId`. Traps otherwise. /// - gas: how much gas to devote to the execution. @@ -1377,7 +1377,7 @@ pub mod env { /// /// # Parameters /// - /// - flags: See [`CallFlags`] for a documentation of the supported flags. + /// - flags: See `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. /// - code_hash: a pointer to the hash of the code to be called. /// - input_data_ptr: a pointer to a buffer to be used as input data to the callee. /// - input_data_len: length of the input data buffer. From ce9031b18f94c6527cf642180e3988f093485cd1 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 17:23:25 +0200 Subject: [PATCH 07/14] make it compile without `doc` attr passed to macro --- frame/contracts/proc-macro/src/lib.rs | 38 ++++++++++++++++----------- frame/contracts/src/wasm/mod.rs | 3 +-- frame/contracts/src/wasm/runtime.rs | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index da43bd771c1a4..8c86798d2d5cc 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -420,7 +420,10 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { }); let module = Ident::new(m, Span::call_site()); - let module_doc = format!("Documentation of the API available to contracts by importing `{}` module.", module); + let module_doc = format!( + "Documentation of the API available to contracts by importing `{}` module.", + module + ); quote! { #[doc = #module_doc] @@ -435,17 +438,7 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { } }); quote! { - pub use docs as api_doc; - /// Contains the documentation of the API available to contracts. - /// - /// This module is not meant to be used by code. It is meant to be consumed by humans through rustdoc. - /// - /// Every function described in this module's sub module's traits uses this sub module's identifier - /// as its imported module name. The identifier of the function is the function's imported name. - /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). - pub mod docs { #( #docs )* - } } } @@ -460,7 +453,20 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { quote! { pub struct Env; #impls - #docs + pub use docs as api_doc; + /// Contains the documentation of the API available to contracts. + /// + /// In order to generate this documentation, pass `doc` attribute to the [`#[define_env]`][`macro@define_env`] macro: + /// `#[define_env(doc)]`, and then run `cargo doc --no-deps`. + /// + /// This module is not meant to be used by any code. Rather, it is meant to be consumed by humans through rustdoc. + /// + /// Every function described in this module's sub module's traits uses this sub module's identifier + /// as its imported module name. The identifier of the function is the function's imported name. + /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). + pub mod docs { + #docs + } } } @@ -651,14 +657,14 @@ fn expand_functions( /// # Generating Documentation /// /// Passing `doc` attribute to the macro (like `#[define_env(doc)]`) will make it also expand -/// additional `pallet_contracts::wasm::runtime::seal0`, `pallet_contracts::wasm::runtime::seal1`, -/// `...` modules each having its `Doc` trait containing methods holding documentation for every -/// defined host function. +/// additional `pallet_contracts::api_doc::seal0`, `pallet_contracts::api_doc::seal1`, +/// `...` modules each having its `Api` trait containing functions holding documentation for every +/// host function defined by the macro. /// /// To build up these docs, run: /// /// ```nocompile -/// cargo doc --no-deps --document-private-items +/// cargo doc --no-deps /// ``` #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index d02f695820a81..af0a440c68a3c 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -26,9 +26,8 @@ mod runtime; pub use crate::wasm::code_cache::reinstrument; pub use crate::wasm::{ prepare::TryInstantiate, - runtime::{CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, + runtime::{api_doc, CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, }; -pub use crate::wasm::runtime::api_doc; use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 61bb8966dbc80..e7e7e5d7ed106 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env(doc)] +#[define_env] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From dced64cdb1feafb7b1a33a3c24513cdb5d098ea9 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 19:19:12 +0200 Subject: [PATCH 08/14] make alias functions indicated explicitly in docs --- frame/contracts/proc-macro/src/lib.rs | 52 ++++++++++++++++++--------- frame/contracts/src/wasm/runtime.rs | 2 +- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 8c86798d2d5cc..ccf96a44b8037 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -161,6 +161,7 @@ struct HostFn { name: String, returns: HostFnReturn, is_stable: bool, + alias_to: String, } enum HostFnReturn { @@ -190,7 +191,7 @@ impl ToTokens for HostFn { } impl HostFn { - pub fn try_from(item: syn::ItemFn) -> syn::Result { + pub fn try_from(mut item: syn::ItemFn) -> syn::Result { let err = |span, msg| { let msg = format!("Invalid host function definition. {}", msg); syn::Error::new(span, msg) @@ -201,10 +202,10 @@ impl HostFn { "only #[version()], #[unstable] and #[prefixed_alias] attributes are allowed."; let span = item.span(); 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(); + attrs.retain(|a| !a.path.is_ident("doc")); let mut maybe_module = None; let mut is_stable = true; + let mut alias_to = String::new(); while let Some(attr) = attrs.pop() { let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { @@ -222,9 +223,17 @@ impl HostFn { } is_stable = false; }, + "prefixed_alias" => { + alias_to = item.sig.ident.to_string(); + item.sig.ident = syn::Ident::new( + &format!("seal_{}", &item.sig.ident.to_string()), + item.sig.ident.span(), + ); + }, _ => return Err(err(span, msg)), } } + let name = item.sig.ident.to_string(); // process arguments: The first and second arg are treated differently (ctx, memory) // they must exist and be `ctx: _` and `memory: _`. @@ -320,6 +329,7 @@ impl HostFn { name, returns, is_stable, + alias_to, }) }, _ => Err(err(span, &msg)), @@ -351,19 +361,15 @@ impl EnvDef { .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() .filter_map(extract_fn) + .map(|mut i| { + i.attrs.retain(|i| !selector(i)); + i + }) .map(|i| HostFn::try_from(i)) .chain(aliases) .collect::, _>>()?; @@ -406,12 +412,21 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { .map(|p| p.clone()) .collect::>(); let func_decl = f.item.sig.to_token_stream(); - let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { - let docs = d.to_token_stream(); - quote! { #docs } - }); + let func_doc = if f.alias_to.is_empty() { + let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { + let docs = d.to_token_stream(); + quote! { #docs } + }); + quote! { #( #func_docs )* } + } else { + let alias_doc = format!( + "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatibile prefixed identifier.", + f.alias_to + ); + quote! { #[doc = #alias_doc] } + }; quote! { - #( #func_docs )* + #func_doc #func_decl; } } else { @@ -429,8 +444,11 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { #[doc = #module_doc] pub mod #module { use crate::wasm::runtime::{TrapReason, ReturnCode}; - /// Every function in this trait represents one function that can be imported by a contract. + /// Every function in this trait represents (at least) one function that can be imported by a contract. + /// /// The function's identifier is to be set as the name in the import definition. + /// Where it is specifically indicated, an _alias_ function having `seal_`-prefixed identifier and + /// just the same signature and body, is also available (for backwards-compatibility purposes). pub trait Api { #( #funcs )* } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index e7e7e5d7ed106..1f7d77f249bc2 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -1606,7 +1606,7 @@ pub mod env { /// This function never returns as it stops execution of the caller. /// This is the only way to return a data buffer to the caller. Returning from /// execution without calling this function is equivalent to calling: - /// ``` + /// ```nocompile /// seal_return(0, 0, 0); /// ``` /// From 98a6c63e155c6eb190029bff3588d2ef8d5e3d19 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 19:33:30 +0200 Subject: [PATCH 09/14] tidy up docs --- frame/contracts/proc-macro/src/lib.rs | 8 +- frame/contracts/src/wasm/runtime.rs | 182 +++++++++++++------------- 2 files changed, 97 insertions(+), 93 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index ccf96a44b8037..5f99b468106a6 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -420,7 +420,7 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { quote! { #( #func_docs )* } } else { let alias_doc = format!( - "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatibile prefixed identifier.", + "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.", f.alias_to ); quote! { #[doc = #alias_doc] } @@ -436,7 +436,7 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let module = Ident::new(m, Span::call_site()); let module_doc = format!( - "Documentation of the API available to contracts by importing `{}` module.", + "Documentation of the API available to contracts by importing `{}` WASM module.", module ); @@ -475,7 +475,7 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { /// Contains the documentation of the API available to contracts. /// /// In order to generate this documentation, pass `doc` attribute to the [`#[define_env]`][`macro@define_env`] macro: - /// `#[define_env(doc)]`, and then run `cargo doc --no-deps`. + /// `#[define_env(doc)]`, and then run `cargo doc`. /// /// This module is not meant to be used by any code. Rather, it is meant to be consumed by humans through rustdoc. /// @@ -682,7 +682,7 @@ fn expand_functions( /// To build up these docs, run: /// /// ```nocompile -/// cargo doc --no-deps +/// cargo doc /// ``` #[proc_macro_attribute] pub fn define_env(attr: TokenStream, item: TokenStream) -> TokenStream { diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 1f7d77f249bc2..053d70f5822ec 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,14 +996,14 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env] +#[define_env(doc)] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// /// NOTE: This is a implementation defined call and is NOT a part of the public API. /// This call is supposed to be called only by instrumentation injected code. /// - /// - amount: How much gas is used. + /// - `amount`: How much gas is used. fn gas(ctx: _, _memory: _, amount: u64) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; Ok(()) @@ -1011,8 +1011,8 @@ pub mod env { /// Set the value at the given key in the contract storage. /// - /// Equivalent to the newer version of `seal_set_storage` with the exception of the return - /// type. Still a valid thing to call when not interested in the return value. + /// Equivalent to the newer version [`super::seal1::Api::set_storage`] with the exception of the + /// return type. Still a valid thing to call when not interested in the return value. #[prefixed_alias] fn set_storage( ctx: _, @@ -1085,8 +1085,8 @@ pub mod env { /// Clear the value at the given key in the contract storage. /// - /// Equivalent to the newer version of `seal_clear_storage` with the exception of the return - /// type. Still a valid thing to call when not interested in the return value. + /// Equivalent to the newer version [`super::seal1::Api::clear_storage`] with the exception of + /// the return type. Still a valid thing to call when not interested in the return value. #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(memory, KeyType::Fix, key_ptr).map(|_| ()) @@ -1152,7 +1152,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::KeyNotFound` + /// - `ReturnCode::KeyNotFound` #[version(1)] #[prefixed_alias] fn get_storage( @@ -1215,7 +1215,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::KeyNotFound` + /// - `ReturnCode::KeyNotFound` #[prefixed_alias] fn take_storage( ctx: _, @@ -1245,16 +1245,16 @@ pub mod env { /// /// # Parameters /// - /// - account_ptr: a pointer to the address of the beneficiary account Should be decodable as an - /// `T::AccountId`. Traps otherwise. - /// - account_len: length of the address buffer. - /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable - /// as a `T::Balance`. Traps otherwise. - /// - value_len: length of the value buffer. + /// - `account_ptr`: a pointer to the address of the beneficiary account Should be decodable as + /// an `T::AccountId`. Traps otherwise. + /// - `account_len`: length of the address buffer. + /// - `value_ptr`: a pointer to the buffer with value, how much value to send. Should be + /// decodable as a `T::Balance`. Traps otherwise. + /// - `value_len`: length of the value buffer. /// /// # Errors /// - /// `ReturnCode::TransferFailed` + /// - `ReturnCode::TransferFailed` #[prefixed_alias] fn transfer( ctx: _, @@ -1288,8 +1288,9 @@ pub mod env { /// # Note /// /// The values `_callee_len` and `_value_len` are ignored because the encoded sizes - /// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards - /// compatibility. Consider switching to the newest version of this function. + /// of those types are fixed through + /// [`codec::MaxEncodedLen`]. The fields exist + /// for backwards compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn call( ctx: _, @@ -1323,16 +1324,16 @@ pub mod env { /// /// # Parameters /// - /// - flags: See `crate::wasm::runtime::CallFlags` for a documenation of the supported flags. - /// - callee_ptr: a pointer to the address of the callee contract. Should be decodable as an + /// - `flags`: See `crate::wasm::runtime::CallFlags` for a documenation of the supported flags. + /// - `callee_ptr`: a pointer to the address of the callee contract. Should be decodable as an /// `T::AccountId`. Traps otherwise. - /// - gas: how much gas to devote to the execution. - /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable - /// as a `T::Balance`. Traps otherwise. - /// - input_data_ptr: a pointer to a buffer to be used as input data to the callee. - /// - input_data_len: length of the input data buffer. - /// - output_ptr: a pointer where the output buffer is copied to. - /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// - `gas`: how much gas to devote to the execution. + /// - `value_ptr`: a pointer to the buffer with value, how much value to send. Should be + /// decodable as a `T::Balance`. Traps otherwise. + /// - `input_data_ptr`: a pointer to a buffer to be used as input data to the callee. + /// - `input_data_len`: length of the input data buffer. + /// - `output_ptr`: a pointer where the output buffer is copied to. + /// - `output_len_ptr`: in-out pointer to where the length of the buffer is read from and the /// actual length is written to. /// /// # Errors @@ -1340,10 +1341,10 @@ pub mod env { /// An error means that the call wasn't successful output buffer is returned unless /// stated otherwise. /// - /// `ReturnCode::CalleeReverted`: Output buffer is returned. - /// `ReturnCode::CalleeTrapped` - /// `ReturnCode::TransferFailed` - /// `ReturnCode::NotCallable` + /// - `ReturnCode::CalleeReverted`: Output buffer is returned. + /// - `ReturnCode::CalleeTrapped` + /// - `ReturnCode::TransferFailed` + /// - `ReturnCode::NotCallable` #[version(1)] #[prefixed_alias] fn call( @@ -1377,12 +1378,12 @@ pub mod env { /// /// # Parameters /// - /// - flags: See `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. - /// - code_hash: a pointer to the hash of the code to be called. - /// - input_data_ptr: a pointer to a buffer to be used as input data to the callee. - /// - input_data_len: length of the input data buffer. - /// - output_ptr: a pointer where the output buffer is copied to. - /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// - `flags`: see `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. + /// - `code_hash`: a pointer to the hash of the code to be called. + /// - `input_data_ptr`: a pointer to a buffer to be used as input data to the callee. + /// - `input_data_len`: length of the input data buffer. + /// - `output_ptr`: a pointer where the output buffer is copied to. + /// - `output_len_ptr`: in-out pointer to where the length of the buffer is read from and the /// actual length is written to. /// /// # Errors @@ -1390,9 +1391,9 @@ pub mod env { /// An error means that the call wasn't successful and no output buffer is returned unless /// stated otherwise. /// - /// `ReturnCode::CalleeReverted`: Output buffer is returned. - /// `ReturnCode::CalleeTrapped` - /// `ReturnCode::CodeNotFound` + /// - `ReturnCode::CalleeReverted`: Output buffer is returned. + /// - `ReturnCode::CalleeTrapped` + /// - `ReturnCode::CodeNotFound` #[prefixed_alias] fn delegate_call( ctx: _, @@ -1425,8 +1426,9 @@ pub mod env { /// # Note /// /// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes - /// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards - /// compatibility. Consider switching to the newest version of this function. + /// of those types are fixed through + /// [`codec::MaxEncodedLen`]. The fields exist + /// for backwards compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn instantiate( ctx: _, @@ -1474,20 +1476,20 @@ pub mod env { /// /// # Parameters /// - /// - code_hash_ptr: a pointer to the buffer that contains the initializer code. - /// - gas: how much gas to devote to the execution of the initializer code. - /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable - /// as a `T::Balance`. Traps otherwise. - /// - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code. - /// - input_data_len: length of the input data buffer. - /// - address_ptr: a pointer where the new account's address is copied to. - /// - address_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// - `code_hash_ptr`: a pointer to the buffer that contains the initializer code. + /// - `gas`: how much gas to devote to the execution of the initializer code. + /// - `value_ptr`: a pointer to the buffer with value, how much value to send. Should be + /// decodable as a `T::Balance`. Traps otherwise. + /// - `input_data_ptr`: a pointer to a buffer to be used as input data to the initializer code. + /// - `input_data_len`: length of the input data buffer. + /// - `address_ptr`: a pointer where the new account's address is copied to. + /// - `address_len_ptr`: in-out pointer to where the length of the buffer is read from and the /// actual length is written to. - /// - output_ptr: a pointer where the output buffer is copied to. - /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// - `output_ptr`: a pointer where the output buffer is copied to. + /// - `output_len_ptr`: in-out pointer to where the length of the buffer is read from and the /// actual length is written to. - /// - salt_ptr: Pointer to raw bytes used for address derivation. See `fn contract_address`. - /// - salt_len: length in bytes of the supplied salt. + /// - `salt_ptr`: Pointer to raw bytes used for address derivation. See `fn contract_address`. + /// - `salt_len`: length in bytes of the supplied salt. /// /// # Errors /// @@ -1497,10 +1499,10 @@ pub mod env { /// An error means that the account wasn't created and no address or output buffer /// is returned unless stated otherwise. /// - /// `ReturnCode::CalleeReverted`: Output buffer is returned. - /// `ReturnCode::CalleeTrapped` - /// `ReturnCode::TransferFailed` - /// `ReturnCode::CodeNotFound` + /// - `ReturnCode::CalleeReverted`: Output buffer is returned. + /// - `ReturnCode::CalleeTrapped` + /// - `ReturnCode::TransferFailed` + /// - `ReturnCode::CodeNotFound` #[version(1)] #[prefixed_alias] fn instantiate( @@ -1562,7 +1564,7 @@ pub mod env { /// execution of the destroyed contract is halted. Or it failed during the termination /// which is considered fatal and results in a trap + rollback. /// - /// - beneficiary_ptr: a pointer to the address of the beneficiary account where all where all + /// - `beneficiary_ptr`: a pointer to the address of the beneficiary account where all where all /// remaining funds of the caller are transferred. Should be decodable as an `T::AccountId`. /// Traps otherwise. /// @@ -1586,7 +1588,7 @@ pub mod env { /// /// # Note /// - /// This function traps if the input was previously forwarded by a `seal_call`. + /// This function traps if the input was previously forwarded by a [`call()`][`Self::call()`]. #[prefixed_alias] fn input(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::InputBase)?; @@ -1659,10 +1661,10 @@ pub mod env { /// /// # Parameters /// - /// - account_ptr: a pointer to the address of the beneficiary account Should be decodable as an - /// `T::AccountId`. Traps otherwise. + /// - `account_ptr`: a pointer to the address of the beneficiary account Should be decodable as + /// an `T::AccountId`. Traps otherwise. /// - /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). + /// Returned value is a `u32`-encoded boolean: (0 = false, 1 = true). #[prefixed_alias] fn is_contract(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::IsContract)?; @@ -1684,7 +1686,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::KeyNotFound` + /// - `ReturnCode::KeyNotFound` #[prefixed_alias] fn code_hash( ctx: _, @@ -1734,14 +1736,14 @@ pub mod env { /// Checks whether the caller of the current contract is the origin of the whole call stack. /// - /// Prefer this over `seal_is_contract` when checking whether your contract is being called by a - /// contract or a plain account. The reason is that it performs better since it does not need to - /// do any storage lookups. + /// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract + /// is being called by a contract or a plain account. The reason is that it performs better + /// since it does not need to do any storage lookups. /// - /// A return value of`true` indicates that this contract is being called by a plain account + /// A return value of `true` indicates that this contract is being called by a plain account /// and `false` indicates that the caller is another contract. /// - /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). + /// Returned value is a `u32`-encoded boolean: (`0 = false`, `1 = true`). #[prefixed_alias] fn caller_is_origin(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; @@ -1774,7 +1776,7 @@ pub mod env { /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// The data is encoded as T::Balance. + /// The data is encoded as `T::Balance`. /// /// # Note /// @@ -1822,14 +1824,14 @@ pub mod env { )?) } - /// Stores the **free* balance of the current account into the supplied buffer. + /// Stores the *free* balance of the current account into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// The data is encoded as T::Balance. + /// The data is encoded as `T::Balance`. #[prefixed_alias] fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; @@ -1846,11 +1848,11 @@ pub mod env { /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. - /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_len_ptr` must point to a `u32` value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// The data is encoded as T::Balance. + /// The data is encoded as `T::Balance`. #[prefixed_alias] fn value_transferred( ctx: _, @@ -1876,11 +1878,12 @@ pub mod env { /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// - /// The data is encoded as T::Hash. + /// The data is encoded as `T::Hash`. /// /// # Deprecation /// - /// This function is deprecated. Users should migrate to the version in the "seal1" module. + /// This function is deprecated. Users should migrate to the [`super::seal1::Api::random()`] + /// version. #[prefixed_alias] fn random( ctx: _, @@ -1972,7 +1975,7 @@ pub mod env { /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// - /// The data is encoded as T::Balance. + /// The data is encoded as `T::Balance`. #[prefixed_alias] fn minimum_balance( ctx: _, @@ -2000,7 +2003,7 @@ pub mod env { /// /// # Deprecation /// - /// There is no longer a tombstone deposit. This function always returns 0. + /// There is no longer a tombstone deposit. This function always returns `0`. #[prefixed_alias] fn tombstone_deposit( ctx: _, @@ -2067,11 +2070,12 @@ pub mod env { /// Deposit a contract event with the data buffer and optional list of topics. There is a limit /// on the maximum number of topics specified by `event_topics`. /// - /// - topics_ptr - a pointer to the buffer of topics encoded as `Vec`. The value of - /// this is ignored if `topics_len` is set to 0. The topics list can't contain duplicates. - /// - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector. - /// - data_ptr - a pointer to a raw data buffer which will saved along the event. - /// - data_len - the length of the data buffer. + /// - `topics_ptr`: a pointer to the buffer of topics encoded as `Vec`. The value of + /// this is ignored if `topics_len` is set to `0`. The topics list can't contain duplicates. + /// - `topics_len`: the length of the topics buffer. Pass 0 if you want to pass an empty + /// vector. + /// - `data_ptr`: a pointer to a raw data buffer which will saved along the event. + /// - `data_len`: the length of the data buffer. #[prefixed_alias] fn deposit_event( ctx: _, @@ -2466,7 +2470,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::EcdsaRecoverFailed` + /// - `ReturnCode::EcdsaRecoverFailed` #[prefixed_alias] fn ecdsa_recover( ctx: _, @@ -2513,8 +2517,8 @@ pub mod env { /// /// 3. If a contract calls into itself after changing its code the new call would use /// the new code. However, if the original caller panics after returning from the sub call it - /// would revert the changes made by `seal_set_code_hash` and the next caller would use - /// the old code. + /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next + /// caller would use the old code. /// /// # Parameters /// @@ -2522,7 +2526,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::CodeNotFound` + /// - `ReturnCode::CodeNotFound` #[prefixed_alias] fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; @@ -2552,7 +2556,7 @@ pub mod env { /// /// # Errors /// - /// `ReturnCode::EcdsaRecoverFailed` + /// - `ReturnCode::EcdsaRecoverFailed` #[prefixed_alias] fn ecdsa_to_eth_address( ctx: _, @@ -2578,7 +2582,7 @@ pub mod env { /// /// # Return Value /// - /// Returns 0 when there is no reentrancy. + /// Returns `0` when there is no reentrancy. #[unstable] fn reentrance_count(ctx: _, memory: _) -> Result { ctx.charge_gas(RuntimeCosts::ReentrantCount)?; @@ -2594,7 +2598,7 @@ pub mod env { /// /// # Return Value /// - /// Returns 0 when the contract does not exist on the call stack. + /// Returns `0` when the contract does not exist on the call stack. #[unstable] fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?; From ab369423b04eab8448462bdbd1350c5e2e9f0b13 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 21:04:32 +0200 Subject: [PATCH 10/14] refactored a bit --- frame/contracts/proc-macro/src/lib.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 5f99b468106a6..175c81d3a075f 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -161,7 +161,7 @@ struct HostFn { name: String, returns: HostFnReturn, is_stable: bool, - alias_to: String, + alias_to: Option, } enum HostFnReturn { @@ -205,7 +205,7 @@ impl HostFn { attrs.retain(|a| !a.path.is_ident("doc")); let mut maybe_module = None; let mut is_stable = true; - let mut alias_to = String::new(); + let mut alias_to = None; while let Some(attr) = attrs.pop() { let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { @@ -224,7 +224,7 @@ impl HostFn { is_stable = false; }, "prefixed_alias" => { - alias_to = item.sig.ident.to_string(); + alias_to = Some(item.sig.ident.to_string()); item.sig.ident = syn::Ident::new( &format!("seal_{}", &item.sig.ident.to_string()), item.sig.ident.span(), @@ -412,18 +412,19 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { .map(|p| p.clone()) .collect::>(); let func_decl = f.item.sig.to_token_stream(); - let func_doc = if f.alias_to.is_empty() { + let func_doc = if let Some(origin_fn) = &f.alias_to { + let alias_doc = format!( + "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.", + origin_fn, + ); + quote! { #[doc = #alias_doc] } + + } else { let func_docs = f.item.attrs.iter().filter(|a| doc_selector(a)).map(|d| { let docs = d.to_token_stream(); quote! { #docs } }); quote! { #( #func_docs )* } - } else { - let alias_doc = format!( - "This is just an alias function to [`{0}()`][`Self::{0}`] with backwards-compatible prefixed identifier.", - f.alias_to - ); - quote! { #[doc = #alias_doc] } }; quote! { #func_doc From f8def8c6392fd2014a7afc3bd233d05862fdb61f Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 21:26:27 +0200 Subject: [PATCH 11/14] macro to auto-add doc warning for unstable functions --- frame/contracts/proc-macro/src/lib.rs | 13 ++++++++++++- frame/contracts/src/wasm/runtime.rs | 5 ----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 175c81d3a075f..a70a3c944be13 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -424,7 +424,18 @@ fn expand_docs(def: &mut EnvDef) -> TokenStream2 { let docs = d.to_token_stream(); quote! { #docs } }); - quote! { #( #func_docs )* } + let unstable_notice = if !f.is_stable { + let warning = "\n # Unstable\n\n \ + This function is unstable and it is a subject to change (or removal) in the future.\n \ + Do not deploy a contract using it to a production chain."; + quote! { #[doc = #warning] } + } else { + quote! {} + }; + quote! { + #( #func_docs )* + #unstable_notice + } }; quote! { #func_doc diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 053d70f5822ec..098f8d280e6a9 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2426,11 +2426,6 @@ pub mod env { /// - Provide functionality **exclusively** to contracts. /// - Provide custom weights. /// - Avoid the need to keep the `Call` data structure stable. - /// - /// # Unstable - /// - /// This function is unstable and subject to change (or removal) in the future. Do not - /// deploy a contract using it to a production chain. #[unstable] #[prefixed_alias] fn call_runtime( From f118f7428c28ee7f7320d617ad716a02cf6c1c84 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Tue, 3 Jan 2023 21:39:35 +0200 Subject: [PATCH 12/14] invoke macro with no doc generation by default --- frame/contracts/src/wasm/runtime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 098f8d280e6a9..1bb14a650c871 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env(doc)] +#[define_env] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From fff947e7480dbf3d1bbfea167b79b84a33beefff Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Wed, 4 Jan 2023 15:43:29 +0200 Subject: [PATCH 13/14] addressed review comments --- frame/contracts/proc-macro/src/lib.rs | 3 +-- frame/contracts/src/lib.rs | 3 +-- frame/contracts/src/wasm/runtime.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index a70a3c944be13..97540d3aaa3a4 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -483,7 +483,6 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { quote! { pub struct Env; #impls - pub use docs as api_doc; /// Contains the documentation of the API available to contracts. /// /// In order to generate this documentation, pass `doc` attribute to the [`#[define_env]`][`macro@define_env`] macro: @@ -494,7 +493,7 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { /// Every function described in this module's sub module's traits uses this sub module's identifier /// as its imported module name. The identifier of the function is the function's imported name. /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). - pub mod docs { + pub mod api_doc { #docs } } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 8e1bf63993b2f..cdff4021d0ff4 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -92,7 +92,6 @@ mod schedule; mod storage; mod wasm; -pub use wasm::api_doc; pub mod chain_extension; pub mod weights; @@ -132,7 +131,7 @@ pub use crate::{ migration::Migration, pallet::*, schedule::{HostFnWeights, InstructionWeights, Limits, Schedule}, - wasm::Determinism, + wasm::{api_doc, Determinism}, }; type CodeHash = ::Hash; diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 1bb14a650c871..098f8d280e6a9 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -996,7 +996,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env] +#[define_env(doc)] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From 1af74cc74a7171c73f5a05f6ef3305e304ea617c Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 5 Jan 2023 15:15:31 +0200 Subject: [PATCH 14/14] hide api_doc module behind cfg(doc) --- frame/contracts/proc-macro/src/lib.rs | 1 + frame/contracts/src/lib.rs | 5 ++++- frame/contracts/src/wasm/mod.rs | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 97540d3aaa3a4..a31d39f47e215 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -493,6 +493,7 @@ fn expand_env(def: &mut EnvDef, docs: bool) -> TokenStream2 { /// Every function described in this module's sub module's traits uses this sub module's identifier /// as its imported module name. The identifier of the function is the function's imported name. /// According to the [WASM spec of imports](https://webassembly.github.io/spec/core/text/modules.html#text-import). + #[cfg(doc)] pub mod api_doc { #docs } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index cdff4021d0ff4..9df790e09a1e8 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -131,9 +131,12 @@ pub use crate::{ migration::Migration, pallet::*, schedule::{HostFnWeights, InstructionWeights, Limits, Schedule}, - wasm::{api_doc, Determinism}, + wasm::Determinism, }; +#[cfg(doc)] +pub use crate::wasm::api_doc; + type CodeHash = ::Hash; type TrieId = BoundedVec>; type BalanceOf = diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index af0a440c68a3c..f1d0ccc7c216b 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -26,9 +26,12 @@ mod runtime; pub use crate::wasm::code_cache::reinstrument; pub use crate::wasm::{ prepare::TryInstantiate, - runtime::{api_doc, CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, + runtime::{CallFlags, Environment, ReturnCode, Runtime, RuntimeCosts}, }; +#[cfg(doc)] +pub use crate::wasm::runtime::api_doc; + use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::GasMeter,