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