ink_codegen/generator/trait_def/
trait_registry.rs1use derive_more::From;
23use ink_primitives::abi::Abi;
24use proc_macro2::{
25 Span,
26 TokenStream as TokenStream2,
27};
28use quote::{
29 format_ident,
30 quote,
31 quote_spanned,
32};
33use syn::{
34 parse_quote,
35 spanned::Spanned,
36};
37
38use super::TraitDefinition;
39use crate::{
40 generator,
41 generator::sol,
42 traits::GenerateCode,
43 EnforcedErrors,
44};
45
46impl TraitDefinition<'_> {
47 pub fn generate_trait_registry_impl(&self) -> TokenStream2 {
53 TraitRegistry::from(*self).generate_code()
54 }
55
56 pub fn trait_info_ident(&self) -> syn::Ident {
58 self.append_trait_suffix("TraitInfo")
59 }
60}
61
62#[derive(From)]
64struct TraitRegistry<'a> {
65 trait_def: TraitDefinition<'a>,
66}
67
68impl GenerateCode for TraitRegistry<'_> {
69 fn generate_code(&self) -> TokenStream2 {
70 let registry_impl = self.generate_registry_impl();
71 let trait_info = self.generate_trait_info_object();
72 quote! {
73 #registry_impl
74 #trait_info
75 }
76 }
77}
78
79impl TraitRegistry<'_> {
80 fn span(&self) -> Span {
82 self.trait_def.span()
83 }
84
85 fn trait_ident(&self) -> &syn::Ident {
87 self.trait_def.trait_def.item().ident()
88 }
89
90 fn generate_registry_impl(&self) -> TokenStream2 {
105 let span = self.span();
106 let name = self.trait_ident();
107 let trait_info_ident = self.trait_def.trait_info_ident();
108 let messages = self.generate_registry_messages();
109 quote_spanned!(span=>
110 impl<E> #name for ::ink::reflect::TraitDefinitionRegistry<E>
111 where
112 E: ::ink::env::Environment,
113 {
114 #[allow(non_camel_case_types)]
116 type __ink_TraitInfo = #trait_info_ident<E>;
117
118 #messages
119 }
120 )
121 }
122
123 fn generate_registry_messages(&self) -> TokenStream2 {
125 let messages = self.trait_def.trait_def.item().iter_items().filter_map(
126 |(item, selector)| {
127 let ret = item.filter_map_message().map(|message| {
128 self.generate_registry_for_message(&message, selector)
129 });
130 ret
131 },
132 );
133 quote! {
134 #( #messages )*
135 }
136 }
137
138 fn generate_inout_guards_for_message(
140 message: &ir::InkTraitMessage,
141 abi: Abi,
142 ) -> TokenStream2 {
143 let message_span = message.span();
144 let (input_trait, output_trait) = match abi {
145 Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
146 Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
147 };
148 let message_inputs = message.inputs().map(|input| {
149 let input_span = input.span();
150 let input_type = &*input.ty;
151 quote_spanned!(input_span=>
152 ::ink::codegen::utils::consume_type::<
153 ::ink::codegen::#input_trait<#input_type>
154 >();
155 )
156 });
157 let message_output = message.output().map(|output_type| {
158 let output_span = output_type.span();
159 quote_spanned!(output_span=>
160 ::ink::codegen::utils::consume_type::<
161 ::ink::codegen::#output_trait<#output_type>
162 >();
163 )
164 });
165 quote_spanned!(message_span=>
166 #( #message_inputs )*
167 #message_output
168 )
169 }
170
171 fn generate_registry_for_message(
176 &self,
177 message: &ir::InkTraitMessage,
178 selector: ir::Selector,
179 ) -> TokenStream2 {
180 let span = message.span();
181 let ident = message.ident();
182 let attrs = message.attrs();
183 let cfg_attrs = message.get_cfg_attrs(span);
184 let output_ident = generator::output_ident(message.ident());
185 let output_type = message
186 .output()
187 .cloned()
188 .unwrap_or_else(|| parse_quote! { () });
189 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
190 let (input_bindings, input_types) =
191 Self::input_bindings_and_types(message.inputs());
192 let linker_error_ident = EnforcedErrors::cannot_call_trait_message(
193 self.trait_ident(),
194 message.ident(),
195 selector,
196 message.mutates(),
197 );
198 let inout_guards = generate_abi_impls!(@type |abi| {
199 Self::generate_inout_guards_for_message(message, abi)
200 });
201 let impl_body = match option_env!("INK_COVERAGE_REPORTING") {
202 Some("true") => {
203 quote! {
204 ::core::unreachable!(
209 "this is an invalid ink! message call which should never be possible."
210 );
211 }
212 }
213 _ => {
214 quote! {
215 extern {
218 fn #linker_error_ident() -> !;
219 }
220 unsafe { #linker_error_ident() }
221 }
222 }
223 };
224 quote_spanned!(span=>
225 #( #cfg_attrs )*
226 type #output_ident = #output_type;
227
228 #( #attrs )*
229 #[cold]
230 fn #ident(
231 & #mut_token self
232 #( , #input_bindings : #input_types )*
233 ) -> Self::#output_ident {
234 #inout_guards
235 #impl_body
236 }
237 )
238 }
239
240 fn input_bindings_and_types(
242 inputs: ir::InputsIter,
243 ) -> (Vec<syn::Ident>, Vec<&syn::Type>) {
244 inputs
245 .enumerate()
246 .map(|(n, pat_type)| {
247 let binding = format_ident!("__ink_binding_{}", n);
248 let ty = &*pat_type.ty;
249 (binding, ty)
250 })
251 .unzip()
252 }
253
254 fn generate_trait_info_object(&self) -> TokenStream2 {
260 let span = self.span();
261 let trait_id = self.generate_trait_id();
262 let trait_ident = self.trait_ident();
263 let trait_info_ident = self.trait_def.trait_info_ident();
264 let trait_call_forwarder = self.trait_def.call_forwarder_ident();
265 let trait_message_builder = self.trait_def.message_builder_ident();
266 let trait_message_info = self.generate_info_for_trait_messages();
267 quote_spanned!(span =>
268 #[doc(hidden)]
269 #[allow(non_camel_case_types)]
270 pub struct #trait_info_ident<E> {
271 marker: ::core::marker::PhantomData<fn() -> E>,
272 }
273
274 #trait_message_info
275
276 impl<E> ::ink::reflect::TraitInfo for #trait_info_ident<E>
277 where
278 E: ::ink::env::Environment,
279 {
280 const ID: u32 = #trait_id;
281
282 const PATH: &'static ::core::primitive::str = ::core::module_path!();
283
284 const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident);
285 }
286
287 impl<E> ::ink::codegen::TraitCallForwarder for #trait_info_ident<E>
288 where
289 E: ::ink::env::Environment,
290 {
291 type Forwarder<Abi> = #trait_call_forwarder<E, Abi>;
292 }
293
294 impl<E> ::ink::codegen::TraitMessageBuilder for #trait_info_ident<E>
295 where
296 E: ::ink::env::Environment,
297 {
298 type MessageBuilder<Abi> = #trait_message_builder<E, Abi>;
299 }
300 )
301 }
302
303 fn generate_trait_id(&self) -> syn::LitInt {
305 let span = self.span();
306 let mut id = 0u32;
307 debug_assert!(
308 self.trait_def
309 .trait_def
310 .item()
311 .iter_items()
312 .next()
313 .is_some(),
314 "invalid empty ink! trait definition"
315 );
316 for (_, selector) in self.trait_def.trait_def.item().iter_items() {
317 id ^= selector.into_be_u32()
318 }
319 syn::LitInt::new(&format!("{id}"), span)
320 }
321
322 fn generate_info_for_trait_messages(&self) -> TokenStream2 {
325 let span = self.span();
326 let message_impls = self.trait_def.trait_def.item().iter_items().filter_map(
327 |(trait_item, selector)| {
328 trait_item.filter_map_message().map(|message| {
329 self.generate_info_for_trait_for_message(&message, selector)
330 })
331 },
332 );
333 quote_spanned!(span=>
334 #( #message_impls )*
335 )
336 }
337
338 fn generate_info_for_trait_for_message(
341 &self,
342 message: &ir::InkTraitMessage,
343 selector: ir::Selector,
344 ) -> TokenStream2 {
345 let span = message.span();
346 let trait_info_ident = self.trait_def.trait_info_ident();
347 let is_payable = message.ink_attrs().is_payable();
348 generate_abi_impls!(@type |abi| {
349 let (local_id, selector_bytes) = match abi {
350 Abi::Ink => {
351 let local_id = message.local_id();
352 let selector_bytes = selector.hex_lits();
353 (
354 quote!(#local_id),
355 quote!([ #( #selector_bytes ),* ])
356 )
357 }
358 Abi::Sol => {
359 let ident_str = message.ident().to_string();
360 let signature = sol::utils::call_signature(ident_str, message.inputs());
361 let selector_bytes = quote! {
362 ::ink::codegen::sol::selector_bytes(#signature)
363 };
364 let selector_id = quote!(
365 {
366 ::core::primitive::u32::from_be_bytes(#selector_bytes)
367 }
368 );
369 (
370 selector_id,
371 selector_bytes
372 )
373 }
374 };
375 quote_spanned!(span=>
376 impl<E> ::ink::reflect::TraitMessageInfo<#local_id> for #trait_info_ident<E> {
377 const PAYABLE: ::core::primitive::bool = #is_payable;
378 const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
379 }
380 )
381 })
382 }
383}