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