ink_codegen/generator/trait_def/
message_builder.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 derive_more::From;
16use ink_primitives::abi::Abi;
17use proc_macro2::{
18    Span,
19    TokenStream as TokenStream2,
20};
21use quote::{
22    quote,
23    quote_spanned,
24};
25
26use super::TraitDefinition;
27use crate::{
28    generator,
29    generator::sol,
30    traits::GenerateCode,
31};
32
33impl TraitDefinition<'_> {
34    /// Generates code for the global trait call builder for an ink! trait.
35    ///
36    /// # Note
37    ///
38    /// - The generated call builder type implements the ink! trait definition and allows
39    ///   to build up contract calls that allow for customization by the user to provide
40    ///   gas limit, endowment etc.
41    /// - The call builder is used directly by the generated call forwarder. There exists
42    ///   one global call forwarder and call builder pair for every ink! trait definition.
43    pub fn generate_message_builder(&self, abi: Option<Abi>) -> TokenStream2 {
44        MessageBuilder::from((*self, abi)).generate_code()
45    }
46
47    /// The identifier of the ink! trait message builder.
48    pub fn message_builder_ident(&self) -> syn::Ident {
49        self.append_trait_suffix(MessageBuilder::SUFFIX)
50    }
51}
52
53/// Generates code for the global ink! trait call builder.
54#[derive(From)]
55struct MessageBuilder<'a> {
56    trait_def: TraitDefinition<'a>,
57    abi: Option<Abi>,
58}
59
60impl GenerateCode for MessageBuilder<'_> {
61    fn generate_code(&self) -> TokenStream2 {
62        let struct_definition = self.generate_struct_definition();
63        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
64        let ink_trait_impl = self.generate_ink_trait_impl();
65        quote! {
66            #struct_definition
67            #auxiliary_trait_impls
68            #ink_trait_impl
69        }
70    }
71}
72
73impl MessageBuilder<'_> {
74    /// The name suffix for ink! trait message builder.
75    const SUFFIX: &'static str = "TraitMessageBuilder";
76
77    /// Returns the span of the ink! trait definition.
78    fn span(&self) -> Span {
79        self.trait_def.span()
80    }
81
82    /// Generates the struct type definition for the account wrapper type.
83    ///
84    /// This type is going to implement the trait so that invoking its trait
85    /// methods will return an `Execution` instance populated with the message selector,
86    /// arguments and return type, which can then be executed via the `exec` method.
87    fn generate_struct_definition(&self) -> TokenStream2 {
88        let span = self.span();
89        let message_builder_ident = self.trait_def.message_builder_ident();
90        quote_spanned!(span =>
91            /// The global call builder type for all trait implementers.
92            ///
93            /// All calls to types (contracts) implementing the trait will be built by this type.
94            #[doc(hidden)]
95            #[allow(non_camel_case_types)]
96            #[::ink::scale_derive(Encode, Decode, TypeInfo)]
97            pub struct #message_builder_ident<E, Abi> {
98                _marker: ::core::marker::PhantomData<fn() -> (E, Abi)>,
99            }
100        )
101    }
102
103    /// Generates trait implementations for auxiliary traits for the message builder.
104    ///
105    /// # Note
106    ///
107    /// Auxiliary traits currently include:
108    ///
109    /// - `Default`: To allow initializing a contract message builder.
110    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
111        let span = self.span();
112        let message_builder_ident = self.trait_def.message_builder_ident();
113        let sol_codec = if matches!(self.abi, Some(Abi::Sol))
114            || cfg!(any(ink_abi = "sol", ink_abi = "all"))
115        {
116            // These manual implementations are a bit more efficient than the derived
117            // equivalents.
118            quote_spanned!(span=>
119                impl<E, Abi> ::ink::SolDecode for #message_builder_ident<E, Abi>
120                where
121                    E: ::ink::env::Environment,
122                {
123                    type SolType = ();
124
125                    fn from_sol_type(_: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
126                        Ok(Self {
127                            _marker: ::core::marker::PhantomData,
128                        })
129                    }
130                }
131
132                impl<'a, E, Abi> ::ink::SolEncode<'a> for #message_builder_ident<E, Abi>
133                where
134                    E: ::ink::env::Environment,
135                {
136                    type SolType = ();
137
138                    fn to_sol_type(&'a self) {}
139                }
140            )
141        } else {
142            quote!()
143        };
144        quote_spanned!(span=>
145            impl<E, Abi> ::core::default::Default for #message_builder_ident<E, Abi>
146            where
147                E: ::ink::env::Environment,
148            {
149                fn default() -> Self {
150                    Self {
151                        _marker: ::core::default::Default::default()
152                    }
153                }
154            }
155
156            impl<E, Abi> ::ink::env::ContractEnv for #message_builder_ident<E, Abi>
157            where
158                E: ::ink::env::Environment,
159            {
160                type Env = E;
161            }
162
163            #sol_codec
164        )
165    }
166
167    /// Generates the implementation of the associated ink! trait definition.
168    ///
169    /// # Note
170    ///
171    /// The implemented messages create an instance of the `Execution` type which
172    /// encapsulates the execution input (the selector and arguments) and the output
173    /// type of the message.
174    fn generate_ink_trait_impl(&self) -> TokenStream2 {
175        let span = self.trait_def.span();
176        let trait_ident = self.trait_def.trait_def.item().ident();
177        let trait_info_ident = self.trait_def.trait_info_ident();
178        let message_builder_ident = self.trait_def.message_builder_ident();
179        let generator = |abi| {
180            let abi_ty = match abi {
181                Abi::Ink => quote!(::ink::abi::Ink),
182                Abi::Sol => quote!(::ink::abi::Sol),
183            };
184            let message_impls = self.generate_ink_trait_impl_messages(abi);
185            quote_spanned!(span=>
186                impl<E> #trait_ident for #message_builder_ident<E, #abi_ty>
187                where
188                    E: ::ink::env::Environment,
189                {
190                    #[allow(non_camel_case_types)]
191                    type __ink_TraitInfo = #trait_info_ident<E>;
192
193                    #message_impls
194                }
195            )
196        };
197        match self.abi {
198            None => generate_abi_impls!(@type generator),
199            Some(abi) => generator(abi),
200        }
201    }
202
203    /// Generate the code for all ink! trait messages implemented by the trait call
204    /// builder.
205    fn generate_ink_trait_impl_messages(&self, abi: Abi) -> TokenStream2 {
206        let messages = self.trait_def.trait_def.item().iter_items().filter_map(
207            |(item, selector)| {
208                item.filter_map_message().map(|message| {
209                    let (selector_bytes, abi_ty) = match abi {
210                        Abi::Ink => {
211                            let selector_bytes = selector.hex_lits();
212                            (quote!([ #( #selector_bytes ),* ]), quote!(::ink::abi::Ink))
213                        }
214                        Abi::Sol => {
215                            let name = message.normalized_name();
216                            let signature =
217                                sol::utils::call_signature(name, message.inputs());
218                            (
219                                quote!(::ink::codegen::sol::selector_bytes(#signature)),
220                                quote!(::ink::abi::Sol),
221                            )
222                        }
223                    };
224                    self.generate_ink_trait_impl_for_message(
225                        &message,
226                        selector_bytes,
227                        abi_ty,
228                    )
229                })
230            },
231        );
232        quote! {
233            #( #messages )*
234        }
235    }
236
237    /// Generate the code for a single ink! trait message implemented by the trait call
238    /// builder.
239    fn generate_ink_trait_impl_for_message(
240        &self,
241        message: &ir::InkTraitMessage,
242        selector_bytes: TokenStream2,
243        abi: TokenStream2,
244    ) -> TokenStream2 {
245        let span = message.span();
246        let message_ident = message.ident();
247        let attrs = self
248            .trait_def
249            .trait_def
250            .config()
251            .whitelisted_attributes()
252            .filter_attr(message.attrs());
253        let output_ident = generator::output_ident(message_ident);
254        let output = message.output();
255        let output_type =
256            output.map_or_else(|| quote! { () }, |output| quote! { #output });
257        let input_bindings = generator::input_bindings(message.inputs());
258        let input_types = generator::input_types(message.inputs());
259        let mut_tok = message.mutates().then(|| quote! { mut });
260        let arg_list =
261            generator::generate_argument_list(input_types.iter().cloned(), abi.clone());
262        let cfg_attrs = message.get_cfg_attrs(span);
263        quote_spanned!(span =>
264            #( #cfg_attrs )*
265            type #output_ident = ::ink::env::call::Execution<
266                #arg_list,
267                #output_type,
268                #abi
269            >;
270
271            #( #attrs )*
272            #[inline]
273            fn #message_ident(
274                & #mut_tok self
275                #( , #input_bindings : #input_types )*
276            ) -> Self::#output_ident {
277                ::ink::env::call::Execution::new(
278                    ::ink::env::call::ExecutionInput::new(
279                        ::ink::env::call::Selector::new(#selector_bytes)
280                    )
281                    #(
282                        .push_arg(#input_bindings)
283                    )*
284                )
285            }
286        )
287    }
288}