ink_codegen/generator/as_dependency/
contract_ref.rs1use crate::{
16 generator,
17 GenerateCode,
18};
19use derive_more::From;
20use ir::{
21 Callable,
22 IsDocAttribute as _,
23};
24use proc_macro2::TokenStream as TokenStream2;
25use quote::{
26 format_ident,
27 quote,
28 quote_spanned,
29};
30use syn::spanned::Spanned as _;
31
32#[derive(From)]
44pub struct ContractRef<'a> {
45 contract: &'a ir::Contract,
46}
47
48impl GenerateCode for ContractRef<'_> {
49 fn generate_code(&self) -> TokenStream2 {
50 let contract_ref = self.generate_struct();
51 let contract_ref_trait_impls = self.generate_contract_trait_impls();
52 let contract_ref_inherent_impls = self.generate_contract_inherent_impls();
53 let call_builder_trait_impl = self.generate_call_builder_trait_impl();
54 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
55 quote! {
56 #contract_ref
57 #contract_ref_trait_impls
58 #contract_ref_inherent_impls
59 #call_builder_trait_impl
60 #auxiliary_trait_impls
61 }
62 }
63}
64
65impl ContractRef<'_> {
66 fn generate_contract_ref_ident(&self) -> syn::Ident {
68 quote::format_ident!("{}Ref", self.contract.module().storage().ident())
69 }
70
71 fn generate_struct(&self) -> TokenStream2 {
78 let span = self.contract.module().storage().span();
79 let doc_attrs = self
80 .contract
81 .module()
82 .storage()
83 .attrs()
84 .iter()
85 .filter(|&x| syn::Attribute::is_doc_attribute(x))
86 .cloned();
87 let storage_ident = self.contract.module().storage().ident();
88 let ref_ident = self.generate_contract_ref_ident();
89 quote_spanned!(span=>
90 #[cfg_attr(feature = "std", derive(
91 ::ink::storage::traits::StorageLayout,
92 ))]
93 #[derive(
94 ::core::fmt::Debug,
95 ::core::hash::Hash,
96 ::core::cmp::PartialEq,
97 ::core::cmp::Eq,
98 ::core::clone::Clone,
99 )]
100 #[::ink::scale_derive(Encode, Decode, TypeInfo)]
101 #( #doc_attrs )*
102 pub struct #ref_ident {
103 inner: <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type,
104 }
105
106 const _: () = {
107 impl ::ink::env::ContractReference for #storage_ident {
108 type Type = #ref_ident;
109 }
110
111 impl ::ink::env::ContractReverseReference for #ref_ident {
112 type Type = #storage_ident;
113 }
114
115 impl ::ink::env::call::ConstructorReturnType<#ref_ident> for #storage_ident {
116 type Output = #ref_ident;
117 type Error = ();
118
119 fn ok(value: #ref_ident) -> Self::Output {
120 value
121 }
122 }
123
124 impl<E> ::ink::env::call::ConstructorReturnType<#ref_ident>
125 for ::core::result::Result<#storage_ident, E>
126 where
127 E: ::ink::scale::Decode
128 {
129 const IS_RESULT: bool = true;
130
131 type Output = ::core::result::Result<#ref_ident, E>;
132 type Error = E;
133
134 fn ok(value: #ref_ident) -> Self::Output {
135 ::core::result::Result::Ok(value)
136 }
137
138 fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
139 ::core::option::Option::Some(::core::result::Result::Err(err))
140 }
141 }
142
143 impl ::ink::env::ContractEnv for #ref_ident {
144 type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
145 }
146 };
147 )
148 }
149
150 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
155 let span = self.contract.module().storage().span();
156 let storage_ident = self.contract.module().storage().ident();
157 let ref_ident = self.generate_contract_ref_ident();
158 quote_spanned!(span=>
159 impl ::ink::env::call::FromAddr for #ref_ident {
160 #[inline]
161 fn from_addr(addr: ::ink::H160) -> Self {
162 Self { inner: <<#storage_ident
163 as ::ink::codegen::ContractCallBuilder>::Type
164 as ::ink::env::call::FromAddr>::from_addr(addr)
165 }
166 }
167 }
168
169 impl ::ink::ToAddr for #ref_ident {
170 #[inline]
171 fn to_addr(&self) -> ::ink::H160 {
172 <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type
173 as ::ink::ToAddr>::to_addr(&self.inner)
174 }
175 }
176
177 impl ::core::convert::AsRef<::ink::H160> for #ref_ident {
178 fn as_ref(&self) -> &::ink::H160 {
179 <_ as ::core::convert::AsRef<::ink::H160>>::as_ref(&self.inner)
180 }
181 }
182
183 impl ::core::convert::AsMut<::ink::H160> for #ref_ident {
184 fn as_mut(&mut self) -> &mut ::ink::H160 {
185 <_ as ::core::convert::AsMut<::ink::H160>>::as_mut(&mut self.inner)
186 }
187 }
188 )
189 }
190
191 fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
196 let span = self.contract.module().storage().span();
197 let ref_ident = self.generate_contract_ref_ident();
198 let storage_ident = self.contract.module().storage().ident();
199 quote_spanned!(span=>
200 const _: () = {
201 impl ::ink::codegen::TraitCallBuilder for #ref_ident {
202 type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type;
203
204 #[inline]
205 fn call(&self) -> &Self::Builder {
206 &self.inner
207 }
208
209 #[inline]
210 fn call_mut(&mut self) -> &mut Self::Builder {
211 &mut self.inner
212 }
213 }
214 };
215 )
216 }
217
218 fn generate_contract_trait_impls(&self) -> TokenStream2 {
225 self.contract
226 .module()
227 .impls()
228 .filter_map(|impl_block| {
229 impl_block.trait_path().map(|trait_path| {
231 self.generate_contract_trait_impl(trait_path, impl_block)
232 })
233 })
234 .collect()
235 }
236
237 fn generate_contract_trait_impl(
242 &self,
243 trait_path: &syn::Path,
244 impl_block: &ir::ItemImpl,
245 ) -> TokenStream2 {
246 let span = impl_block.span();
247 let attrs = impl_block.attrs();
248 let forwarder_ident = self.generate_contract_ref_ident();
249 let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block);
250 quote_spanned!(span=>
251 #( #attrs )*
252 impl #trait_path for #forwarder_ident {
253 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
254 as #trait_path>::__ink_TraitInfo;
255
256 #messages
257 }
258 )
259 }
260
261 fn generate_contract_trait_impl_messages(
264 &self,
265 trait_path: &syn::Path,
266 impl_block: &ir::ItemImpl,
267 ) -> TokenStream2 {
268 impl_block
269 .iter_messages()
270 .map(|message| {
271 self.generate_contract_trait_impl_for_message(trait_path, message)
272 })
273 .collect()
274 }
275
276 fn generate_contract_trait_impl_for_message(
279 &self,
280 trait_path: &syn::Path,
281 message: ir::CallableWithSelector<ir::Message>,
282 ) -> TokenStream2 {
283 use ir::Callable as _;
284 let span = message.span();
285 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
286 let message_ident = message.ident();
287 let output_ident = generator::output_ident(message_ident);
288 let call_operator = match message.receiver() {
289 ir::Receiver::Ref => quote! { call },
290 ir::Receiver::RefMut => quote! { call_mut },
291 };
292 let forward_operator = match message.receiver() {
293 ir::Receiver::Ref => quote! { forward },
294 ir::Receiver::RefMut => quote! { forward_mut },
295 };
296 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
297 let input_idents = generator::input_message_idents(message.inputs());
298 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
299 let cfg_attrs = message.get_cfg_attrs(span);
300 quote_spanned!(span=>
301 #( #cfg_attrs )*
302 type #output_ident =
303 <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder as #trait_path>::#output_ident;
304
305 #[inline]
306 #( #cfg_attrs )*
307 fn #message_ident(
308 & #mut_token self
309 #( , #input_idents : #input_types )*
310 ) -> Self::#output_ident {
311 <_ as #trait_path>::#message_ident(
312 <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
313 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
314 )
315 #( , #input_idents )*
316 )
317 }
318 )
319 }
320
321 fn generate_contract_inherent_impls(&self) -> TokenStream2 {
328 let abi = self.contract.config().abi();
329 self.contract
330 .module()
331 .impls()
332 .filter(|impl_block| {
333 impl_block.trait_path().is_none()
335 })
336 .map(|impl_block| self.generate_contract_inherent_impl(impl_block, abi))
337 .collect()
338 }
339
340 fn generate_contract_inherent_impl(
349 &self,
350 impl_block: &ir::ItemImpl,
351 abi: &ir::Abi,
352 ) -> TokenStream2 {
353 let span = impl_block.span();
354 let attrs = impl_block.attrs();
355 let forwarder_ident = self.generate_contract_ref_ident();
356 let messages = impl_block
357 .iter_messages()
358 .map(|message| self.generate_contract_inherent_impl_for_message(message));
359 let constructors = impl_block.iter_constructors().map(|constructor| {
360 self.generate_contract_inherent_impl_for_constructor(constructor, abi)
361 });
362 quote_spanned!(span=>
363 #( #attrs )*
364 impl #forwarder_ident {
365 #( #constructors )*
366 #( #messages )*
367 }
368 )
369 }
370
371 fn generate_contract_inherent_impl_for_message(
379 &self,
380 message: ir::CallableWithSelector<ir::Message>,
381 ) -> TokenStream2 {
382 use ir::Callable as _;
383 let span = message.span();
384 let attrs = self
385 .contract
386 .config()
387 .whitelisted_attributes()
388 .filter_attr(message.attrs().to_vec());
389 let storage_ident = self.contract.module().storage().ident();
390 let message_ident = message.ident();
391 let try_message_ident = message.try_ident();
392 let call_operator = match message.receiver() {
393 ir::Receiver::Ref => quote! { call },
394 ir::Receiver::RefMut => quote! { call_mut },
395 };
396 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
397 let input_idents = generator::input_message_idents(message.inputs());
398 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
399 let output_type = message.output().map(|ty| quote! { -> #ty });
400 let wrapped_output_type = message.wrapped_output();
401 quote_spanned!(span=>
402 #( #attrs )*
403 #[inline]
404 pub fn #message_ident(
405 & #mut_token self
406 #( , #input_idents : #input_types )*
407 ) #output_type {
408 self.#try_message_ident( #( #input_idents, )* )
409 .unwrap_or_else(|error| ::core::panic!(
410 "encountered error while calling {}::{}: {:?}",
411 ::core::stringify!(#storage_ident),
412 ::core::stringify!(#message_ident),
413 error,
414 ))
415 }
416
417 #( #attrs )*
418 #[inline]
419 pub fn #try_message_ident(
420 & #mut_token self
421 #( , #input_idents : #input_types )*
422 ) -> #wrapped_output_type {
423 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
424 .#message_ident( #( #input_idents ),* )
425 .try_invoke()
426 .unwrap_or_else(|error| ::core::panic!(
427 "encountered error while calling {}::{}: {:?}",
428 ::core::stringify!(#storage_ident),
429 ::core::stringify!(#message_ident),
430 error,
431 ))
432 }
433 )
434 }
435
436 fn generate_contract_inherent_impl_for_constructor(
444 &self,
445 constructor: ir::CallableWithSelector<ir::Constructor>,
446 abi: &ir::Abi,
447 ) -> TokenStream2 {
448 let span = constructor.span();
449 let attrs = self
450 .contract
451 .config()
452 .whitelisted_attributes()
453 .filter_attr(constructor.attrs().to_vec());
454 let constructor_ident = constructor.ident();
455 let selector_bytes = constructor.composed_selector().hex_lits();
456 let input_bindings = generator::input_bindings(constructor.inputs());
457 let input_types = generator::input_types(constructor.inputs());
458 let _storage_ident = self.contract.module().storage().ident();
459 let ret_type = constructor
460 .output()
461 .map(quote::ToTokens::to_token_stream)
462 .unwrap_or_else(|| quote::quote! { Self });
463
464 let mut create_builders = Vec::new();
465 if abi.is_ink() {
466 let arg_list = generator::generate_argument_list(
467 input_types.iter().cloned(),
468 quote!(::ink::reflect::ScaleEncoding),
469 );
470
471 let create_builder = quote_spanned!(span =>
472 #( #attrs )*
473 #[inline]
474 #[allow(clippy::type_complexity)]
475 pub fn #constructor_ident(
476 #( #input_bindings : #input_types ),*
477 ) -> ::ink::env::call::CreateBuilder<
478 Environment,
479 Self,
480 ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
481 ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::ScaleEncoding>>,
482 ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
483 > {
484 ::ink::env::call::build_create::<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::<#ret_type>()
494 }
495 );
496 create_builders.push(create_builder);
497 }
498
499 if abi.is_solidity() {
500 let sol_constructor_ident = if abi.is_all() {
503 format_ident!("{}_sol", constructor_ident)
504 } else {
505 constructor_ident.clone()
506 };
507 let arg_list = generator::generate_argument_list(
508 input_types.iter().cloned(),
509 quote!(::ink::reflect::SolEncoding),
510 );
511
512 let create_builder = quote_spanned!(span =>
513 #( #attrs )*
514 #[inline]
515 #[allow(clippy::type_complexity)]
516 pub fn #sol_constructor_ident(
517 #( #input_bindings : #input_types ),*
518 ) -> ::ink::env::call::CreateBuilder<
519 Environment,
520 Self,
521 ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
522 ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::SolEncoding>>,
523 ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
524 > {
525 ::ink::env::call::build_create_solidity::<Self>()
526 .exec_input(
527 ::ink::env::call::ExecutionInput::new(
528 ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
529 )
530 #(
531 .push_arg(#input_bindings)
532 )*
533 )
534 .returns::<#ret_type>()
535 }
536 );
537 create_builders.push(create_builder);
538 }
539
540 quote_spanned!(span=>
541 #( #create_builders )*
542 )
543 }
544}