ink_codegen/generator/trait_def/
trait_registry.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The global registry with which it is possible to refer back to the global
16//! trait call builder and call forwarder types using only the trait identifier.
17//!
18//! This works by making the global trait registry type defined in the `ink`
19//! crate implement each and every ink! trait definition and defining associated
20//! types for the trait's respective call builder and call forwarder.
21
22use derive_more::From;
23use ink_primitives::abi::Abi;
24use proc_macro2::{
25    Span,
26    TokenStream as TokenStream2,
27};
28use quote::{
29    format_ident,
30    quote,
31    quote_spanned,
32};
33use syn::{
34    parse_quote,
35    spanned::Spanned,
36};
37
38use super::TraitDefinition;
39use crate::{
40    generator,
41    generator::sol,
42    traits::GenerateCode,
43    EnforcedErrors,
44};
45
46impl TraitDefinition<'_> {
47    /// Generates the code for the global trait registry implementation.
48    ///
49    /// This also generates the code for the global trait info object which
50    /// implements some `ink` traits to provide common information about
51    /// the ink! trait definition such as its unique identifier.
52    pub fn generate_trait_registry_impl(&self) -> TokenStream2 {
53        TraitRegistry::from(*self).generate_code()
54    }
55
56    /// Returns the identifier for the ink! trait definition info object.
57    pub fn trait_info_ident(&self) -> syn::Ident {
58        self.append_trait_suffix("TraitInfo")
59    }
60}
61
62/// Generates code for the global ink! trait registry implementation.
63#[derive(From)]
64struct TraitRegistry<'a> {
65    trait_def: TraitDefinition<'a>,
66}
67
68impl GenerateCode for TraitRegistry<'_> {
69    fn generate_code(&self) -> TokenStream2 {
70        let registry_impl = self.generate_registry_impl();
71        let trait_info = self.generate_trait_info_object();
72        quote! {
73            #registry_impl
74            #trait_info
75        }
76    }
77}
78
79impl TraitRegistry<'_> {
80    /// Returns the span of the ink! trait definition.
81    fn span(&self) -> Span {
82        self.trait_def.span()
83    }
84
85    /// Returns the identifier of the ink! trait definition.
86    fn trait_ident(&self) -> &syn::Ident {
87        self.trait_def.trait_def.item().ident()
88    }
89
90    /// Generates the global trait registry implementation for the ink! trait.
91    ///
92    /// This makes it possible to refer back to the global call forwarder and
93    /// call builder specific to this ink! trait from anywhere with just the Rust
94    /// trait identifier which allows for type safe access.
95    ///
96    /// # Note
97    ///
98    /// Through this implementation we register the previously defined ink! trait
99    /// call forwarder and call builder types as such for the ink! trait.
100    ///
101    /// This is done by the fact that ink! implements all ink! traits by the
102    /// [`ink::TraitDefinitionRegistry`] type and uses the `__ink_ConcreteImplementer`
103    /// associated type to refer back to the actual call forwarder and call builder types.
104    fn generate_registry_impl(&self) -> TokenStream2 {
105        let span = self.span();
106        let name = self.trait_ident();
107        let trait_info_ident = self.trait_def.trait_info_ident();
108        let messages = self.generate_registry_messages();
109        quote_spanned!(span=>
110            impl<E> #name for ::ink::reflect::TraitDefinitionRegistry<E>
111            where
112                E: ::ink::env::Environment,
113            {
114                /// Holds general and global information about the trait.
115                #[allow(non_camel_case_types)]
116                type __ink_TraitInfo = #trait_info_ident<E>;
117
118                #messages
119            }
120        )
121    }
122
123    /// Generate the code for all ink! trait messages implemented by the trait registry.
124    fn generate_registry_messages(&self) -> TokenStream2 {
125        let messages = self.trait_def.trait_def.item().iter_items().filter_map(
126            |(item, selector)| {
127                let ret = item.filter_map_message().map(|message| {
128                    self.generate_registry_for_message(&message, selector)
129                });
130                ret
131            },
132        );
133        quote! {
134            #( #messages )*
135        }
136    }
137
138    /// Generates code to assert that ink! input and output types meet certain properties.
139    fn generate_inout_guards_for_message(
140        message: &ir::InkTraitMessage,
141        abi: Abi,
142    ) -> TokenStream2 {
143        let message_span = message.span();
144        let (input_trait, output_trait) = match abi {
145            Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
146            Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
147        };
148        let message_inputs = message.inputs().map(|input| {
149            let input_span = input.span();
150            let input_type = &*input.ty;
151            quote_spanned!(input_span=>
152                ::ink::codegen::utils::consume_type::<
153                    ::ink::codegen::#input_trait<#input_type>
154                >();
155            )
156        });
157        let message_output = message.output().map(|output_type| {
158            let output_span = output_type.span();
159            quote_spanned!(output_span=>
160                ::ink::codegen::utils::consume_type::<
161                    ::ink::codegen::#output_trait<#output_type>
162                >();
163            )
164        });
165        quote_spanned!(message_span=>
166            #( #message_inputs )*
167            #message_output
168        )
169    }
170
171    /// Generate the code for a single ink! trait message implemented by the trait
172    /// registry.
173    ///
174    /// Generally the implementation of any ink! trait of the ink! trait registry.
175    fn generate_registry_for_message(
176        &self,
177        message: &ir::InkTraitMessage,
178        selector: ir::Selector,
179    ) -> TokenStream2 {
180        let span = message.span();
181        let ident = message.ident();
182        let attrs = message.attrs();
183        let cfg_attrs = message.get_cfg_attrs(span);
184        let output_ident = generator::output_ident(message.ident());
185        let output_type = message
186            .output()
187            .cloned()
188            .unwrap_or_else(|| parse_quote! { () });
189        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
190        let (input_bindings, input_types) =
191            Self::input_bindings_and_types(message.inputs());
192        let linker_error_ident = EnforcedErrors::cannot_call_trait_message(
193            self.trait_ident(),
194            message.ident(),
195            selector,
196            message.mutates(),
197        );
198        let inout_guards = generate_abi_impls!(@type |abi| {
199            Self::generate_inout_guards_for_message(message, abi)
200        });
201        let impl_body = match option_env!("INK_COVERAGE_REPORTING") {
202            Some("true") => {
203                quote! {
204                    // The code coverage reporting CI stage links dead code,
205                    // hence we have to provide an `unreachable!` here. If
206                    // the invalid implementation above is linked this results
207                    // in a linker error.
208                    ::core::unreachable!(
209                        "this is an invalid ink! message call which should never be possible."
210                    );
211                }
212            }
213            _ => {
214                quote! {
215                    /// We enforce linking errors in case this is ever actually called.
216                    /// These linker errors are properly resolved by the cargo-contract tool.
217                    extern {
218                        fn #linker_error_ident() -> !;
219                    }
220                    unsafe { #linker_error_ident() }
221                }
222            }
223        };
224        quote_spanned!(span=>
225            #( #cfg_attrs )*
226            type #output_ident = #output_type;
227
228            #( #attrs )*
229            #[cold]
230            fn #ident(
231                & #mut_token self
232                #( , #input_bindings : #input_types )*
233            ) -> Self::#output_ident {
234                #inout_guards
235                #impl_body
236            }
237        )
238    }
239
240    /// Returns a pair of input bindings `__ink_bindings_N` and types.
241    fn input_bindings_and_types(
242        inputs: ir::InputsIter,
243    ) -> (Vec<syn::Ident>, Vec<&syn::Type>) {
244        inputs
245            .enumerate()
246            .map(|(n, pat_type)| {
247                let binding = format_ident!("__ink_binding_{}", n);
248                let ty = &*pat_type.ty;
249                (binding, ty)
250            })
251            .unzip()
252    }
253
254    /// Phantom type that implements the following traits for every ink! trait:
255    ///
256    /// - `ink::TraitCallForwarder`
257    ///
258    /// It is mainly used to access global information about the ink! trait.
259    fn generate_trait_info_object(&self) -> TokenStream2 {
260        let span = self.span();
261        let trait_id = self.generate_trait_id();
262        let trait_ident = self.trait_ident();
263        let trait_info_ident = self.trait_def.trait_info_ident();
264        let trait_call_forwarder = self.trait_def.call_forwarder_ident();
265        let trait_message_builder = self.trait_def.message_builder_ident();
266        let trait_message_info = self.generate_info_for_trait_messages();
267        quote_spanned!(span =>
268            #[doc(hidden)]
269            #[allow(non_camel_case_types)]
270            pub struct #trait_info_ident<E> {
271                marker: ::core::marker::PhantomData<fn() -> E>,
272            }
273
274            #trait_message_info
275
276            impl<E> ::ink::reflect::TraitInfo for #trait_info_ident<E>
277            where
278                E: ::ink::env::Environment,
279            {
280                const ID: u32 = #trait_id;
281
282                const PATH: &'static ::core::primitive::str = ::core::module_path!();
283
284                const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident);
285            }
286
287            impl<E> ::ink::codegen::TraitCallForwarder for #trait_info_ident<E>
288            where
289                E: ::ink::env::Environment,
290            {
291                type Forwarder<Abi> = #trait_call_forwarder<E, Abi>;
292            }
293
294            impl<E> ::ink::codegen::TraitMessageBuilder for #trait_info_ident<E>
295            where
296                E: ::ink::env::Environment,
297            {
298                type MessageBuilder<Abi> = #trait_message_builder<E, Abi>;
299            }
300        )
301    }
302
303    /// Generates a unique id for the trait, as an XOR of the set of selectors.
304    fn generate_trait_id(&self) -> syn::LitInt {
305        let span = self.span();
306        let mut id = 0u32;
307        debug_assert!(
308            self.trait_def
309                .trait_def
310                .item()
311                .iter_items()
312                .next()
313                .is_some(),
314            "invalid empty ink! trait definition"
315        );
316        for (_, selector) in self.trait_def.trait_def.item().iter_items() {
317            id ^= selector.into_be_u32()
318        }
319        syn::LitInt::new(&format!("{id}"), span)
320    }
321
322    /// Generates the [`::ink::reflect::TraitMessageInfo`] implementations for all
323    /// ink! messages defined by the ink! trait definition.
324    fn generate_info_for_trait_messages(&self) -> TokenStream2 {
325        let span = self.span();
326        let message_impls = self.trait_def.trait_def.item().iter_items().filter_map(
327            |(trait_item, selector)| {
328                trait_item.filter_map_message().map(|message| {
329                    self.generate_info_for_trait_for_message(&message, selector)
330                })
331            },
332        );
333        quote_spanned!(span=>
334            #( #message_impls )*
335        )
336    }
337
338    /// Generates the [`::ink::reflect::TraitMessageInfo`] implementation for a single
339    /// ink! message defined by the ink! trait definition.
340    fn generate_info_for_trait_for_message(
341        &self,
342        message: &ir::InkTraitMessage,
343        selector: ir::Selector,
344    ) -> TokenStream2 {
345        let span = message.span();
346        let trait_info_ident = self.trait_def.trait_info_ident();
347        let is_payable = message.ink_attrs().is_payable();
348        generate_abi_impls!(@type |abi| {
349            let (local_id, selector_bytes) = match abi {
350                Abi::Ink => {
351                    let local_id = message.local_id();
352                    let selector_bytes = selector.hex_lits();
353                    (
354                        quote!(#local_id),
355                        quote!([ #( #selector_bytes ),* ])
356                    )
357                }
358                Abi::Sol => {
359                    let ident_str = message.ident().to_string();
360                    let signature = sol::utils::call_signature(ident_str, message.inputs());
361                    let selector_bytes = quote! {
362                        ::ink::codegen::sol::selector_bytes(#signature)
363                    };
364                    let selector_id = quote!(
365                        {
366                            ::core::primitive::u32::from_be_bytes(#selector_bytes)
367                        }
368                    );
369                    (
370                        selector_id,
371                        selector_bytes
372                    )
373                }
374            };
375            quote_spanned!(span=>
376                impl<E> ::ink::reflect::TraitMessageInfo<#local_id> for #trait_info_ident<E> {
377                    const PAYABLE: ::core::primitive::bool = #is_payable;
378                    const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
379                }
380            )
381        })
382    }
383}