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) -> 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 item.filter_map_message()
128 .map(|message| self.generate_registry_for_message(&message, selector))
129 },
130 );
131 quote! {
132 #( #messages )*
133 }
134 }
135
136 fn generate_inout_guards_for_message(
138 message: &ir::InkTraitMessage,
139 abi: Abi,
140 ) -> TokenStream2 {
141 let message_span = message.span();
142 let (input_trait, output_trait) = match abi {
143 Abi::Ink => (quote!(DispatchInput), quote!(DispatchOutput)),
144 Abi::Sol => (quote!(DispatchInputSol), quote!(DispatchOutputSol)),
145 };
146 let message_inputs = message.inputs().map(|input| {
147 let input_span = input.span();
148 let input_type = &*input.ty;
149 quote_spanned!(input_span=>
150 ::ink::codegen::utils::consume_type::<
151 ::ink::codegen::#input_trait<#input_type>
152 >();
153 )
154 });
155 let message_output = message.output().map(|output_type| {
156 let output_span = output_type.span();
157 quote_spanned!(output_span=>
158 ::ink::codegen::utils::consume_type::<
159 ::ink::codegen::#output_trait<#output_type>
160 >();
161 )
162 });
163 quote_spanned!(message_span=>
164 #( #message_inputs )*
165 #message_output
166 )
167 }
168
169 fn generate_registry_for_message(
174 &self,
175 message: &ir::InkTraitMessage,
176 selector: ir::Selector,
177 ) -> TokenStream2 {
178 let span = message.span();
179 let ident = message.ident();
180 let attrs = message.attrs();
181 let cfg_attrs = message.get_cfg_attrs(span);
182 let output_ident = generator::output_ident(message.ident());
183 let output_type = message
184 .output()
185 .cloned()
186 .unwrap_or_else(|| parse_quote! { () });
187 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
188 let (input_bindings, input_types) =
189 Self::input_bindings_and_types(message.inputs());
190 let linker_error_ident = EnforcedErrors::cannot_call_trait_message(
191 self.trait_ident(),
192 message.ident(),
193 selector,
194 message.mutates(),
195 );
196 let inout_guards = generate_abi_impls!(@type |abi| {
197 Self::generate_inout_guards_for_message(message, abi)
198 });
199 let impl_body = match option_env!("INK_COVERAGE_REPORTING") {
200 Some("true") => {
201 quote! {
202 ::core::unreachable!(
207 "this is an invalid ink! message call which should never be possible."
208 );
209 }
210 }
211 _ => {
212 quote! {
213 unsafe extern {
216 fn #linker_error_ident() -> !;
217 }
218 unsafe { #linker_error_ident() }
219 }
220 }
221 };
222 quote_spanned!(span=>
223 #( #cfg_attrs )*
224 type #output_ident = #output_type;
225
226 #( #attrs )*
227 #[cold]
228 fn #ident(
229 & #mut_token self
230 #( , #input_bindings : #input_types )*
231 ) -> Self::#output_ident {
232 #inout_guards
233 #impl_body
234 }
235 )
236 }
237
238 fn input_bindings_and_types(
240 inputs: ir::InputsIter<'_>,
241 ) -> (Vec<syn::Ident>, Vec<&syn::Type>) {
242 inputs
243 .enumerate()
244 .map(|(n, pat_type)| {
245 let binding = format_ident!("__ink_binding_{}", n);
246 let ty = &*pat_type.ty;
247 (binding, ty)
248 })
249 .unzip()
250 }
251
252 fn generate_trait_info_object(&self) -> TokenStream2 {
258 let span = self.span();
259 let trait_id = self.generate_trait_id();
260 let trait_ident = self.trait_ident();
261 let trait_info_ident = self.trait_def.trait_info_ident();
262 let trait_call_forwarder = self.trait_def.call_forwarder_ident();
263 let trait_message_builder = self.trait_def.message_builder_ident();
264 let trait_message_info = self.generate_info_for_trait_messages();
265 quote_spanned!(span =>
266 #[doc(hidden)]
267 #[allow(non_camel_case_types)]
268 pub struct #trait_info_ident<E> {
269 marker: ::core::marker::PhantomData<fn() -> E>,
270 }
271
272 #trait_message_info
273
274 impl<E> ::ink::reflect::TraitInfo for #trait_info_ident<E>
275 where
276 E: ::ink::env::Environment,
277 {
278 const ID: u32 = #trait_id;
279
280 const PATH: &'static ::core::primitive::str = ::core::module_path!();
281
282 const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident);
283 }
284
285 impl<E> ::ink::codegen::TraitCallForwarder for #trait_info_ident<E>
286 where
287 E: ::ink::env::Environment,
288 {
289 type Forwarder<Abi> = #trait_call_forwarder<E, Abi>;
290 }
291
292 impl<E> ::ink::codegen::TraitMessageBuilder for #trait_info_ident<E>
293 where
294 E: ::ink::env::Environment,
295 {
296 type MessageBuilder<Abi> = #trait_message_builder<E, Abi>;
297 }
298 )
299 }
300
301 fn generate_trait_id(&self) -> syn::LitInt {
303 let span = self.span();
304 let mut id = 0u32;
305 debug_assert!(
306 self.trait_def
307 .trait_def
308 .item()
309 .iter_items()
310 .next()
311 .is_some(),
312 "invalid empty ink! trait definition"
313 );
314 for (_, selector) in self.trait_def.trait_def.item().iter_items() {
315 id ^= selector.into_be_u32()
316 }
317 syn::LitInt::new(&format!("{id}"), span)
318 }
319
320 fn generate_info_for_trait_messages(&self) -> TokenStream2 {
323 let span = self.span();
324 let message_impls = self.trait_def.trait_def.item().iter_items().filter_map(
325 |(trait_item, selector)| {
326 trait_item.filter_map_message().map(|message| {
327 self.generate_info_for_trait_for_message(&message, selector)
328 })
329 },
330 );
331 quote_spanned!(span=>
332 #( #message_impls )*
333 )
334 }
335
336 fn generate_info_for_trait_for_message(
339 &self,
340 message: &ir::InkTraitMessage,
341 selector: ir::Selector,
342 ) -> TokenStream2 {
343 let span = message.span();
344 let trait_info_ident = self.trait_def.trait_info_ident();
345 let is_payable = message.ink_attrs().is_payable();
346 generate_abi_impls!(@type |abi| {
347 let (local_id, selector_bytes) = match abi {
348 Abi::Ink => {
349 let local_id = message.local_id();
350 let selector_bytes = selector.hex_lits();
351 (
352 quote!(#local_id),
353 quote!([ #( #selector_bytes ),* ])
354 )
355 }
356 Abi::Sol => {
357 let ident_str = message.ident().to_string();
358 let signature = sol::utils::call_signature(ident_str, 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 (
368 selector_id,
369 selector_bytes
370 )
371 }
372 };
373 quote_spanned!(span=>
374 impl<E> ::ink::reflect::TraitMessageInfo<#local_id> for #trait_info_ident<E> {
375 const PAYABLE: ::core::primitive::bool = #is_payable;
376 const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
377 }
378 )
379 })
380 }
381}