ink_codegen/generator/trait_def/
trait_registry.rs1use super::TraitDefinition;
23use crate::{
24 generator,
25 traits::GenerateCode,
26 EnforcedErrors,
27};
28use derive_more::From;
29use proc_macro2::{
30 Span,
31 TokenStream as TokenStream2,
32};
33use quote::{
34 format_ident,
35 quote,
36 quote_spanned,
37};
38use syn::{
39 parse_quote,
40 spanned::Spanned,
41};
42
43impl TraitDefinition<'_> {
44 pub fn generate_trait_registry_impl(&self) -> TokenStream2 {
50 TraitRegistry::from(*self).generate_code()
51 }
52
53 pub fn trait_info_ident(&self) -> syn::Ident {
55 self.append_trait_suffix("TraitInfo")
56 }
57}
58
59#[derive(From)]
61struct TraitRegistry<'a> {
62 trait_def: TraitDefinition<'a>,
63}
64
65impl GenerateCode for TraitRegistry<'_> {
66 fn generate_code(&self) -> TokenStream2 {
67 let registry_impl = self.generate_registry_impl();
68 let trait_info = self.generate_trait_info_object();
69 quote! {
70 #registry_impl
71 #trait_info
72 }
73 }
74}
75
76impl TraitRegistry<'_> {
77 fn span(&self) -> Span {
79 self.trait_def.span()
80 }
81
82 fn trait_ident(&self) -> &syn::Ident {
84 self.trait_def.trait_def.item().ident()
85 }
86
87 fn generate_registry_impl(&self) -> TokenStream2 {
102 let span = self.span();
103 let name = self.trait_ident();
104 let trait_info_ident = self.trait_def.trait_info_ident();
105 let messages = self.generate_registry_messages();
106 quote_spanned!(span=>
107 impl<E> #name for ::ink::reflect::TraitDefinitionRegistry<E>
108 where
109 E: ::ink::env::Environment,
110 {
111 #[allow(non_camel_case_types)]
113 type __ink_TraitInfo = #trait_info_ident<E>;
114
115 #messages
116 }
117 )
118 }
119
120 fn generate_registry_messages(&self) -> TokenStream2 {
122 let messages = self.trait_def.trait_def.item().iter_items().filter_map(
123 |(item, selector)| {
124 let ret = item.filter_map_message().map(|message| {
125 self.generate_registry_for_message(&message, selector)
126 });
127 ret
128 },
129 );
130 quote! {
131 #( #messages )*
132 }
133 }
134
135 fn generate_inout_guards_for_message(message: &ir::InkTraitMessage) -> TokenStream2 {
137 let message_span = message.span();
138 let message_inputs = message.inputs().map(|input| {
139 let input_span = input.span();
140 let input_type = &*input.ty;
141 quote_spanned!(input_span=>
142 ::ink::codegen::utils::consume_type::<
143 ::ink::codegen::DispatchInput<#input_type>
144 >();
145 )
146 });
147 let message_output = message.output().map(|output_type| {
148 let output_span = output_type.span();
149 quote_spanned!(output_span=>
150 ::ink::codegen::utils::consume_type::<
151 ::ink::codegen::DispatchOutput<#output_type>
152 >();
153 )
154 });
155 quote_spanned!(message_span=>
156 #( #message_inputs )*
157 #message_output
158 )
159 }
160
161 fn generate_registry_for_message(
166 &self,
167 message: &ir::InkTraitMessage,
168 selector: ir::Selector,
169 ) -> TokenStream2 {
170 let span = message.span();
171 let ident = message.ident();
172 let attrs = message.attrs();
173 let cfg_attrs = message.get_cfg_attrs(span);
174 let output_ident = generator::output_ident(message.ident());
175 let output_type = message
176 .output()
177 .cloned()
178 .unwrap_or_else(|| parse_quote! { () });
179 let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
180 let (input_bindings, input_types) =
181 Self::input_bindings_and_types(message.inputs());
182 let linker_error_ident = EnforcedErrors::cannot_call_trait_message(
183 self.trait_ident(),
184 message.ident(),
185 selector,
186 message.mutates(),
187 );
188 let inout_guards = Self::generate_inout_guards_for_message(message);
189 let impl_body = match option_env!("INK_COVERAGE_REPORTING") {
190 Some("true") => {
191 quote! {
192 ::core::unreachable!(
197 "this is an invalid ink! message call which should never be possible."
198 );
199 }
200 }
201 _ => {
202 quote! {
203 extern {
206 fn #linker_error_ident() -> !;
207 }
208 unsafe { #linker_error_ident() }
209 }
210 }
211 };
212 quote_spanned!(span=>
213 #( #cfg_attrs )*
214 type #output_ident = #output_type;
215
216 #( #attrs )*
217 #[cold]
218 fn #ident(
219 & #mut_token self
220 #( , #input_bindings : #input_types )*
221 ) -> Self::#output_ident {
222 #inout_guards
223 #impl_body
224 }
225 )
226 }
227
228 fn input_bindings_and_types(
230 inputs: ir::InputsIter,
231 ) -> (Vec<syn::Ident>, Vec<&syn::Type>) {
232 inputs
233 .enumerate()
234 .map(|(n, pat_type)| {
235 let binding = format_ident!("__ink_binding_{}", n);
236 let ty = &*pat_type.ty;
237 (binding, ty)
238 })
239 .unzip()
240 }
241
242 fn generate_trait_info_object(&self) -> TokenStream2 {
248 let span = self.span();
249 let trait_id = self.generate_trait_id();
250 let trait_ident = self.trait_ident();
251 let trait_info_ident = self.trait_def.trait_info_ident();
252 let trait_call_forwarder = self.trait_def.call_forwarder_ident();
253 let trait_message_builder = self.trait_def.message_builder_ident();
254 let trait_message_info = self.generate_info_for_trait_messages();
255 quote_spanned!(span =>
256 #[doc(hidden)]
257 #[allow(non_camel_case_types)]
258 pub struct #trait_info_ident<E> {
259 marker: ::core::marker::PhantomData<fn() -> E>,
260 }
261
262 #trait_message_info
263
264 impl<E> ::ink::reflect::TraitInfo for #trait_info_ident<E>
265 where
266 E: ::ink::env::Environment,
267 {
268 const ID: u32 = #trait_id;
269
270 const PATH: &'static ::core::primitive::str = ::core::module_path!();
271
272 const NAME: &'static ::core::primitive::str = ::core::stringify!(#trait_ident);
273 }
274
275 impl<E> ::ink::codegen::TraitCallForwarder for #trait_info_ident<E>
276 where
277 E: ::ink::env::Environment,
278 {
279 type Forwarder = #trait_call_forwarder<E>;
280 }
281
282 impl<E> ::ink::codegen::TraitMessageBuilder for #trait_info_ident<E>
283 where
284 E: ::ink::env::Environment,
285 {
286 type MessageBuilder = #trait_message_builder<E>;
287 }
288 )
289 }
290
291 fn generate_trait_id(&self) -> syn::LitInt {
293 let span = self.span();
294 let mut id = 0u32;
295 debug_assert!(
296 self.trait_def
297 .trait_def
298 .item()
299 .iter_items()
300 .next()
301 .is_some(),
302 "invalid empty ink! trait definition"
303 );
304 for (_, selector) in self.trait_def.trait_def.item().iter_items() {
305 id ^= selector.into_be_u32()
306 }
307 syn::LitInt::new(&format!("{id}"), span)
308 }
309
310 fn generate_info_for_trait_messages(&self) -> TokenStream2 {
313 let span = self.span();
314 let message_impls = self.trait_def.trait_def.item().iter_items().filter_map(
315 |(trait_item, selector)| {
316 trait_item.filter_map_message().map(|message| {
317 self.generate_info_for_trait_for_message(&message, selector)
318 })
319 },
320 );
321 quote_spanned!(span=>
322 #( #message_impls )*
323 )
324 }
325
326 fn generate_info_for_trait_for_message(
329 &self,
330 message: &ir::InkTraitMessage,
331 selector: ir::Selector,
332 ) -> TokenStream2 {
333 let span = message.span();
334 let trait_info_ident = self.trait_def.trait_info_ident();
335 let local_id = message.local_id();
336 let selector_bytes = selector.hex_lits();
337 let is_payable = message.ink_attrs().is_payable();
338 quote_spanned!(span=>
343 impl<E> ::ink::reflect::TraitMessageInfo<#local_id> for #trait_info_ident<E> {
344 const PAYABLE: ::core::primitive::bool = #is_payable;
345
346 const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
347 }
348 )
349 }
350}