ink_codegen/generator/
item_impls.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
15use core::iter;
16
17use derive_more::From;
18use heck::ToLowerCamelCase as _;
19use ink_primitives::abi::Abi;
20use ir::Callable as _;
21#[cfg(not(ink_abi = "sol"))]
22use ir::HexLiteral;
23use proc_macro2::TokenStream as TokenStream2;
24use quote::{
25    format_ident,
26    quote,
27    quote_spanned,
28    ToTokens,
29};
30use syn::spanned::Spanned as _;
31
32use crate::GenerateCode;
33
34/// Generates code for all ink! implementation blocks.
35#[derive(From)]
36pub struct ItemImpls<'a> {
37    contract: &'a ir::Contract,
38}
39impl_as_ref_for_generator!(ItemImpls);
40
41impl GenerateCode for ItemImpls<'_> {
42    fn generate_code(&self) -> TokenStream2 {
43        let item_impls = self
44            .contract
45            .module()
46            .impls()
47            .map(|item_impl| self.generate_item_impl(item_impl));
48        let inout_guards = generate_abi_impls!(@type |abi| {
49            self.generate_input_output_guards(abi)
50        });
51        let trait_message_property_guards = self.generate_trait_message_property_guards();
52        quote! {
53            const _: () = {
54                // Required to make `self.env()` and `Self::env()` syntax available.
55                use ::ink::codegen::{Env as _, StaticEnv as _};
56
57                #( #item_impls )*
58                #inout_guards
59                #trait_message_property_guards
60            };
61        }
62    }
63}
64
65impl ItemImpls<'_> {
66    /// Generates code to guard annotated ink! trait message properties.
67    ///
68    /// These guarded properties include `selector` and `payable`.
69    /// If an ink! trait message is annotated with `#[ink(payable)]`
70    /// or `#[ink(selector = ..)]` then code is generated to guard that
71    /// the given argument to `payable` or `selector` is equal to
72    /// what the associated ink! trait definition defines for the same
73    /// ink! message.
74    fn generate_trait_message_property_guards(&self) -> TokenStream2 {
75        let storage_span = self.contract.module().storage().span();
76        let storage_ident = self.contract.module().storage().ident();
77        let trait_message_guards = self
78            .contract
79            .module()
80            .impls()
81            .filter_map(|item_impl| item_impl.trait_path().map(|trait_path| {
82                iter::repeat(trait_path).zip(item_impl.iter_messages())
83            }))
84            .flatten()
85            .map(|(trait_path, message)| {
86                let message_span = message.span();
87
88                #[cfg(not(ink_abi = "sol"))]
89                let message_local_id = {
90                    let id = message.local_id().hex_padded_suffixed();
91                    quote!(#id)
92                };
93
94                #[cfg(ink_abi = "sol")]
95                let message_local_id = {
96                    use crate::generator::sol;
97                    sol::utils::selector_id(&message)
98                };
99
100                let message_guard_payable = message.is_payable().then(|| {
101                    quote_spanned!(message_span=>
102                        const _: ::ink::codegen::TraitMessagePayable<{
103                            <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
104                                as #trait_path>::__ink_TraitInfo
105                                as ::ink::reflect::TraitMessageInfo<#message_local_id>>::PAYABLE
106                        }> = ::ink::codegen::TraitMessagePayable::<true>;
107                    )
108                });
109
110                // Solidity ABI compatible codegen ignores user provided selector overrides.
111                #[cfg(not(ink_abi = "sol"))]
112                let message_guard_selector = message.user_provided_selector().map(|selector| {
113                    let given_selector = selector.into_be_u32().hex_padded_suffixed();
114                    quote_spanned!(message_span=>
115                        const _: ::ink::codegen::TraitMessageSelector<{
116                            ::core::primitive::u32::from_be_bytes(
117                                <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
118                                    as #trait_path>::__ink_TraitInfo
119                                    as ::ink::reflect::TraitMessageInfo<#message_local_id>>::SELECTOR
120                            )
121                        }> = ::ink::codegen::TraitMessageSelector::<#given_selector>;
122                    )
123                });
124
125                #[cfg(ink_abi = "sol")]
126                let message_guard_selector = quote!();
127
128                quote_spanned!(message_span=>
129                    #message_guard_payable
130                    #message_guard_selector
131                )
132            });
133        quote_spanned!(storage_span=>
134            #( #trait_message_guards )*
135        )
136    }
137
138    /// Generates code to assert that ink! input and output types meet certain properties.
139    fn generate_input_output_guards(&self, abi: Abi) -> TokenStream2 {
140        let (input_trait, output_trait) = match abi {
141            Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
142            Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
143        };
144        let storage_span = self.contract.module().storage().span();
145        let constructor_input_guards = self
146            .contract
147            .module()
148            .impls()
149            .flat_map(|item_impl| item_impl.iter_constructors())
150            .map(|constructor| {
151                let constructor_span = constructor.span();
152                let constructor_inputs = constructor.inputs().map(|input| {
153                    let span = input.span();
154                    let input_type = &*input.ty;
155                    quote_spanned!(span=>
156                        ::ink::codegen::utils::consume_type::<
157                            ::ink::codegen::#input_trait<#input_type>
158                        >();
159                    )
160                });
161                quote_spanned!(constructor_span=>
162                    #( #constructor_inputs )*
163                )
164            });
165        let message_inout_guards = self
166            .contract
167            .module()
168            .impls()
169            .flat_map(|item_impl| item_impl.iter_messages())
170            .map(|message| {
171                let message_span = message.span();
172                let message_inputs = message.inputs().map(|input| {
173                    let span = input.span();
174                    let input_type = &*input.ty;
175                    quote_spanned!(span=>
176                        ::ink::codegen::utils::consume_type::<
177                            ::ink::codegen::#input_trait<#input_type>
178                        >();
179                    )
180                });
181                let message_output = message.output().map(|output_type| {
182                    let span = output_type.span();
183                    quote_spanned!(span=>
184                        ::ink::codegen::utils::consume_type::<
185                            ::ink::codegen::#output_trait<#output_type>
186                        >();
187                    )
188                });
189                quote_spanned!(message_span=>
190                    #( #message_inputs )*
191                    #message_output
192                )
193            });
194        quote_spanned!(storage_span=>
195            const _: () = {
196                #( #constructor_input_guards )*
197                #( #message_inout_guards )*
198            };
199        )
200    }
201
202    /// Generates the code for the given ink! message within a trait implementation block.
203    fn generate_trait_message(message: &ir::Message) -> TokenStream2 {
204        let span = message.span();
205        let attrs = message.attrs();
206        let vis = message.visibility();
207        let receiver = message.receiver();
208        let ident = message.ident();
209        let output_ident =
210            format_ident!("{}Output", ident.to_string().to_lower_camel_case());
211        let inputs = message.inputs();
212        let output = message
213            .output()
214            .cloned()
215            .unwrap_or_else(|| syn::parse_quote! { () });
216        let statements = message.statements();
217        let cfg_attrs = message.get_cfg_attrs(span);
218        quote_spanned!(span =>
219            #( #cfg_attrs )*
220            type #output_ident = #output;
221
222            #( #attrs )*
223            #vis fn #ident(#receiver #( , #inputs )* ) -> Self::#output_ident {
224                #( #statements )*
225            }
226        )
227    }
228
229    fn generate_trait_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
230        assert!(item_impl.trait_path().is_some());
231        let span = item_impl.span();
232        let attrs = item_impl.attrs();
233        let messages = item_impl
234            .iter_messages()
235            .map(|cws| Self::generate_trait_message(cws.callable()));
236        let trait_path = item_impl
237            .trait_path()
238            .expect("encountered missing trait path for trait impl block");
239        let self_type = item_impl.self_type();
240        quote_spanned!(span =>
241            #( #attrs )*
242            impl #trait_path for #self_type {
243                type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
244                    as #trait_path>::__ink_TraitInfo;
245
246                #( #messages )*
247            }
248        )
249    }
250
251    /// Generates the code for the given ink! constructor within an inherent
252    /// implementation block.
253    ///
254    /// # Developer Note
255    ///
256    /// The `dragonfly` config attribute is used here to convey the
257    /// information that the generated function is an ink! constructor to `dylint`.
258    ///
259    ///
260    /// We decided on this attribute to mark the function, as it has to be a
261    /// key-value pair that is well known to `cargo`. dragonfly seems like an
262    /// obscure target which it is highly unlikely that someone will ever
263    /// compile a contract for.
264    fn generate_inherent_constructor(constructor: &ir::Constructor) -> TokenStream2 {
265        let span = constructor.span();
266        let attrs = constructor.attrs();
267        let vis = constructor.visibility();
268        let ident = constructor.ident();
269        let inputs = constructor.inputs();
270        let statements = constructor.statements();
271        let output = constructor.output();
272        quote_spanned!(span =>
273            #( #attrs )*
274            #[cfg(not(target_os = "dragonfly"))]
275            #vis fn #ident( #( #inputs ),* ) -> #output {
276                #( #statements )*
277            }
278        )
279    }
280
281    /// Generates the code for the given ink! message within an inherent implementation
282    /// block.
283    fn generate_inherent_message(message: &ir::Message) -> TokenStream2 {
284        let span = message.span();
285        let attrs = message.attrs();
286        let vis = message.visibility();
287        let receiver = message.receiver();
288        let ident = message.ident();
289        let inputs = message.inputs();
290        let output_arrow = message.output().map(|_| quote! { -> });
291        let output = message.output();
292        let statements = message.statements();
293        quote_spanned!(span =>
294            #( #attrs )*
295            #vis fn #ident(#receiver #( , #inputs )* ) #output_arrow #output {
296                #( #statements )*
297            }
298        )
299    }
300
301    fn generate_inherent_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
302        assert!(item_impl.trait_path().is_none());
303        let span = item_impl.span();
304        let attrs = item_impl.attrs();
305        let messages = item_impl
306            .iter_messages()
307            .map(|cws| Self::generate_inherent_message(cws.callable()));
308        let constructors = item_impl
309            .iter_constructors()
310            .map(|cws| Self::generate_inherent_constructor(cws.callable()));
311        let other_items = item_impl
312            .items()
313            .iter()
314            .filter_map(ir::ImplItem::filter_map_other_item)
315            .map(ToTokens::to_token_stream);
316        let self_type = item_impl.self_type();
317        quote_spanned!(span =>
318            #( #attrs )*
319            impl #self_type {
320                #( #constructors )*
321                #( #messages )*
322                #( #other_items )*
323            }
324        )
325    }
326
327    /// Generates code to guard against ink! implementations that have not been
328    /// implemented for the ink! storage struct.
329    fn generate_item_impl_self_ty_guard(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
330        let self_ty = item_impl.self_type();
331        let span = self_ty.span();
332        let storage_ident = self.contract.module().storage().ident();
333        quote_spanned!(span =>
334            const _: ::ink::codegen::utils::IsSameType<#storage_ident> =
335                ::ink::codegen::utils::IsSameType::<#self_ty>::new();
336        )
337    }
338
339    /// Generates code for the given ink! implementation block.
340    fn generate_item_impl(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
341        let self_ty_guard = self.generate_item_impl_self_ty_guard(item_impl);
342        let impl_block = match item_impl.trait_path() {
343            Some(_) => Self::generate_trait_item_impl(item_impl),
344            None => Self::generate_inherent_item_impl(item_impl),
345        };
346        quote! {
347            #self_ty_guard
348            #impl_block
349        }
350    }
351}