sp_runtime_interface_proc_macro/runtime_interface/
bare_function_interface.rs1use crate::utils::{
33 create_exchangeable_host_function_ident, create_function_ident_with_version,
34 generate_crate_access, get_function_argument_names, get_function_arguments,
35 get_runtime_interface, RuntimeInterfaceFunction,
36};
37
38use syn::{
39 parse_quote, spanned::Spanned, FnArg, Ident, ItemTrait, Result, Signature, Token, TraitItemFn,
40};
41
42use proc_macro2::{Span, TokenStream};
43
44use quote::{quote, quote_spanned};
45
46use std::iter;
47
48pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool, tracing: bool) -> Result<TokenStream> {
51 let trait_name = &trait_def.ident;
52 let runtime_interface = get_runtime_interface(trait_def)?;
53
54 let token_stream: Result<TokenStream> = runtime_interface.latest_versions_to_call().try_fold(
56 TokenStream::new(),
57 |mut t, (latest_version, method)| {
58 t.extend(function_for_method(method, latest_version, is_wasm_only)?);
59 Ok(t)
60 },
61 );
62
63 let result: Result<TokenStream> =
65 runtime_interface
66 .all_versions()
67 .try_fold(token_stream?, |mut t, (version, method)| {
68 t.extend(function_std_impl(trait_name, method, version, is_wasm_only, tracing)?);
69 Ok(t)
70 });
71
72 result
73}
74
75fn function_for_method(
77 method: &RuntimeInterfaceFunction,
78 latest_version: u32,
79 is_wasm_only: bool,
80) -> Result<TokenStream> {
81 let std_impl =
82 if !is_wasm_only { function_std_latest_impl(method, latest_version)? } else { quote!() };
83
84 let no_std_impl = function_no_std_impl(method, is_wasm_only)?;
85
86 Ok(quote! {
87 #std_impl
88
89 #no_std_impl
90 })
91}
92
93fn function_no_std_impl(
95 method: &RuntimeInterfaceFunction,
96 is_wasm_only: bool,
97) -> Result<TokenStream> {
98 let function_name = &method.sig.ident;
99 let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident);
100 let args = get_function_arguments(&method.sig);
101 let arg_names = get_function_argument_names(&method.sig);
102 let return_value = if method.should_trap_on_return() {
103 syn::ReturnType::Type(
104 <Token![->]>::default(),
105 Box::new(syn::TypeNever { bang_token: <Token![!]>::default() }.into()),
106 )
107 } else {
108 method.sig.output.clone()
109 };
110 let maybe_unreachable = if method.should_trap_on_return() {
111 quote! {
112 ;
113 #[cfg(target_family = "wasm")]
114 { core::arch::wasm32::unreachable(); }
115
116 #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
117 unsafe { core::arch::asm!("unimp", options(noreturn)); }
118 }
119 } else {
120 quote! {}
121 };
122
123 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
124
125 let cfg_wasm_only = if is_wasm_only {
126 quote! { #[cfg(substrate_runtime)] }
127 } else {
128 quote! {}
129 };
130
131 Ok(quote! {
132 #cfg_wasm_only
133 #[cfg(not(feature = "std"))]
134 #( #attrs )*
135 pub fn #function_name( #( #args, )* ) #return_value {
136 #host_function_name.get()( #( #arg_names, )* )
138 #maybe_unreachable
139 }
140 })
141}
142
143fn function_std_latest_impl(method: &TraitItemFn, latest_version: u32) -> Result<TokenStream> {
147 let function_name = &method.sig.ident;
148 let args = get_function_arguments(&method.sig).map(FnArg::Typed);
149 let arg_names = get_function_argument_names(&method.sig).collect::<Vec<_>>();
150 let return_value = &method.sig.output;
151 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
152 let latest_function_name =
153 create_function_ident_with_version(&method.sig.ident, latest_version);
154
155 Ok(quote_spanned! { method.span() =>
156 #[cfg(feature = "std")]
157 #( #attrs )*
158 pub fn #function_name( #( #args, )* ) #return_value {
159 #latest_function_name(
160 #( #arg_names, )*
161 )
162 }
163 })
164}
165
166fn function_std_impl(
168 trait_name: &Ident,
169 method: &TraitItemFn,
170 version: u32,
171 is_wasm_only: bool,
172 tracing: bool,
173) -> Result<TokenStream> {
174 let function_name = create_function_ident_with_version(&method.sig.ident, version);
175 let function_name_str = function_name.to_string();
176
177 let crate_ = generate_crate_access();
178 let args = get_function_arguments(&method.sig).map(FnArg::Typed).chain(
179 iter::from_fn(|| {
181 if is_wasm_only {
182 Some(parse_quote!(
183 mut __function_context__: &mut dyn #crate_::sp_wasm_interface::FunctionContext
184 ))
185 } else {
186 None
187 }
188 })
189 .take(1),
190 );
191 let return_value = &method.sig.output;
192 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
193 let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only);
195 let call_to_trait = if !tracing {
196 call_to_trait
197 } else {
198 parse_quote!(
199 #crate_::sp_tracing::within_span! { #crate_::sp_tracing::trace_span!(#function_name_str);
200 #call_to_trait
201 }
202 )
203 };
204
205 Ok(quote_spanned! { method.span() =>
206 #[cfg(feature = "std")]
207 #( #attrs )*
208 fn #function_name( #( #args, )* ) #return_value {
209 #call_to_trait
210 }
211 })
212}
213
214fn generate_call_to_trait(
216 trait_name: &Ident,
217 method: &TraitItemFn,
218 version: u32,
219 is_wasm_only: bool,
220) -> TokenStream {
221 let crate_ = generate_crate_access();
222 let method_name = create_function_ident_with_version(&method.sig.ident, version);
223 let expect_msg =
224 format!("`{}` called outside of an Externalities-provided environment.", method_name);
225 let arg_names = get_function_argument_names(&method.sig);
226
227 if takes_self_argument(&method.sig) {
228 let instance = if is_wasm_only {
229 Ident::new("__function_context__", Span::call_site())
230 } else {
231 Ident::new("__externalities__", Span::call_site())
232 };
233
234 let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) );
235
236 if is_wasm_only {
237 quote_spanned! { method.span() => #impl_ }
238 } else {
239 quote_spanned! { method.span() =>
240 #crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg)
241 }
242 }
243 } else {
244 let impl_trait_name = if is_wasm_only {
246 quote!( #crate_::sp_wasm_interface::FunctionContext )
247 } else {
248 quote!( #crate_::Externalities )
249 };
250
251 quote_spanned! { method.span() =>
252 <&mut dyn #impl_trait_name as #trait_name>::#method_name(
253 #( #arg_names, )*
254 )
255 }
256 }
257}
258
259fn takes_self_argument(sig: &Signature) -> bool {
261 matches!(sig.inputs.first(), Some(FnArg::Receiver(_)))
262}