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, host_inner_return_ty, pat_ty_to_host_inner, 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 should_trap_on_return = method.should_trap_on_return();
99 let mut method = (*method).clone();
100 crate::utils::unpack_inner_types_in_signature(&mut method.sig);
101
102 let function_name = &method.sig.ident;
103 let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident);
104 let args = get_function_arguments(&method.sig);
105 let arg_names = get_function_argument_names(&method.sig);
106 let return_value = if should_trap_on_return {
107 syn::ReturnType::Type(
108 <Token![->]>::default(),
109 Box::new(syn::TypeNever { bang_token: <Token![!]>::default() }.into()),
110 )
111 } else {
112 method.sig.output.clone()
113 };
114 let maybe_unreachable = if should_trap_on_return {
115 quote! {
116 ;
117 #[cfg(target_family = "wasm")]
118 { core::arch::wasm32::unreachable(); }
119
120 #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
121 unsafe { core::arch::asm!("unimp", options(noreturn)); }
122 }
123 } else {
124 quote! {}
125 };
126
127 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
128
129 let cfg_wasm_only = if is_wasm_only {
130 quote! { #[cfg(substrate_runtime)] }
131 } else {
132 quote! {}
133 };
134
135 Ok(quote! {
136 #cfg_wasm_only
137 #[cfg(substrate_runtime)]
138 #( #attrs )*
139 pub fn #function_name( #( #args, )* ) #return_value {
140 #host_function_name.get()( #( #arg_names, )* )
142 #maybe_unreachable
143 }
144 })
145}
146
147fn function_std_latest_impl(method: &TraitItemFn, latest_version: u32) -> Result<TokenStream> {
151 let function_name = &method.sig.ident;
152 let args = get_function_arguments(&method.sig).map(pat_ty_to_host_inner).map(FnArg::Typed);
153 let arg_names = get_function_argument_names(&method.sig).collect::<Vec<_>>();
154 let return_value = host_inner_return_ty(&method.sig.output);
155 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
156 let latest_function_name =
157 create_function_ident_with_version(&method.sig.ident, latest_version);
158
159 Ok(quote_spanned! { method.span() =>
160 #[cfg(not(substrate_runtime))]
161 #( #attrs )*
162 pub fn #function_name( #( #args, )* ) #return_value {
163 #latest_function_name(
164 #( #arg_names, )*
165 )
166 }
167 })
168}
169
170fn function_std_impl(
172 trait_name: &Ident,
173 method: &TraitItemFn,
174 version: u32,
175 is_wasm_only: bool,
176 tracing: bool,
177) -> Result<TokenStream> {
178 let function_name = create_function_ident_with_version(&method.sig.ident, version);
179 let function_name_str = function_name.to_string();
180
181 let crate_ = generate_crate_access();
182 let args = get_function_arguments(&method.sig)
183 .map(pat_ty_to_host_inner)
184 .map(FnArg::Typed)
185 .chain(
186 iter::from_fn(|| {
188 if is_wasm_only {
189 Some(parse_quote!(
190 mut __function_context__: &mut dyn #crate_::sp_wasm_interface::FunctionContext
191 ))
192 } else {
193 None
194 }
195 })
196 .take(1),
197 );
198 let return_value = host_inner_return_ty(&method.sig.output);
199 let attrs = method.attrs.iter().filter(|a| !a.path().is_ident("version"));
200 let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only);
202 let call_to_trait = if !tracing {
203 call_to_trait
204 } else {
205 parse_quote!(
206 #crate_::sp_tracing::within_span! { #crate_::sp_tracing::trace_span!(#function_name_str);
207 #call_to_trait
208 }
209 )
210 };
211
212 Ok(quote_spanned! { method.span() =>
213 #[cfg(not(substrate_runtime))]
214 #( #attrs )*
215 fn #function_name( #( #args, )* ) #return_value {
216 #call_to_trait
217 }
218 })
219}
220
221fn generate_call_to_trait(
223 trait_name: &Ident,
224 method: &TraitItemFn,
225 version: u32,
226 is_wasm_only: bool,
227) -> TokenStream {
228 let crate_ = generate_crate_access();
229 let method_name = create_function_ident_with_version(&method.sig.ident, version);
230 let expect_msg =
231 format!("`{}` called outside of an Externalities-provided environment.", method_name);
232 let arg_names = get_function_argument_names(&method.sig);
233
234 if takes_self_argument(&method.sig) {
235 let instance = if is_wasm_only {
236 Ident::new("__function_context__", Span::call_site())
237 } else {
238 Ident::new("__externalities__", Span::call_site())
239 };
240
241 let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) );
242
243 if is_wasm_only {
244 quote_spanned! { method.span() => #impl_ }
245 } else {
246 quote_spanned! { method.span() =>
247 #crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg)
248 }
249 }
250 } else {
251 let impl_trait_name = if is_wasm_only {
253 quote!( #crate_::sp_wasm_interface::FunctionContext )
254 } else {
255 quote!( #crate_::Externalities )
256 };
257
258 quote_spanned! { method.span() =>
259 <&mut dyn #impl_trait_name as #trait_name>::#method_name(
260 #( #arg_names, )*
261 )
262 }
263 }
264}
265
266fn takes_self_argument(sig: &Signature) -> bool {
268 matches!(sig.inputs.first(), Some(FnArg::Receiver(_)))
269}