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