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