1use crate::{
19 common::{
20 API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
21 RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
22 },
23 utils::{
24 extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
25 generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
26 prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
27 versioned_trait_name, AllowSelfRefInParameters,
28 },
29};
30
31use proc_macro2::{Span, TokenStream};
32
33use quote::quote;
34
35use std::collections::{BTreeMap, HashMap};
36use syn::{
37 fold::{self, Fold},
38 parse::{Error, Parse, ParseStream, Result},
39 parse_macro_input, parse_quote,
40 spanned::Spanned,
41 token::Comma,
42 visit::{self, Visit},
43 Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound,
44 TraitItem, TraitItemFn,
45};
46
47struct RuntimeApiDecls {
49 decls: Vec<ItemTrait>,
50}
51
52impl Parse for RuntimeApiDecls {
53 fn parse(input: ParseStream) -> Result<Self> {
54 let mut decls = Vec::new();
55
56 while !input.is_empty() {
57 decls.push(ItemTrait::parse(input)?);
58 }
59
60 Ok(Self { decls })
61 }
62}
63
64fn extend_generics_with_block(generics: &mut Generics) {
66 let c = generate_crate_access();
67
68 generics.lt_token = Some(Default::default());
69 generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
70 generics.gt_token = Some(Default::default());
71}
72
73fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static str, Attribute> {
77 let mut result = HashMap::new();
78 attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) {
79 Some(attribute) => {
80 result.insert(*attribute, v.clone());
81 false
82 },
83 None => true,
84 });
85
86 result
87}
88
89fn generate_versioned_api_traits(
134 api: ItemTrait,
135 methods: BTreeMap<u32, Vec<TraitItemFn>>,
136) -> Vec<ItemTrait> {
137 let mut result = Vec::<ItemTrait>::new();
138 for (version, _) in &methods {
139 let mut versioned_trait = api.clone();
140 versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
141 versioned_trait.items = Vec::new();
142 for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
145 versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m)));
146 }
147
148 result.push(versioned_trait);
149 }
150
151 result
152}
153
154fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
156 let err = || {
157 Error::new(
158 renamed.span(),
159 &format!(
160 "Unexpected `{RENAMED_ATTRIBUTE}` attribute. \
161 The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`",
162 ),
163 )
164 };
165
166 renamed
167 .parse_args_with(|input: ParseStream| {
168 let old_name: LitStr = input.parse()?;
169 let _comma: Comma = input.parse()?;
170 let version: LitInt = input.parse()?;
171
172 if !input.is_empty() {
173 return Err(input.error("No more arguments expected"))
174 }
175
176 Ok((old_name.value(), version.base10_parse()?))
177 })
178 .map_err(|_| err())
179}
180
181fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
183 let mut result = Vec::new();
184
185 for decl in decls {
186 let mut decl = decl.clone();
187 let decl_span = decl.span();
188 extend_generics_with_block(&mut decl.generics);
189 let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
190
191 let found_attributes = remove_supported_attributes(&mut decl.attrs);
192 let api_version = get_api_version(&found_attributes).map(generate_runtime_api_version)?;
193 let id = generate_runtime_api_id(&decl.ident.to_string());
194
195 let trait_api_version = get_api_version(&found_attributes)?;
196
197 let mut methods_by_version: BTreeMap<u32, Vec<TraitItemFn>> = BTreeMap::new();
198
199 decl.items.iter_mut().for_each(|i| {
202 match i {
203 TraitItem::Fn(ref mut method) => {
204 let method_attrs = remove_supported_attributes(&mut method.attrs);
205 let mut method_version = trait_api_version;
206 if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
209 method_version = match parse_runtime_api_version(version_attribute) {
210 Ok(method_api_ver) if method_api_ver < trait_api_version => {
211 let method_ver = method_api_ver.to_string();
212 let trait_ver = trait_api_version.to_string();
213 let mut err1 = Error::new(
214 version_attribute.span(),
215 format!(
216 "Method version `{}` is older than (or equal to) trait version `{}`.\
217 Methods can't define versions older than the trait version.",
218 method_ver,
219 trait_ver,
220 ),
221 );
222
223 let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
224 Some(attr) =>
225 Error::new(attr.span(), "Trait version is set here."),
226 None => Error::new(
227 decl_span,
228 "Trait version is not set so it is implicitly equal to 1.",
229 ),
230 };
231 err1.combine(err2);
232 result.push(err1.to_compile_error());
233
234 trait_api_version
235 },
236 Ok(method_api_ver) => method_api_ver,
237 Err(e) => {
238 result.push(e.to_compile_error());
239 trait_api_version
240 },
241 };
242 }
243
244 if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
247 replace_wild_card_parameter_names(&mut method.sig);
249
250 methods_by_version.entry(method_version).or_default().push(method.clone());
252 }
253 },
254 _ => (),
255 }
256 });
257
258 let versioned_methods_iter = methods_by_version
259 .iter()
260 .flat_map(|(&version, methods)| methods.iter().map(move |method| (method, version)));
261 let metadata =
262 crate::runtime_metadata::generate_decl_runtime_metadata(&decl, versioned_methods_iter);
263
264 let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
265
266 let main_api_ident = decl.ident.clone();
267 let versioned_ident = &versioned_api_traits
268 .first()
269 .expect("There should always be at least one version.")
270 .ident;
271
272 result.push(quote!(
273 #[doc(hidden)]
274 #[allow(dead_code)]
275 #[allow(deprecated)]
276 pub mod #mod_name {
277 pub use super::*;
278
279 #( #versioned_api_traits )*
280
281 pub use #versioned_ident as #main_api_ident;
282
283 #metadata
284
285 pub #api_version
286
287 pub #id
288 }
289 ));
290 }
291
292 Ok(quote!( #( #result )* ))
293}
294
295struct ToClientSideDecl<'a> {
297 block_hash: &'a TokenStream,
298 crate_: &'a TokenStream,
299 found_attributes: &'a mut HashMap<&'static str, Attribute>,
300 errors: &'a mut Vec<TokenStream>,
302 trait_: &'a Ident,
303}
304
305impl<'a> ToClientSideDecl<'a> {
306 fn process(mut self, decl: ItemTrait) -> ItemTrait {
308 let mut decl = self.fold_item_trait(decl);
309
310 let block_hash = self.block_hash;
311 let crate_ = self.crate_;
312
313 decl.items.push(parse_quote! {
316 #[doc(hidden)]
318 fn __runtime_api_internal_call_api_at(
319 &self,
320 at: #block_hash,
321 params: std::vec::Vec<u8>,
322 fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
323 ) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
324 });
325
326 decl
327 }
328}
329
330impl<'a> ToClientSideDecl<'a> {
331 fn fold_item_trait_items(
332 &mut self,
333 items: Vec<TraitItem>,
334 trait_generics_num: usize,
335 ) -> Vec<TraitItem> {
336 let mut result = Vec::new();
337
338 items.into_iter().for_each(|i| match i {
339 TraitItem::Fn(method) => {
340 let fn_decl = self.create_method_decl(method, trait_generics_num);
341 result.push(fn_decl.into());
342 },
343 r => result.push(r),
344 });
345
346 result
347 }
348
349 fn create_method_decl(
353 &mut self,
354 mut method: TraitItemFn,
355 trait_generics_num: usize,
356 ) -> TraitItemFn {
357 let params = match extract_parameter_names_types_and_borrows(
358 &method.sig,
359 AllowSelfRefInParameters::No,
360 ) {
361 Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
362 Err(e) => {
363 self.errors.push(e.to_compile_error());
364 Vec::new()
365 },
366 };
367 let ret_type = return_type_extract_type(&method.sig.output);
368
369 fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_);
370
371 let crate_ = self.crate_;
372
373 let found_attributes = remove_supported_attributes(&mut method.attrs);
374
375 let mut renames = Vec::new();
377 for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
378 match parse_renamed_attribute(a) {
379 Ok((old_name, version)) => {
380 renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
381 },
382 Err(e) => self.errors.push(e.to_compile_error()),
383 }
384 }
385
386 renames.sort_by(|l, r| r.cmp(l));
387 let (versions, old_names) = renames.into_iter().fold(
388 (Vec::new(), Vec::new()),
389 |(mut versions, mut old_names), (version, old_name)| {
390 versions.push(version);
391 old_names.push(old_name);
392 (versions, old_names)
393 },
394 );
395
396 let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
399
400 match get_changed_in(&found_attributes) {
403 Ok(Some(version)) => {
404 if get_api_version(self.found_attributes).ok() < Some(version) {
406 self.errors.push(
407 Error::new(
408 method.span(),
409 "`changed_in` version can not be greater than the `api_version`",
410 )
411 .to_compile_error(),
412 );
413 }
414
415 let ident = Ident::new(
416 &format!("{}_before_version_{}", method.sig.ident, version),
417 method.sig.ident.span(),
418 );
419 method.sig.ident = ident;
420 method.attrs.push(parse_quote!( #[deprecated] ));
421 },
422 Ok(None) => {},
423 Err(e) => {
424 self.errors.push(e.to_compile_error());
425 },
426 };
427
428 let trait_name = &self.trait_;
430 let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
431 let underscores = (0..trait_generics_num).map(|_| quote!(_));
432
433 method.default = Some(parse_quote! {
435 {
436 let __runtime_api_impl_params_encoded__ =
437 #crate_::Encode::encode(&( #( &#params ),* ));
438
439 <Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
440 self,
441 __runtime_api_at_param__,
442 __runtime_api_impl_params_encoded__,
443 &|_version| {
444 #(
445 if _version.apis.iter().any(|(s, v)| {
447 s == &#runtime_mod::ID && *v < #versions
448 }) {
449 return #old_names
450 }
451 )*
452
453 #function_name
454 }
455 )
456 .and_then(|r|
457 std::result::Result::map_err(
458 <#ret_type as #crate_::Decode>::decode(&mut &r[..]),
459 |err| #crate_::ApiError::FailedToDecodeReturnValue {
460 function: #function_name,
461 error: err,
462 raw: r.clone(),
463 }
464 )
465 )
466 }
467 });
468
469 method
470 }
471}
472
473impl<'a> Fold for ToClientSideDecl<'a> {
474 fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
475 extend_generics_with_block(&mut input.generics);
476
477 *self.found_attributes = remove_supported_attributes(&mut input.attrs);
478 let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
480 let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
481
482 if is_core_trait {
483 input.supertraits = parse_quote!('static + Send);
485 } else {
486 let crate_ = &self.crate_;
488 input.supertraits.push(parse_quote!( #crate_::Core<#block_ident> ));
489 }
490
491 input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
492
493 fold::fold_item_trait(self, input)
494 }
495}
496
497fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
500 use blake2::digest::{consts::U8, Digest};
501
502 let mut res = [0; 8];
503 res.copy_from_slice(blake2::Blake2b::<U8>::digest(trait_name).as_slice());
504
505 quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
506}
507
508fn generate_runtime_api_version(version: u32) -> TokenStream {
510 quote!( const VERSION: u32 = #version; )
511}
512
513fn generate_runtime_info_impl(trait_: &ItemTrait, version: u32) -> TokenStream {
515 let trait_name = &trait_.ident;
516 let crate_ = generate_crate_access();
517 let id = generate_runtime_api_id(&trait_name.to_string());
518 let version = generate_runtime_api_version(version as u32);
519
520 let impl_generics = trait_.generics.type_params().map(|t| {
521 let ident = &t.ident;
522 let colon_token = &t.colon_token;
523 let bounds = &t.bounds;
524
525 quote! { #ident #colon_token #bounds }
526 });
527
528 let ty_generics = trait_.generics.type_params().map(|t| {
529 let ident = &t.ident;
530 quote! { #ident }
531 });
532 let maybe_allow_attrs = trait_.attrs.iter().filter(|attr| attr.path().is_ident("allow"));
533 let maybe_allow_deprecated = trait_.attrs.iter().filter_map(|attr| {
534 attr.path().is_ident("deprecated").then(|| quote! { #[allow(deprecated)] })
535 });
536
537 quote!(
538 #crate_::std_enabled! {
539 #( #maybe_allow_attrs )*
540 #( #maybe_allow_deprecated )*
541 impl < #( #impl_generics, )* > #crate_::RuntimeApiInfo
542 for dyn #trait_name < #( #ty_generics, )* >
543 {
544 #id
545 #version
546 }
547 }
548 )
549}
550
551fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u32>> {
553 found_attributes
554 .get(&CHANGED_IN_ATTRIBUTE)
555 .map(|v| parse_runtime_api_version(v).map(Some))
556 .unwrap_or(Ok(None))
557}
558
559fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u32> {
561 found_attributes
562 .get(&API_VERSION_ATTRIBUTE)
563 .map(parse_runtime_api_version)
564 .unwrap_or(Ok(1))
565}
566
567fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
569 let mut result = Vec::new();
570
571 for decl in decls {
572 let decl = decl.clone();
573
574 let crate_ = generate_crate_access();
575 let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
576 let mut found_attributes = HashMap::new();
577 let mut errors = Vec::new();
578 let trait_ = decl.ident.clone();
579
580 let decl = ToClientSideDecl {
581 crate_: &crate_,
582 block_hash: &block_hash,
583 found_attributes: &mut found_attributes,
584 errors: &mut errors,
585 trait_: &trait_,
586 }
587 .process(decl);
588
589 let api_version = get_api_version(&found_attributes);
590
591 let runtime_info = api_version.map(|v| generate_runtime_info_impl(&decl, v))?;
592
593 result.push(quote!(
594 #crate_::std_enabled! {
595 #[allow(deprecated)]
596 #decl
597 }
598 #runtime_info
599 #( #errors )*
600 ));
601 }
602
603 Ok(quote!( #( #result )* ))
604}
605
606struct CheckTraitDecl {
608 errors: Vec<Error>,
609}
610
611impl CheckTraitDecl {
612 fn check(&mut self, trait_: &ItemTrait) {
616 self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
617 TraitItem::Fn(method) => Some(method),
618 _ => None,
619 }));
620
621 visit::visit_item_trait(self, trait_);
622 }
623
624 fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemFn>) {
628 let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u32>>>::new();
629
630 methods.into_iter().for_each(|method| {
631 let attributes = remove_supported_attributes(&mut method.attrs.clone());
632
633 let changed_in = match get_changed_in(&attributes) {
634 Ok(r) => r,
635 Err(e) => {
636 self.errors.push(e);
637 return
638 },
639 };
640
641 method_to_signature_changed
642 .entry(method.sig.ident.clone())
643 .or_default()
644 .push(changed_in);
645
646 if method.default.is_some() {
647 self.errors.push(Error::new(
648 method.default.span(),
649 "A runtime API function cannot have a default implementation!",
650 ));
651 }
652 });
653
654 method_to_signature_changed.into_iter().for_each(|(f, changed)| {
655 if changed.iter().filter(|c| c.is_none()).count() == 0 {
658 self.errors.push(Error::new(
659 f.span(),
660 "There is no 'default' method with this name (without `changed_in` attribute).\n\
661 The 'default' method is used to call into the latest implementation.",
662 ));
663 }
664 });
665 }
666}
667
668impl<'ast> Visit<'ast> for CheckTraitDecl {
669 fn visit_fn_arg(&mut self, input: &'ast FnArg) {
670 if let FnArg::Receiver(_) = input {
671 self.errors.push(Error::new(input.span(), "`self` as argument not supported."))
672 }
673
674 visit::visit_fn_arg(self, input);
675 }
676
677 fn visit_generic_param(&mut self, input: &'ast GenericParam) {
678 match input {
679 GenericParam::Type(ty) if ty.ident == BLOCK_GENERIC_IDENT =>
680 self.errors.push(Error::new(
681 input.span(),
682 "`Block: BlockT` generic parameter will be added automatically by the \
683 `decl_runtime_apis!` macro!",
684 )),
685 _ => {},
686 }
687
688 visit::visit_generic_param(self, input);
689 }
690
691 fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
692 if let Some(last_ident) = input.path.segments.last().map(|v| &v.ident) {
693 if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
694 self.errors.push(Error::new(
695 input.span(),
696 "`Block: BlockT` generic parameter will be added automatically by the \
697 `decl_runtime_apis!` macro! If you try to use a different trait than the \
698 substrate `Block` trait, please rename it locally.",
699 ))
700 }
701 }
702
703 visit::visit_trait_bound(self, input)
704 }
705}
706
707fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
709 let mut checker = CheckTraitDecl { errors: Vec::new() };
710 decls.iter().for_each(|decl| checker.check(decl));
711
712 if let Some(err) = checker.errors.pop() {
713 Err(checker.errors.into_iter().fold(err, |mut err, other| {
714 err.combine(other);
715 err
716 }))
717 } else {
718 Ok(())
719 }
720}
721
722pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
724 let RuntimeApiDecls { decls: api_decls } = parse_macro_input!(input as RuntimeApiDecls);
726
727 decl_runtime_apis_impl_inner(&api_decls)
728 .unwrap_or_else(|e| e.to_compile_error())
729 .into()
730}
731
732fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
733 check_trait_decls(api_decls)?;
734
735 let runtime_decls = generate_runtime_decls(api_decls)?;
736 let client_side_decls = generate_client_side_decls(api_decls)?;
737
738 let decl = quote! {
739 #runtime_decls
740
741 #client_side_decls
742 };
743
744 let decl = expander::Expander::new("decl_runtime_apis")
745 .dry(std::env::var("EXPAND_MACROS").is_err())
746 .verbose(true)
747 .write_to_out_dir(decl)
748 .expect("Does not fail because of IO in OUT_DIR; qed");
749
750 Ok(decl)
751}