ink_codegen/generator/
item_impls.rs1use core::iter;
16
17use derive_more::From;
18use heck::ToLowerCamelCase as _;
19use ink_primitives::abi::Abi;
20use ir::Callable as _;
21#[cfg(not(ink_abi = "sol"))]
22use ir::HexLiteral;
23use proc_macro2::TokenStream as TokenStream2;
24use quote::{
25 format_ident,
26 quote,
27 quote_spanned,
28 ToTokens,
29};
30use syn::spanned::Spanned as _;
31
32use crate::GenerateCode;
33
34#[derive(From)]
36pub struct ItemImpls<'a> {
37 contract: &'a ir::Contract,
38}
39impl_as_ref_for_generator!(ItemImpls);
40
41impl GenerateCode for ItemImpls<'_> {
42 fn generate_code(&self) -> TokenStream2 {
43 let item_impls = self
44 .contract
45 .module()
46 .impls()
47 .map(|item_impl| self.generate_item_impl(item_impl));
48 let inout_guards = generate_abi_impls!(@type |abi| {
49 self.generate_input_output_guards(abi)
50 });
51 let trait_message_property_guards = self.generate_trait_message_property_guards();
52 quote! {
53 const _: () = {
54 use ::ink::codegen::{Env as _, StaticEnv as _};
56
57 #( #item_impls )*
58 #inout_guards
59 #trait_message_property_guards
60 };
61 }
62 }
63}
64
65impl ItemImpls<'_> {
66 fn generate_trait_message_property_guards(&self) -> TokenStream2 {
75 let storage_span = self.contract.module().storage().span();
76 let storage_ident = self.contract.module().storage().ident();
77 let trait_message_guards = self
78 .contract
79 .module()
80 .impls()
81 .filter_map(|item_impl| item_impl.trait_path().map(|trait_path| {
82 iter::repeat(trait_path).zip(item_impl.iter_messages())
83 }))
84 .flatten()
85 .map(|(trait_path, message)| {
86 let message_span = message.span();
87
88 #[cfg(not(ink_abi = "sol"))]
89 let message_local_id = {
90 let id = message.local_id().hex_padded_suffixed();
91 quote!(#id)
92 };
93
94 #[cfg(ink_abi = "sol")]
95 let message_local_id = {
96 use crate::generator::sol;
97 sol::utils::selector_id(&message)
98 };
99
100 let message_guard_payable = message.is_payable().then(|| {
101 quote_spanned!(message_span=>
102 const _: ::ink::codegen::TraitMessagePayable<{
103 <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
104 as #trait_path>::__ink_TraitInfo
105 as ::ink::reflect::TraitMessageInfo<#message_local_id>>::PAYABLE
106 }> = ::ink::codegen::TraitMessagePayable::<true>;
107 )
108 });
109
110 #[cfg(not(ink_abi = "sol"))]
112 let message_guard_selector = message.user_provided_selector().map(|selector| {
113 let given_selector = selector.into_be_u32().hex_padded_suffixed();
114 quote_spanned!(message_span=>
115 const _: ::ink::codegen::TraitMessageSelector<{
116 ::core::primitive::u32::from_be_bytes(
117 <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
118 as #trait_path>::__ink_TraitInfo
119 as ::ink::reflect::TraitMessageInfo<#message_local_id>>::SELECTOR
120 )
121 }> = ::ink::codegen::TraitMessageSelector::<#given_selector>;
122 )
123 });
124
125 #[cfg(ink_abi = "sol")]
126 let message_guard_selector = quote!();
127
128 quote_spanned!(message_span=>
129 #message_guard_payable
130 #message_guard_selector
131 )
132 });
133 quote_spanned!(storage_span=>
134 #( #trait_message_guards )*
135 )
136 }
137
138 fn generate_input_output_guards(&self, abi: Abi) -> TokenStream2 {
140 let (input_trait, output_trait) = match abi {
141 Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
142 Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
143 };
144 let storage_span = self.contract.module().storage().span();
145 let constructor_input_guards = self
146 .contract
147 .module()
148 .impls()
149 .flat_map(|item_impl| item_impl.iter_constructors())
150 .map(|constructor| {
151 let constructor_span = constructor.span();
152 let constructor_inputs = constructor.inputs().map(|input| {
153 let span = input.span();
154 let input_type = &*input.ty;
155 quote_spanned!(span=>
156 ::ink::codegen::utils::consume_type::<
157 ::ink::codegen::#input_trait<#input_type>
158 >();
159 )
160 });
161 quote_spanned!(constructor_span=>
162 #( #constructor_inputs )*
163 )
164 });
165 let message_inout_guards = self
166 .contract
167 .module()
168 .impls()
169 .flat_map(|item_impl| item_impl.iter_messages())
170 .map(|message| {
171 let message_span = message.span();
172 let message_inputs = message.inputs().map(|input| {
173 let span = input.span();
174 let input_type = &*input.ty;
175 quote_spanned!(span=>
176 ::ink::codegen::utils::consume_type::<
177 ::ink::codegen::#input_trait<#input_type>
178 >();
179 )
180 });
181 let message_output = message.output().map(|output_type| {
182 let span = output_type.span();
183 quote_spanned!(span=>
184 ::ink::codegen::utils::consume_type::<
185 ::ink::codegen::#output_trait<#output_type>
186 >();
187 )
188 });
189 quote_spanned!(message_span=>
190 #( #message_inputs )*
191 #message_output
192 )
193 });
194 quote_spanned!(storage_span=>
195 const _: () = {
196 #( #constructor_input_guards )*
197 #( #message_inout_guards )*
198 };
199 )
200 }
201
202 fn generate_trait_message(message: &ir::Message) -> TokenStream2 {
204 let span = message.span();
205 let attrs = message.attrs();
206 let vis = message.visibility();
207 let receiver = message.receiver();
208 let ident = message.ident();
209 let output_ident =
210 format_ident!("{}Output", ident.to_string().to_lower_camel_case());
211 let inputs = message.inputs();
212 let output = message
213 .output()
214 .cloned()
215 .unwrap_or_else(|| syn::parse_quote! { () });
216 let statements = message.statements();
217 let cfg_attrs = message.get_cfg_attrs(span);
218 quote_spanned!(span =>
219 #( #cfg_attrs )*
220 type #output_ident = #output;
221
222 #( #attrs )*
223 #vis fn #ident(#receiver #( , #inputs )* ) -> Self::#output_ident {
224 #( #statements )*
225 }
226 )
227 }
228
229 fn generate_trait_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
230 assert!(item_impl.trait_path().is_some());
231 let span = item_impl.span();
232 let attrs = item_impl.attrs();
233 let messages = item_impl
234 .iter_messages()
235 .map(|cws| Self::generate_trait_message(cws.callable()));
236 let trait_path = item_impl
237 .trait_path()
238 .expect("encountered missing trait path for trait impl block");
239 let self_type = item_impl.self_type();
240 quote_spanned!(span =>
241 #( #attrs )*
242 impl #trait_path for #self_type {
243 type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
244 as #trait_path>::__ink_TraitInfo;
245
246 #( #messages )*
247 }
248 )
249 }
250
251 fn generate_inherent_constructor(constructor: &ir::Constructor) -> TokenStream2 {
265 let span = constructor.span();
266 let attrs = constructor.attrs();
267 let vis = constructor.visibility();
268 let ident = constructor.ident();
269 let inputs = constructor.inputs();
270 let statements = constructor.statements();
271 let output = constructor.output();
272 quote_spanned!(span =>
273 #( #attrs )*
274 #[cfg(not(target_os = "dragonfly"))]
275 #vis fn #ident( #( #inputs ),* ) -> #output {
276 #( #statements )*
277 }
278 )
279 }
280
281 fn generate_inherent_message(message: &ir::Message) -> TokenStream2 {
284 let span = message.span();
285 let attrs = message.attrs();
286 let vis = message.visibility();
287 let receiver = message.receiver();
288 let ident = message.ident();
289 let inputs = message.inputs();
290 let output_arrow = message.output().map(|_| quote! { -> });
291 let output = message.output();
292 let statements = message.statements();
293 quote_spanned!(span =>
294 #( #attrs )*
295 #vis fn #ident(#receiver #( , #inputs )* ) #output_arrow #output {
296 #( #statements )*
297 }
298 )
299 }
300
301 fn generate_inherent_item_impl(item_impl: &ir::ItemImpl) -> TokenStream2 {
302 assert!(item_impl.trait_path().is_none());
303 let span = item_impl.span();
304 let attrs = item_impl.attrs();
305 let messages = item_impl
306 .iter_messages()
307 .map(|cws| Self::generate_inherent_message(cws.callable()));
308 let constructors = item_impl
309 .iter_constructors()
310 .map(|cws| Self::generate_inherent_constructor(cws.callable()));
311 let other_items = item_impl
312 .items()
313 .iter()
314 .filter_map(ir::ImplItem::filter_map_other_item)
315 .map(ToTokens::to_token_stream);
316 let self_type = item_impl.self_type();
317 quote_spanned!(span =>
318 #( #attrs )*
319 impl #self_type {
320 #( #constructors )*
321 #( #messages )*
322 #( #other_items )*
323 }
324 )
325 }
326
327 fn generate_item_impl_self_ty_guard(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
330 let self_ty = item_impl.self_type();
331 let span = self_ty.span();
332 let storage_ident = self.contract.module().storage().ident();
333 quote_spanned!(span =>
334 const _: ::ink::codegen::utils::IsSameType<#storage_ident> =
335 ::ink::codegen::utils::IsSameType::<#self_ty>::new();
336 )
337 }
338
339 fn generate_item_impl(&self, item_impl: &ir::ItemImpl) -> TokenStream2 {
341 let self_ty_guard = self.generate_item_impl_self_ty_guard(item_impl);
342 let impl_block = match item_impl.trait_path() {
343 Some(_) => Self::generate_trait_item_impl(item_impl),
344 None => Self::generate_inherent_item_impl(item_impl),
345 };
346 quote! {
347 #self_ty_guard
348 #impl_block
349 }
350 }
351}