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