ink_codegen/generator/as_dependency/
call_builder.rs1use derive_more::From;
16use ink_primitives::abi::Abi;
17use ir::Callable;
18use proc_macro2::TokenStream as TokenStream2;
19use quote::{
20 format_ident,
21 quote,
22 quote_spanned,
23};
24use syn::spanned::Spanned as _;
25
26use crate::{
27 generator,
28 generator::sol,
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 let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
83 quote_spanned!(span=>
86 impl<Abi> ::ink::SolDecode for #cb_ident<Abi> {
87 type SolType = ::ink::Address;
88
89 fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
90 Ok(Self {
91 addr: value,
92 _marker: ::core::marker::PhantomData,
93 })
94 }
95 }
96
97 impl<'a, Abi> ::ink::SolEncode<'a> for #cb_ident<Abi> {
98 type SolType = &'a ::ink::Address;
99
100 fn to_sol_type(&'a self) -> Self::SolType {
101 &self.addr
102 }
103 }
104 )
105 } else {
106 quote!()
107 };
108 quote_spanned!(span=>
109 #[repr(transparent)]
114 #[derive(
115 ::core::fmt::Debug,
116 ::core::hash::Hash,
117 ::core::cmp::PartialEq,
118 ::core::cmp::Eq,
119 ::core::clone::Clone,
120 )]
121 #[::ink::scale_derive(Encode, Decode)]
122 pub struct #cb_ident<Abi> {
123 addr: ::ink::Address,
124 _marker: core::marker::PhantomData<Abi>,
125 }
126
127 const _: () = {
128 impl ::ink::codegen::ContractCallBuilder for #storage_ident {
129 type Type<Abi> = #cb_ident<Abi>;
130 }
131
132 impl<Abi> ::ink::env::ContractEnv for #cb_ident<Abi> {
133 type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
134 }
135
136 #[cfg(feature = "std")]
137 impl<Abi> ::ink::storage::traits::StorageLayout for #cb_ident<Abi> {
138 fn layout(
139 __key: &::ink::primitives::Key,
140 ) -> ::ink::metadata::layout::Layout {
141 ::ink::metadata::layout::Layout::Struct(
142 ::ink::metadata::layout::StructLayout::new(
143 ::core::stringify!(#cb_ident),
144 [
145 ::ink::metadata::layout::FieldLayout::new(
146 "addr",
147 <::ink::Address
148 as ::ink::storage::traits::StorageLayout>::layout(__key)
149 )
150 ]
151 )
152 )
153 }
154 }
155
156 #[cfg(feature = "std")]
157 impl<Abi> ::ink::scale_info::TypeInfo for #cb_ident<Abi>
159 where
160 ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
161 {
162 type Identity = ::ink::Address;
163
164 fn type_info() -> ::ink::scale_info::Type {
165 <::ink::Address as ::ink::scale_info::TypeInfo>::type_info()
166 }
167 }
168
169 #sol_codec
170 };
171 )
172 }
173
174 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
179 let span = self.contract.module().storage().span();
180 let cb_ident = Self::call_builder_ident();
181 quote_spanned!(span=>
182 impl<Abi> ::ink::env::call::FromAddr for #cb_ident<Abi> {
183 #[inline]
184 fn from_addr(addr: ::ink::Address) -> Self {
185 Self { addr, _marker: ::core::default::Default::default(), }
186 }
187 }
188
189 impl<Abi> ::ink::ToAddr for #cb_ident<Abi> {
190 #[inline]
191 fn to_addr(&self) -> ::ink::Address {
192 <::ink::Address as ::core::clone::Clone>::clone(&self.addr)
193 }
194 }
195
196 impl<Abi> ::core::convert::AsRef<::ink::Address> for #cb_ident<Abi> {
197 fn as_ref(&self) -> &::ink::Address {
198 &self.addr
199 }
200 }
201
202 impl<Abi> ::core::convert::AsMut<::ink::Address> for #cb_ident<Abi> {
203 fn as_mut(&mut self) -> &mut ::ink::Address {
204 &mut self.addr
205 }
206 }
207 )
208 }
209
210 fn generate_call_forwarder_impls(&self) -> TokenStream2 {
218 self.contract
219 .module()
220 .impls()
221 .filter_map(|impl_block| {
222 impl_block.trait_path().map(|trait_path| {
224 self.generate_code_for_trait_impl(trait_path, impl_block)
225 })
226 })
227 .collect()
228 }
229
230 fn generate_code_for_trait_impl(
233 &self,
234 trait_path: &syn::Path,
235 impl_block: &ir::ItemImpl,
236 ) -> TokenStream2 {
237 let call_forwarder_impl =
238 self.generate_call_forwarder_for_trait_impl(trait_path, impl_block);
239 let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block);
240 quote! {
241 #call_forwarder_impl
242 #ink_trait_impl
243 }
244 }
245
246 fn generate_call_forwarder_for_trait_impl(
249 &self,
250 trait_path: &syn::Path,
251 impl_block: &ir::ItemImpl,
252 ) -> TokenStream2 {
253 let span = impl_block.span();
254 let cb_ident = Self::call_builder_ident();
255 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
256 generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
257 #[doc(hidden)]
258 impl ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}> for #cb_ident<#abi> {
259 type Forwarder = <<Self as #trait_path>::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder<#abi>;
260
261 #[inline]
262 fn forward(&self) -> &Self::Forwarder {
263 unsafe {
271 &*(&self.addr as *const ::ink::Address as *const Self::Forwarder)
272 }
273 }
274
275 #[inline]
276 fn forward_mut(&mut self) -> &mut Self::Forwarder {
277 unsafe {
284 &mut *(&mut self.addr as *mut ::ink::Address as *mut Self::Forwarder)
285 }
286 }
287
288 #[inline]
289 fn build(&self) -> &<Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder {
290 <_ as ::ink::codegen::TraitCallBuilder>::call(
291 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward(self)
292 )
293 }
294
295 #[inline]
296 fn build_mut(&mut self)
297 -> &mut <Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder
298 {
299 <_ as ::ink::codegen::TraitCallBuilder>::call_mut(
300 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward_mut(self)
301 )
302 }
303 }
304 ))
305 }
306
307 fn generate_ink_trait_impl(
309 &self,
310 trait_path: &syn::Path,
311 impl_block: &ir::ItemImpl,
312 ) -> TokenStream2 {
313 let span = impl_block.span();
314 let cb_ident = Self::call_builder_ident();
315 let messages = impl_block
316 .iter_messages()
317 .map(|message| self.generate_ink_trait_impl_for_message(trait_path, message));
318 let messages = quote! {
319 #( #messages )*
320 };
321 generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
322 impl #trait_path for #cb_ident<#abi> {
323 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
324 as #trait_path>::__ink_TraitInfo;
325
326 #messages
327 }
328 ))
329 }
330
331 fn generate_ink_trait_impl_for_message(
334 &self,
335 trait_path: &syn::Path,
336 message: ir::CallableWithSelector<ir::Message>,
337 ) -> TokenStream2 {
338 use ir::Callable as _;
339 let span = message.span();
340 let message_ident = message.ident();
341 let output_ident = generator::output_ident(message_ident);
342 let cfg_attrs = message.get_cfg_attrs(span);
343 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
344 let (input_bindings, input_types): (Vec<_>, Vec<_>) = message
345 .callable()
346 .inputs()
347 .map(|input| (&input.pat, &input.ty))
348 .unzip();
349 let mut_token = message
350 .receiver()
351 .is_ref_mut()
352 .then(|| Some(quote! { mut }));
353 let build_cmd = match message.receiver() {
354 ir::Receiver::Ref => quote! { build },
355 ir::Receiver::RefMut => quote! { build_mut },
356 };
357 let attrs = self
358 .contract
359 .config()
360 .whitelisted_attributes()
361 .filter_attr(message.attrs().to_vec());
362 quote_spanned!(span=>
363 #( #cfg_attrs )*
364 type #output_ident = <<<
365 Self
366 as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::Forwarder
367 as ::ink::codegen::TraitCallBuilder>::Builder
368 as #trait_path>::#output_ident;
369
370 #[inline]
371 #( #attrs )*
372 fn #message_ident(
373 & #mut_token self
374 #( , #input_bindings: #input_types )*
375 ) -> Self::#output_ident {
376 <_ as #trait_path>::#message_ident(
377 <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#build_cmd(self)
378 #( , #input_bindings )*
379 )
380 }
381 )
382 }
383
384 fn generate_call_builder_inherent_impls(&self) -> TokenStream2 {
391 self.contract
392 .module()
393 .impls()
394 .filter(|impl_block| impl_block.trait_path().is_none())
395 .map(|impl_block| self.generate_call_builder_inherent_impl(impl_block))
396 .collect()
397 }
398
399 fn generate_call_builder_inherent_impl(
407 &self,
408 impl_block: &ir::ItemImpl,
409 ) -> TokenStream2 {
410 let span = impl_block.span();
411 let cb_ident = Self::call_builder_ident();
412 generate_abi_impls!(@type |abi| {
413 let abi_ty = match abi {
414 Abi::Ink => quote!(::ink::abi::Ink),
415 Abi::Sol => quote!(::ink::abi::Sol),
416 };
417 let messages = impl_block.iter_messages().map(|message| {
418 self.generate_call_builder_inherent_impl_for_message(message, abi)
419 });
420 quote_spanned!(span=>
421 impl #cb_ident<#abi_ty> {
422 #( #messages )*
423 }
424 )
425 })
426 }
427
428 fn generate_call_builder_inherent_impl_for_message(
435 &self,
436 message: ir::CallableWithSelector<ir::Message>,
437 abi: Abi,
438 ) -> TokenStream2 {
439 let span = message.span();
440 let callable = message.callable();
441 let message_ident = message.ident();
442 let attrs = self
443 .contract
444 .config()
445 .whitelisted_attributes()
446 .filter_attr(message.attrs().to_vec());
447 let input_bindings = generator::input_bindings(callable.inputs());
448 let input_types = generator::input_types(message.inputs());
449 let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut });
450 let return_type = message
451 .output()
452 .map(quote::ToTokens::to_token_stream)
453 .unwrap_or_else(|| quote::quote! { () });
454 let output_span = return_type.span();
455 let (selector_bytes, abi_ty) = match abi {
456 Abi::Ink => {
457 let selector = message.composed_selector();
458 let selector_bytes = selector.hex_lits();
459 (quote!([ #( #selector_bytes ),* ]), quote!(::ink::abi::Ink))
460 }
461 Abi::Sol => (sol::utils::selector(&message), quote!(::ink::abi::Sol)),
462 };
463 let arg_list = generator::generate_argument_list(
464 input_types.iter().cloned(),
465 abi_ty.clone(),
466 );
467 let output_type = quote_spanned!(output_span=>
468 ::ink::env::call::CallBuilder<
469 Environment,
470 ::ink::env::call::utils::Set< ::ink::env::call::Call >,
471 ::ink::env::call::utils::Set< ::ink::env::call::ExecutionInput<#arg_list, #abi_ty> >,
472 ::ink::env::call::utils::Set< ::ink::env::call::utils::ReturnType<#return_type> >,
473 >
474 );
475 quote_spanned!(span=>
476 #( #attrs )*
477 #[allow(clippy::type_complexity)]
478 #[inline]
479 pub fn #message_ident(
480 & #mut_tok self
481 #( , #input_bindings : #input_types )*
482 ) -> #output_type {
483 ::ink::env::call::build_call_abi::<Environment, #abi_ty>()
484 .call(::ink::ToAddr::to_addr(self))
485 .exec_input(
486 ::ink::env::call::ExecutionInput::new(
487 ::ink::env::call::Selector::new(#selector_bytes)
488 )
489 #(
490 .push_arg(#input_bindings)
491 )*
492 )
493 .returns::<#return_type>()
494 }
495 )
496 }
497}