ink_codegen/generator/as_dependency/
call_builder.rs1use crate::{
16 generator,
17 generator::solidity::solidity_selector,
18 GenerateCode,
19};
20use derive_more::From;
21use ir::Callable;
22use proc_macro2::TokenStream as TokenStream2;
23use quote::{
24 format_ident,
25 quote,
26 quote_spanned,
27};
28use syn::spanned::Spanned as _;
29
30#[derive(From)]
42pub struct CallBuilder<'a> {
43 contract: &'a ir::Contract,
44}
45
46impl GenerateCode for CallBuilder<'_> {
47 fn generate_code(&self) -> TokenStream2 {
48 let call_builder_struct = self.generate_struct();
49 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
50 let call_builder_impls = self.generate_call_forwarder_impls();
51 let call_builder_inherent_impls = self.generate_call_builder_inherent_impls();
52 quote! {
53 const _: () = {
54 #call_builder_struct
55 #auxiliary_trait_impls
56 #call_builder_impls
57 #call_builder_inherent_impls
58 };
59 }
60 }
61}
62
63impl CallBuilder<'_> {
64 fn call_builder_ident() -> syn::Ident {
73 format_ident!("CallBuilder")
74 }
75
76 fn generate_struct(&self) -> TokenStream2 {
77 let span = self.contract.module().storage().span();
78 let storage_ident = self.contract.module().storage().ident();
79 let cb_ident = Self::call_builder_ident();
80 quote_spanned!(span=>
81 #[repr(transparent)]
86 #[cfg_attr(feature = "std", derive(
87 ::ink::storage::traits::StorageLayout,
88 ))]
89 #[derive(
90 ::core::fmt::Debug,
91 ::core::hash::Hash,
92 ::core::cmp::PartialEq,
93 ::core::cmp::Eq,
94 ::core::clone::Clone,
95 )]
96 #[::ink::scale_derive(Encode, Decode, TypeInfo)]
97 pub struct #cb_ident {
98 addr: ::ink::H160,
99 }
100
101 const _: () = {
102 impl ::ink::codegen::ContractCallBuilder for #storage_ident {
103 type Type = #cb_ident;
104 }
105
106 impl ::ink::env::ContractEnv for #cb_ident {
107 type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
108 }
109 };
110 )
111 }
112
113 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
118 let span = self.contract.module().storage().span();
119 let cb_ident = Self::call_builder_ident();
120 quote_spanned!(span=>
121 impl ::ink::env::call::FromAddr for #cb_ident {
122 #[inline]
123 fn from_addr(addr: ::ink::H160) -> Self {
124 Self { addr }
125 }
126 }
127
128 impl ::ink::ToAddr for #cb_ident {
129 #[inline]
130 fn to_addr(&self) -> ::ink::H160 {
131 <::ink::H160 as ::core::clone::Clone>::clone(&self.addr)
132 }
133 }
134
135 impl ::core::convert::AsRef<::ink::H160> for #cb_ident {
136 fn as_ref(&self) -> &::ink::H160 {
137 &self.addr
138 }
139 }
140
141 impl ::core::convert::AsMut<::ink::H160> for #cb_ident {
142 fn as_mut(&mut self) -> &mut ::ink::H160 {
143 &mut self.addr
144 }
145 }
146 )
147 }
148
149 fn generate_call_forwarder_impls(&self) -> TokenStream2 {
157 self.contract
158 .module()
159 .impls()
160 .filter_map(|impl_block| {
161 impl_block.trait_path().map(|trait_path| {
163 self.generate_code_for_trait_impl(trait_path, impl_block)
164 })
165 })
166 .collect()
167 }
168
169 fn generate_code_for_trait_impl(
172 &self,
173 trait_path: &syn::Path,
174 impl_block: &ir::ItemImpl,
175 ) -> TokenStream2 {
176 let call_forwarder_impl =
177 self.generate_call_forwarder_for_trait_impl(trait_path, impl_block);
178 let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block);
179 quote! {
180 #call_forwarder_impl
181 #ink_trait_impl
182 }
183 }
184
185 fn generate_call_forwarder_for_trait_impl(
188 &self,
189 trait_path: &syn::Path,
190 impl_block: &ir::ItemImpl,
191 ) -> TokenStream2 {
192 let span = impl_block.span();
193 let cb_ident = Self::call_builder_ident();
194 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
195 quote_spanned!(span=>
196 #[doc(hidden)]
197 impl ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}> for #cb_ident {
198 type Forwarder = <<Self as #trait_path>::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder;
199
200 #[inline]
201 fn forward(&self) -> &Self::Forwarder {
202 unsafe {
210 &*(&self.addr as *const ::ink::H160 as *const Self::Forwarder)
211 }
212 }
213
214 #[inline]
215 fn forward_mut(&mut self) -> &mut Self::Forwarder {
216 unsafe {
223 &mut *(&mut self.addr as *mut ::ink::H160 as *mut Self::Forwarder)
224 }
225 }
226
227 #[inline]
228 fn build(&self) -> &<Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder {
229 <_ as ::ink::codegen::TraitCallBuilder>::call(
230 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward(self)
231 )
232 }
233
234 #[inline]
235 fn build_mut(&mut self)
236 -> &mut <Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder
237 {
238 <_ as ::ink::codegen::TraitCallBuilder>::call_mut(
239 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward_mut(self)
240 )
241 }
242 }
243 )
244 }
245
246 fn generate_ink_trait_impl(
248 &self,
249 trait_path: &syn::Path,
250 impl_block: &ir::ItemImpl,
251 ) -> TokenStream2 {
252 let span = impl_block.span();
253 let cb_ident = Self::call_builder_ident();
254 let messages = impl_block
255 .iter_messages()
256 .map(|message| self.generate_ink_trait_impl_for_message(trait_path, message));
257 quote_spanned!(span=>
258 impl #trait_path for #cb_ident {
259 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
260 as #trait_path>::__ink_TraitInfo;
261
262 #( #messages )*
263 }
264 )
265 }
266
267 fn generate_ink_trait_impl_for_message(
270 &self,
271 trait_path: &syn::Path,
272 message: ir::CallableWithSelector<ir::Message>,
273 ) -> TokenStream2 {
274 use ir::Callable as _;
275 let span = message.span();
276 let message_ident = message.ident();
277 let output_ident = generator::output_ident(message_ident);
278 let cfg_attrs = message.get_cfg_attrs(span);
279 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
280 let (input_bindings, input_types): (Vec<_>, Vec<_>) = message
281 .callable()
282 .inputs()
283 .map(|input| (&input.pat, &input.ty))
284 .unzip();
285 let mut_token = message
286 .receiver()
287 .is_ref_mut()
288 .then(|| Some(quote! { mut }));
289 let build_cmd = match message.receiver() {
290 ir::Receiver::Ref => quote! { build },
291 ir::Receiver::RefMut => quote! { build_mut },
292 };
293 let attrs = self
294 .contract
295 .config()
296 .whitelisted_attributes()
297 .filter_attr(message.attrs().to_vec());
298 quote_spanned!(span=>
299 #( #cfg_attrs )*
300 type #output_ident = <<<
301 Self
302 as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::Forwarder
303 as ::ink::codegen::TraitCallBuilder>::Builder
304 as #trait_path>::#output_ident;
305
306 #[inline]
307 #( #attrs )*
308 fn #message_ident(
309 & #mut_token self
310 #( , #input_bindings: #input_types )*
311 ) -> Self::#output_ident {
312 <_ as #trait_path>::#message_ident(
313 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#build_cmd(self)
314 #( , #input_bindings )*
315 )
316 }
317 )
318 }
319
320 fn generate_call_builder_inherent_impls(&self) -> TokenStream2 {
327 let abi = self.contract.config().abi();
328 self.contract
329 .module()
330 .impls()
331 .filter(|impl_block| impl_block.trait_path().is_none())
332 .map(|impl_block| self.generate_call_builder_inherent_impl(impl_block, abi))
333 .collect()
334 }
335
336 fn generate_call_builder_inherent_impl(
344 &self,
345 impl_block: &ir::ItemImpl,
346 abi: &ir::Abi,
347 ) -> TokenStream2 {
348 let span = impl_block.span();
349 let cb_ident = Self::call_builder_ident();
350 let messages = impl_block.iter_messages().map(|message| {
351 self.generate_call_builder_inherent_impl_for_message(message, abi)
352 });
353 quote_spanned!(span=>
354 impl #cb_ident {
355 #( #messages )*
356 }
357 )
358 }
359
360 fn generate_call_builder_inherent_impl_for_message(
367 &self,
368 message: ir::CallableWithSelector<ir::Message>,
369 abi: &ir::Abi,
370 ) -> TokenStream2 {
371 let span = message.span();
372 let callable = message.callable();
373 let message_ident = message.ident();
374 let attrs = self
375 .contract
376 .config()
377 .whitelisted_attributes()
378 .filter_attr(message.attrs().to_vec());
379 let input_bindings = generator::input_bindings(callable.inputs());
380 let input_types = generator::input_types(message.inputs());
381 let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut });
382 let return_type = message
383 .output()
384 .map(quote::ToTokens::to_token_stream)
385 .unwrap_or_else(|| quote::quote! { () });
386 let output_span = return_type.span();
387
388 let mut call_builders = Vec::new();
389 if abi.is_ink() {
390 let selector = message.composed_selector();
391 let selector_bytes = selector.hex_lits();
392 let arg_list = generator::generate_argument_list(
393 input_types.iter().cloned(),
394 quote!(::ink::reflect::ScaleEncoding),
395 );
396 let output_type = quote_spanned!(output_span=>
397 ::ink::env::call::CallBuilder<
398 Environment,
399 ::ink::env::call::utils::Set< ::ink::env::call::Call >,
400 ::ink::env::call::utils::Set< ::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::ScaleEncoding> >,
401 ::ink::env::call::utils::Set< ::ink::env::call::utils::ReturnType<#return_type> >,
402 >
403 );
404
405 let call_builder = quote_spanned!(span=>
406 #( #attrs )*
407 #[allow(clippy::type_complexity)]
408 #[inline]
409 pub fn #message_ident(
410 & #mut_tok self
411 #( , #input_bindings : #input_types )*
412 ) -> #output_type {
413 ::ink::env::call::build_call::<Environment>()
414 .call(::ink::ToAddr::to_addr(self))
415 .exec_input(
416 ::ink::env::call::ExecutionInput::new(
417 ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
418 )
419 #(
420 .push_arg(#input_bindings)
421 )*
422 )
423 .returns::<#return_type>()
424 }
425 );
426 call_builders.push(call_builder);
427 }
428
429 if abi.is_solidity() {
430 let sol_message_ident = if abi.is_all() {
433 format_ident!("{}_sol", message_ident)
434 } else {
435 message_ident.clone()
436 };
437 let selector_bytes = solidity_selector(&message);
438 let arg_list = generator::generate_argument_list(
439 input_types.iter().cloned(),
440 quote!(::ink::reflect::SolEncoding),
441 );
442 let output_type = quote_spanned!(output_span=>
443 ::ink::env::call::CallBuilder<
444 Environment,
445 ::ink::env::call::utils::Set< ::ink::env::call::Call >,
446 ::ink::env::call::utils::Set< ::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::SolEncoding> >,
447 ::ink::env::call::utils::Set< ::ink::env::call::utils::ReturnType<#return_type> >,
448 >
449 );
450
451 let call_builder = quote_spanned!(span=>
452 #( #attrs )*
453 #[allow(clippy::type_complexity)]
454 #[inline]
455 pub fn #sol_message_ident (
456 & #mut_tok self
457 #( , #input_bindings : #input_types )*
458 ) -> #output_type {
459 ::ink::env::call::build_call_solidity::<Environment>()
460 .call(::ink::ToAddr::to_addr(self))
461 .exec_input(
462 ::ink::env::call::ExecutionInput::new(
463 ::ink::env::call::Selector::new(#selector_bytes)
464 )
465 #(
466 .push_arg(#input_bindings)
467 )*
468 )
469 .returns::<#return_type>()
470 }
471 );
472 call_builders.push(call_builder);
473 }
474
475 quote_spanned!(span=>
476 #( #call_builders )*
477 )
478 }
479}