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    EnforcedErrors,
41    generator,
42    generator::sol,
43    traits::GenerateCode,
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                item.filter_map_message()
128                    .map(|message| self.generate_registry_for_message(&message, selector))
129            },
130        );
131        quote! {
132            #( #messages )*
133        }
134    }
135
136    /// Generates code to assert that ink! input and output types meet certain properties.
137    fn generate_inout_guards_for_message(
138        message: &ir::InkTraitMessage,
139        abi: Abi,
140    ) -> TokenStream2 {
141        let message_span = message.span();
142        let (input_trait, output_trait) = match abi {
143            Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
144            Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
145        };
146        let message_inputs = message.inputs().map(|input| {
147            let input_span = input.span();
148            let input_type = &*input.ty;
149            quote_spanned!(input_span=>
150                ::ink::codegen::utils::consume_type::<
151                    ::ink::codegen::#input_trait<#input_type>
152                >();
153            )
154        });
155        let message_output = message.output().map(|output_type| {
156            let output_span = output_type.span();
157            quote_spanned!(output_span=>
158                ::ink::codegen::utils::consume_type::<
159                    ::ink::codegen::#output_trait<#output_type>
160                >();
161            )
162        });
163        quote_spanned!(message_span=>
164            #( #message_inputs )*
165            #message_output
166        )
167    }
168
169    /// Generate the code for a single ink! trait message implemented by the trait
170    /// registry.
171    ///
172    /// Generally the implementation of any ink! trait of the ink! trait registry.
173    fn generate_registry_for_message(
174        &self,
175        message: &ir::InkTraitMessage,
176        selector: ir::Selector,
177    ) -> TokenStream2 {
178        let span = message.span();
179        let ident = message.ident();
180        let attrs = message.attrs();
181        let cfg_attrs = message.get_cfg_attrs(span);
182        let output_ident = generator::output_ident(message.ident());
183        let output_type = message
184            .output()
185            .cloned()
186            .unwrap_or_else(|| parse_quote! { () });
187        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
188        let (input_bindings, input_types) =
189            Self::input_bindings_and_types(message.inputs());
190        let linker_error_ident = EnforcedErrors::cannot_call_trait_message(
191            self.trait_ident(),
192            message.ident(),
193            selector,
194            message.mutates(),
195        );
196        let inout_guards = generate_abi_impls!(@type |abi| {
197            Self::generate_inout_guards_for_message(message, abi)
198        });
199        let impl_body = match option_env!("INK_COVERAGE_REPORTING") {
200            Some("true") => {
201                quote! {
202                    // The code coverage reporting CI stage links dead code,
203                    // hence we have to provide an `unreachable!` here. If
204                    // the invalid implementation above is linked this results
205                    // in a linker error.
206                    ::core::unreachable!(
207                        "this is an invalid ink! message call which should never be possible."
208                    );
209                }
210            }
211            _ => {
212                quote! {
213                    /// We enforce linking errors in case this is ever actually called.
214                    /// These linker errors are properly resolved by the cargo-contract tool.
215                    unsafe extern {
216                        fn #linker_error_ident() -> !;
217                    }
218                    unsafe { #linker_error_ident() }
219                }
220            }
221        };
222        quote_spanned!(span=>
223            #( #cfg_attrs )*
224            type #output_ident = #output_type;
225
226            #( #attrs )*
227            #[cold]
228            fn #ident(
229                & #mut_token self
230                #( , #input_bindings : #input_types )*
231            ) -> Self::#output_ident {
232                #inout_guards
233                #impl_body
234            }
235        )
236    }
237
238    /// Returns a pair of input bindings `__ink_bindings_N` and types.
239    fn input_bindings_and_types(
240        inputs: ir::InputsIter<'_>,
241    ) -> (Vec<syn::Ident>, Vec<&syn::Type>) {
242        inputs
243            .enumerate()
244            .map(|(n, pat_type)| {
245                let binding = format_ident!("__ink_binding_{}", n);
246                let ty = &*pat_type.ty;
247                (binding, ty)
248            })
249            .unzip()
250    }
251
252    /// Phantom type that implements the following traits for every ink! trait:
253    ///
254    /// - `ink::TraitCallForwarder`
255    ///
256    /// It is mainly used to access global information about the ink! trait.
257    fn generate_trait_info_object(&self) -> TokenStream2 {
258        let span = self.span();
259        let trait_id = self.generate_trait_id();
260        let trait_ident = self.trait_ident();
261        let trait_info_ident = self.trait_def.trait_info_ident();
262        let trait_call_forwarder = self.trait_def.call_forwarder_ident();
263        let trait_message_builder = self.trait_def.message_builder_ident();
264        let trait_message_info = self.generate_info_for_trait_messages();
265        quote_spanned!(span =>
266            #[doc(hidden)]
267            #[allow(non_camel_case_types)]
268            pub struct #trait_info_ident<E> {
269                marker: ::core::marker::PhantomData<fn() -> E>,
270            }
271
272            #trait_message_info
273
274            impl<E> ::ink::reflect::TraitInfo for #trait_info_ident<E>
275            where
276                E: ::ink::env::Environment,
277            {
278                const ID: u32 = #trait_id;
279
280                const PATH: &'static ::core::primitive::str = ::core::module_path!();
281
282                const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident);
283            }
284
285            impl<E> ::ink::codegen::TraitCallForwarder for #trait_info_ident<E>
286            where
287                E: ::ink::env::Environment,
288            {
289                type Forwarder<Abi> = #trait_call_forwarder<E, Abi>;
290            }
291
292            impl<E> ::ink::codegen::TraitMessageBuilder for #trait_info_ident<E>
293            where
294                E: ::ink::env::Environment,
295            {
296                type MessageBuilder<Abi> = #trait_message_builder<E, Abi>;
297            }
298        )
299    }
300
301    /// Generates a unique id for the trait, as an XOR of the set of selectors.
302    fn generate_trait_id(&self) -> syn::LitInt {
303        let span = self.span();
304        let mut id = 0u32;
305        debug_assert!(
306            self.trait_def
307                .trait_def
308                .item()
309                .iter_items()
310                .next()
311                .is_some(),
312            "invalid empty ink! trait definition"
313        );
314        for (_, selector) in self.trait_def.trait_def.item().iter_items() {
315            id ^= selector.into_be_u32()
316        }
317        syn::LitInt::new(&format!("{id}"), span)
318    }
319
320    /// Generates the [`::ink::reflect::TraitMessageInfo`] implementations for all
321    /// ink! messages defined by the ink! trait definition.
322    fn generate_info_for_trait_messages(&self) -> TokenStream2 {
323        let span = self.span();
324        let message_impls = self.trait_def.trait_def.item().iter_items().filter_map(
325            |(trait_item, selector)| {
326                trait_item.filter_map_message().map(|message| {
327                    self.generate_info_for_trait_for_message(&message, selector)
328                })
329            },
330        );
331        quote_spanned!(span=>
332            #( #message_impls )*
333        )
334    }
335
336    /// Generates the [`::ink::reflect::TraitMessageInfo`] implementation for a single
337    /// ink! message defined by the ink! trait definition.
338    fn generate_info_for_trait_for_message(
339        &self,
340        message: &ir::InkTraitMessage,
341        selector: ir::Selector,
342    ) -> TokenStream2 {
343        let span = message.span();
344        let trait_info_ident = self.trait_def.trait_info_ident();
345        let is_payable = message.ink_attrs().is_payable();
346        generate_abi_impls!(@type |abi| {
347            let (local_id, selector_bytes) = match abi {
348                Abi::Ink => {
349                    let local_id = message.local_id();
350                    let selector_bytes = selector.hex_lits();
351                    (
352                        quote!(#local_id),
353                        quote!([ #( #selector_bytes ),* ])
354                    )
355                }
356                Abi::Sol => {
357                    let ident_str = message.ident().to_string();
358                    let signature = sol::utils::call_signature(ident_str, message.inputs());
359                    let selector_bytes = quote! {
360                        ::ink::codegen::sol::selector_bytes(#signature)
361                    };
362                    let selector_id = quote!(
363                        {
364                            ::core::primitive::u32::from_be_bytes(#selector_bytes)
365                        }
366                    );
367                    (
368                        selector_id,
369                        selector_bytes
370                    )
371                }
372            };
373            quote_spanned!(span=>
374                impl<E> ::ink::reflect::TraitMessageInfo<#local_id> for #trait_info_ident<E> {
375                    const PAYABLE: ::core::primitive::bool = #is_payable;
376                    const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
377                }
378            )
379        })
380    }
381}