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