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