ink_codegen/generator/as_dependency/
contract_ref.rs1use derive_more::From;
16use ink_primitives::abi::Abi;
17use ir::{
18 Callable,
19 IsDocAttribute as _,
20};
21use itertools::Itertools;
22use proc_macro2::TokenStream as TokenStream2;
23use quote::{
24 quote,
25 quote_spanned,
26};
27use syn::spanned::Spanned as _;
28
29use crate::{
30 generator,
31 GenerateCode,
32};
33
34#[derive(From)]
46pub struct ContractRef<'a> {
47 contract: &'a ir::Contract,
48}
49
50impl GenerateCode for ContractRef<'_> {
51 fn generate_code(&self) -> TokenStream2 {
52 let contract_ref = self.generate_struct();
53 let contract_ref_trait_impls = self.generate_contract_trait_impls();
54 let contract_ref_inherent_impls = self.generate_contract_inherent_impls();
55 let call_builder_trait_impl = self.generate_call_builder_trait_impl();
56 let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
57 quote! {
58 #contract_ref
59 #contract_ref_trait_impls
60 #contract_ref_inherent_impls
61 #call_builder_trait_impl
62 #auxiliary_trait_impls
63 }
64 }
65}
66
67impl ContractRef<'_> {
68 fn generate_contract_ref_ident(&self) -> syn::Ident {
70 quote::format_ident!("{}Ref", self.contract.module().storage().ident())
71 }
72
73 fn generate_struct(&self) -> TokenStream2 {
80 let span = self.contract.module().storage().span();
81 let doc_attrs = self
82 .contract
83 .module()
84 .storage()
85 .attrs()
86 .iter()
87 .filter(|&x| syn::Attribute::is_doc_attribute(x))
88 .cloned();
89 let storage_ident = self.contract.module().storage().ident();
90 let ref_ident = self.generate_contract_ref_ident();
91 let abi = default_abi!();
92 let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
93 quote_spanned!(span=>
96 impl<Abi> ::ink::SolDecode for #ref_ident<Abi> {
97 type SolType = ::ink::Address;
98
99 fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
100 Ok(Self {
101 inner: <<#storage_ident
102 as ::ink::codegen::ContractCallBuilder>::Type<Abi>
103 as ::ink::env::call::FromAddr>::from_addr(value),
104 _marker: ::core::marker::PhantomData,
105 })
106 }
107 }
108
109 impl<'a, Abi> ::ink::SolEncode<'a> for #ref_ident<Abi> {
110 type SolType = &'a ::ink::Address;
111
112 fn to_sol_type(&'a self) -> Self::SolType {
113 self.as_ref()
114 }
115 }
116 )
117 } else {
118 quote!()
119 };
120 quote_spanned!(span=>
121 #[derive(
122 ::core::fmt::Debug,
123 ::core::hash::Hash,
124 ::core::cmp::PartialEq,
125 ::core::cmp::Eq,
126 ::core::clone::Clone,
127 )]
128 #[::ink::scale_derive(Encode, Decode)]
129 #( #doc_attrs )*
130 pub struct #ref_ident<Abi = #abi> {
131 inner: <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>,
132 _marker: core::marker::PhantomData<Abi>,
133 }
134
135 const _: () = {
136 impl ::ink::env::ContractReference for #storage_ident {
137 type Type = #ref_ident;
138 }
139
140 impl<Abi> ::ink::env::ContractReverseReference for #ref_ident<Abi> {
141 type Type = #storage_ident;
142 }
143
144 impl<Abi> ::ink::env::call::ConstructorReturnType<#ref_ident, Abi> for #storage_ident
145 where
146 (): ink::env::call::utils::DecodeConstructorError<Abi>
147 {
148 type Output = #ref_ident;
149 type Error = ();
150
151 fn ok(value: #ref_ident) -> Self::Output {
152 value
153 }
154 }
155
156 impl<E, Abi> ::ink::env::call::ConstructorReturnType<#ref_ident, Abi>
157 for ::core::result::Result<#storage_ident, E>
158 where
159 E: ink::env::call::utils::DecodeConstructorError<Abi>
160 {
161 const IS_RESULT: bool = true;
162
163 type Output = ::core::result::Result<#ref_ident, E>;
164 type Error = E;
165
166 fn ok(value: #ref_ident) -> Self::Output {
167 ::core::result::Result::Ok(value)
168 }
169
170 fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
171 ::core::option::Option::Some(::core::result::Result::Err(err))
172 }
173 }
174
175 impl<Abi> ::ink::env::ContractEnv for #ref_ident<Abi> {
176 type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
177 }
178
179 #[cfg(feature = "std")]
180 impl<Abi> ::ink::storage::traits::StorageLayout for #ref_ident<Abi> {
182 fn layout(
183 __key: &::ink::primitives::Key,
184 ) -> ::ink::metadata::layout::Layout {
185 ::ink::metadata::layout::Layout::Struct(
186 ::ink::metadata::layout::StructLayout::new(
187 ::core::stringify!(#ref_ident),
188 [
189 ::ink::metadata::layout::FieldLayout::new(
190 "inner",
191 <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>
192 as ::ink::storage::traits::StorageLayout>::layout(__key)
193 )
194 ]
195 )
196 )
197 }
198 }
199
200 #[cfg(feature = "std")]
201 impl<Abi> ::ink::scale_info::TypeInfo for #ref_ident<Abi>
203 where
204 ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
205 {
206 type Identity = <
207 <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi> as ::ink::scale_info::TypeInfo
208 >::Identity;
209
210 fn type_info() -> ::ink::scale_info::Type {
211 <
212 <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi> as ::ink::scale_info::TypeInfo
213 >::type_info()
214 }
215 }
216
217 #sol_codec
218 };
219 )
220 }
221
222 fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
227 let span = self.contract.module().storage().span();
228 let storage_ident = self.contract.module().storage().ident();
229 let ref_ident = self.generate_contract_ref_ident();
230 quote_spanned!(span=>
231 impl<Abi> ::ink::env::call::FromAddr for #ref_ident<Abi> {
232 #[inline]
233 fn from_addr(addr: ::ink::Address) -> Self {
234 Self {
235 inner: <<#storage_ident
236 as ::ink::codegen::ContractCallBuilder>::Type<Abi>
237 as ::ink::env::call::FromAddr>::from_addr(addr),
238 _marker: ::core::default::Default::default(),
239 }
240 }
241 }
242
243 impl<Abi> ::ink::ToAddr for #ref_ident<Abi> {
244 #[inline]
245 fn to_addr(&self) -> ::ink::Address {
246 <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>
247 as ::ink::ToAddr>::to_addr(&self.inner)
248 }
249 }
250
251 impl<Abi> ::core::convert::AsRef<::ink::Address> for #ref_ident<Abi> {
252 fn as_ref(&self) -> &::ink::Address {
253 <_ as ::core::convert::AsRef<::ink::Address>>::as_ref(&self.inner)
254 }
255 }
256
257 impl<Abi> ::core::convert::AsMut<::ink::Address> for #ref_ident<Abi> {
258 fn as_mut(&mut self) -> &mut ::ink::Address {
259 <_ as ::core::convert::AsMut<::ink::Address>>::as_mut(&mut self.inner)
260 }
261 }
262 )
263 }
264
265 fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
270 let span = self.contract.module().storage().span();
271 let ref_ident = self.generate_contract_ref_ident();
272 let storage_ident = self.contract.module().storage().ident();
273 quote_spanned!(span=>
274 const _: () = {
275 impl<Abi> ::ink::codegen::TraitCallBuilder for #ref_ident<Abi> {
276 type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>;
277
278 #[inline]
279 fn call(&self) -> &Self::Builder {
280 &self.inner
281 }
282
283 #[inline]
284 fn call_mut(&mut self) -> &mut Self::Builder {
285 &mut self.inner
286 }
287 }
288 };
289 )
290 }
291
292 fn generate_contract_trait_impls(&self) -> TokenStream2 {
299 self.contract
300 .module()
301 .impls()
302 .filter_map(|impl_block| {
303 impl_block.trait_path().map(|trait_path| {
305 self.generate_contract_trait_impl(trait_path, impl_block)
306 })
307 })
308 .collect()
309 }
310
311 fn generate_contract_trait_impl(
316 &self,
317 trait_path: &syn::Path,
318 impl_block: &ir::ItemImpl,
319 ) -> TokenStream2 {
320 let span = impl_block.span();
321 let attrs = impl_block.attrs();
322 let forwarder_ident = self.generate_contract_ref_ident();
323 generate_abi_impls!(@tokens |abi: TokenStream2| {
324 let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block, abi.clone());
325 quote_spanned!(span=>
326 #( #attrs )*
327 impl #trait_path for #forwarder_ident<#abi> {
328 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
329 as #trait_path>::__ink_TraitInfo;
330
331 #messages
332 }
333 )
334 })
335 }
336
337 fn generate_contract_trait_impl_messages(
340 &self,
341 trait_path: &syn::Path,
342 impl_block: &ir::ItemImpl,
343 abi: TokenStream2,
344 ) -> TokenStream2 {
345 impl_block
346 .iter_messages()
347 .map(|message| {
348 self.generate_contract_trait_impl_for_message(
349 trait_path,
350 message,
351 abi.clone(),
352 )
353 })
354 .collect()
355 }
356
357 fn generate_contract_trait_impl_for_message(
360 &self,
361 trait_path: &syn::Path,
362 message: ir::CallableWithSelector<ir::Message>,
363 abi: TokenStream2,
364 ) -> TokenStream2 {
365 use ir::Callable as _;
366 let span = message.span();
367 let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
368 let message_ident = message.ident();
369 let output_ident = generator::output_ident(message_ident);
370 let call_operator = match message.receiver() {
371 ir::Receiver::Ref => quote! { call },
372 ir::Receiver::RefMut => quote! { call_mut },
373 };
374 let forward_operator = match message.receiver() {
375 ir::Receiver::Ref => quote! { forward },
376 ir::Receiver::RefMut => quote! { forward_mut },
377 };
378 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
379 let input_idents = generator::input_message_idents(message.inputs());
380 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
381 let cfg_attrs = message.get_cfg_attrs(span);
382 quote_spanned!(span=>
383 #( #cfg_attrs )*
384 type #output_ident =
385 <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder<#abi> as #trait_path>::#output_ident;
386
387 #[inline]
388 #( #cfg_attrs )*
389 fn #message_ident(
390 & #mut_token self
391 #( , #input_idents : #input_types )*
392 ) -> Self::#output_ident {
393 <_ as #trait_path>::#message_ident(
394 <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
395 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
396 )
397 #( , #input_idents )*
398 )
399 }
400 )
401 }
402
403 fn generate_contract_inherent_impls(&self) -> TokenStream2 {
410 generate_abi_impls!(@type |abi| {
411 let impls = self.contract.module().impls()
412 .filter(|impl_block| {
413 impl_block.trait_path().is_none()
415 })
416 .map(|impl_block| self.generate_contract_inherent_impl(impl_block, abi));
417 let impl_sol_constructor = match abi {
418 Abi::Ink => quote!(),
419 Abi::Sol => {
420 let constructor = self.contract.module().impls()
422 .flat_map(|item_impl| item_impl.iter_constructors())
423 .find_or_first(|constructor| {
424 constructor.is_default()
425 })
426 .expect("Expected at least one constructor");
427 let ctor = self.generate_contract_inherent_impl_for_constructor(constructor, abi);
428 let span = ctor.span();
429 let forwarder_ident = self.generate_contract_ref_ident();
430 quote_spanned!(span=>
431 impl #forwarder_ident<::ink::abi::Sol> {
432 #ctor
433 }
434 )
435 }
436 };
437 let span = self.contract.module().span();
438 quote_spanned!(span=>
439 #impl_sol_constructor
440 #( #impls )*
441 )
442 })
443 }
444
445 fn generate_contract_inherent_impl(
454 &self,
455 impl_block: &ir::ItemImpl,
456 abi: Abi,
457 ) -> TokenStream2 {
458 let span = impl_block.span();
459 let attrs = impl_block.attrs();
460 let forwarder_ident = self.generate_contract_ref_ident();
461 let messages = impl_block
462 .iter_messages()
463 .map(|message| self.generate_contract_inherent_impl_for_message(message));
464 let messages = quote! {
465 #( #messages )*
466 };
467 let (abi_ty, constructors) = match abi {
468 Abi::Ink => {
469 let constructors = impl_block.iter_constructors().map(|constructor| {
470 self.generate_contract_inherent_impl_for_constructor(constructor, abi)
471 });
472 (
473 quote!(::ink::abi::Ink),
474 quote! {
475 #( #constructors )*
476 },
477 )
478 }
479 Abi::Sol => {
480 (
481 quote!(::ink::abi::Sol),
482 quote!(),
485 )
486 }
487 };
488 quote_spanned!(span=>
489 #( #attrs )*
490 impl #forwarder_ident<#abi_ty> {
491 #constructors
492 #messages
493 }
494 )
495 }
496
497 fn generate_contract_inherent_impl_for_message(
505 &self,
506 message: ir::CallableWithSelector<ir::Message>,
507 ) -> TokenStream2 {
508 use ir::Callable as _;
509 let span = message.span();
510 let attrs = self
511 .contract
512 .config()
513 .whitelisted_attributes()
514 .filter_attr(message.attrs().to_vec());
515 let storage_ident = self.contract.module().storage().ident();
516 let message_ident = message.ident();
517 let try_message_ident = message.try_ident();
518 let call_operator = match message.receiver() {
519 ir::Receiver::Ref => quote! { call },
520 ir::Receiver::RefMut => quote! { call_mut },
521 };
522 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
523 let input_idents = generator::input_message_idents(message.inputs());
524 let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
525 let output_type = message.output().map(|ty| quote! { -> #ty });
526 let wrapped_output_type = message.wrapped_output();
527 quote_spanned!(span=>
528 #( #attrs )*
529 #[inline]
530 pub fn #message_ident(
531 & #mut_token self
532 #( , #input_idents : #input_types )*
533 ) #output_type {
534 self.#try_message_ident( #( #input_idents, )* )
535 .unwrap_or_else(|error| ::core::panic!(
536 "encountered error while calling {}::{}: {:?}",
537 ::core::stringify!(#storage_ident),
538 ::core::stringify!(#message_ident),
539 error,
540 ))
541 }
542
543 #( #attrs )*
544 #[inline]
545 pub fn #try_message_ident(
546 & #mut_token self
547 #( , #input_idents : #input_types )*
548 ) -> #wrapped_output_type {
549 <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
550 .#message_ident( #( #input_idents ),* )
551 .try_invoke()
552 .unwrap_or_else(|error| ::core::panic!(
553 "encountered error while calling {}::{}: {:?}",
554 ::core::stringify!(#storage_ident),
555 ::core::stringify!(#message_ident),
556 error,
557 ))
558 }
559 )
560 }
561
562 fn generate_contract_inherent_impl_for_constructor(
570 &self,
571 constructor: ir::CallableWithSelector<ir::Constructor>,
572 abi: Abi,
573 ) -> TokenStream2 {
574 let span = constructor.span();
575 let attrs = self
576 .contract
577 .config()
578 .whitelisted_attributes()
579 .filter_attr(constructor.attrs().to_vec());
580 let constructor_ident = constructor.ident();
581 let input_bindings = generator::input_bindings(constructor.inputs());
582 let input_types = generator::input_types(constructor.inputs());
583 let ret_type = constructor
584 .output()
585 .map(quote::ToTokens::to_token_stream)
586 .unwrap_or_else(|| quote::quote! { Self });
587
588 let (abi_ty, exec_input_init) = match abi {
589 Abi::Ink => {
590 let selector_bytes = constructor.composed_selector().hex_lits();
591 (
592 quote!(::ink::abi::Ink),
593 quote! {
594 ::ink::env::call::ExecutionInput::new(
595 ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
596 )
597 },
598 )
599 }
600 Abi::Sol => {
601 (
602 quote!(::ink::abi::Sol),
603 quote! {
604 ::ink::env::call::ExecutionInput::no_selector()
605 },
606 )
607 }
608 };
609 let arg_list = generator::generate_argument_list(
610 input_types.iter().cloned(),
611 abi_ty.clone(),
612 );
613
614 quote_spanned!(span =>
615 #( #attrs )*
616 #[inline]
617 #[allow(clippy::type_complexity)]
618 pub fn #constructor_ident(
619 #( #input_bindings : #input_types ),*
620 ) -> ::ink::env::call::CreateBuilder<
621 Environment,
622 Self,
623 ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
624 ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, #abi_ty>>,
625 ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
626 #abi_ty
627 > {
628 ::ink::env::call::build_create_abi::<Self, #abi_ty>()
629 .exec_input(
630 #exec_input_init
631 #(
632 .push_arg(#input_bindings)
633 )*
634 )
635 .returns::<#ret_type>()
636 }
637 )
638 }
639}