ink_codegen/generator/
dispatch.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::iter;
16
17use crate::{
18    generator,
19    generator::solidity::{
20        sol_type,
21        solidity_call_type_ident,
22        solidity_selector,
23        solidity_selector_id,
24    },
25    GenerateCode,
26};
27use derive_more::From;
28use ir::{
29    Callable,
30    CallableWithSelector,
31    Constructor,
32    HexLiteral as _,
33    Message,
34};
35use proc_macro2::{
36    Ident,
37    TokenStream as TokenStream2,
38};
39use quote::{
40    format_ident,
41    quote,
42    quote_spanned,
43};
44use syn::{
45    spanned::Spanned as _,
46    LitInt,
47};
48
49/// A message to be dispatched.
50/// Contains its callable and calculated unique id
51pub struct MessageDispatchable<'a> {
52    message: CallableWithSelector<'a, Message>,
53    id: TokenStream2,
54}
55
56/// A constructor to be dispatched.
57/// Contains its callable and calculated unique id
58pub struct ConstructorDispatchable<'a> {
59    constructor: CallableWithSelector<'a, Constructor>,
60    id: LitInt,
61}
62
63/// Generates code for the message and constructor dispatcher.
64///
65/// This code efficiently selects the dispatched ink! constructor or message
66/// by inspecting the first four bytes (selector) of the given input bytes.
67///
68/// As this happens on every contract execution this code must be highly optimized.
69/// For that purpose a so-called dispatch enum is being generated that has a
70/// specialized `scale::Decode` implementation taking the first four bytes of
71/// the input stream in order to identify the enum variant that it is going to
72/// produce out of the rest of the input buffer.
73///
74/// The rest of the input buffer is then automatically decoded directly into the
75/// expected input types of the respective ink! constructor or message.
76#[derive(From)]
77pub struct Dispatch<'a> {
78    contract: &'a ir::Contract,
79}
80impl_as_ref_for_generator!(Dispatch);
81
82impl GenerateCode for Dispatch<'_> {
83    fn generate_code(&self) -> TokenStream2 {
84        let messages = self.compose_messages_with_ids();
85        let constructors = self.compose_constructors_with_ids();
86
87        let contract_dispatchable_constructor_infos =
88            self.generate_dispatchable_constructor_infos();
89        let contract_dispatchable_messages_infos =
90            self.generate_dispatchable_message_infos();
91        let constructor_decoder_type =
92            self.generate_constructor_decoder_type(&constructors);
93        let message_decoder_type = self.generate_message_decoder_type(&messages);
94        let entry_points = self.generate_entry_points(&constructors, &messages);
95
96        let solidity_call_info = self.generate_solidity_call_info();
97
98        quote! {
99            #contract_dispatchable_constructor_infos
100            #contract_dispatchable_messages_infos
101            #constructor_decoder_type
102            #message_decoder_type
103
104            #solidity_call_info
105
106            #[cfg(not(any(test, feature = "std", feature = "ink-as-dependency")))]
107            mod __do_not_access__ {
108                use super::*;
109                #entry_points
110            }
111        }
112    }
113}
114
115impl Dispatch<'_> {
116    /// Returns the index of the ink! message which has a wildcard selector, if existent.
117    fn query_wildcard_message(&self) -> Option<usize> {
118        self.contract
119            .module()
120            .impls()
121            .flat_map(|item_impl| item_impl.iter_messages())
122            .position(|item| item.has_wildcard_selector())
123    }
124
125    /// Returns the index of the ink! constructor which has a wildcard selector, if
126    /// existent.
127    fn query_wildcard_constructor(&self) -> Option<usize> {
128        self.contract
129            .module()
130            .impls()
131            .flat_map(|item_impl| item_impl.iter_constructors())
132            .position(|item| item.has_wildcard_selector())
133    }
134
135    /// Puts messages and their calculated selector ids in a single data structure
136    ///
137    /// See [`MessageDispatchable`]
138    fn compose_messages_with_ids(&self) -> Vec<MessageDispatchable> {
139        let storage_ident = self.contract.module().storage().ident();
140        self.contract
141            .module()
142            .impls()
143            .flat_map(|item_impl| {
144                iter::repeat(item_impl.trait_path()).zip(item_impl.iter_messages())
145            })
146            .flat_map(|(trait_path, message)| {
147                let mut message_dispatchables = Vec::new();
148
149                if self.contract.config().abi().is_ink() {
150                    let span = message.span();
151                    let id = if let Some(trait_path) = trait_path {
152                        let local_id = message.local_id().hex_padded_suffixed();
153                        quote_spanned!(span=>
154                            {
155                                ::core::primitive::u32::from_be_bytes(
156                                    <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
157                                        as #trait_path>::__ink_TraitInfo
158                                        as ::ink::reflect::TraitMessageInfo<#local_id>>::SELECTOR
159                                )
160                            }
161                        )
162                    } else {
163                        let id = message
164                            .composed_selector()
165                            .into_be_u32()
166                            .hex_padded_suffixed();
167                        quote_spanned!(span=>
168                            #id
169                        )
170                    };
171                    message_dispatchables.push(MessageDispatchable { message, id });
172                }
173
174                if self.contract.config().abi().is_solidity() {
175                    let id = solidity_selector_id(&message);
176                    message_dispatchables.push(MessageDispatchable { message, id });
177                }
178
179                message_dispatchables
180            })
181            .collect::<Vec<_>>()
182    }
183
184    /// Puts constructors and their calculated selector ids in a single data structure
185    ///
186    /// See [`ConstructorDispatchable`]
187    fn compose_constructors_with_ids(&self) -> Vec<ConstructorDispatchable> {
188        self.contract
189            .module()
190            .impls()
191            .flat_map(|item_impl| item_impl.iter_constructors())
192            .map(|constructor| {
193                let id = constructor
194                    .composed_selector()
195                    .into_be_u32()
196                    .hex_padded_suffixed();
197                ConstructorDispatchable { constructor, id }
198            })
199            .collect::<Vec<_>>()
200    }
201
202    /// Generate code for the [`ink::DispatchableConstructorInfo`] trait implementations.
203    ///
204    /// These trait implementations store relevant dispatch information for every
205    /// dispatchable ink! constructor of the ink! smart contract.
206    fn generate_dispatchable_constructor_infos(&self) -> TokenStream2 {
207        let span = self.contract.module().storage().span();
208        let storage_ident = self.contract.module().storage().ident();
209        let constructor_infos = self
210            .contract
211            .module()
212            .impls()
213            .flat_map(|item_impl| item_impl.iter_constructors())
214            .map(|constructor| {
215                let constructor_span = constructor.span();
216                let constructor_ident = constructor.ident();
217                let payable = constructor.is_payable();
218                let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed();
219                let selector_bytes = constructor.composed_selector().hex_lits();
220                let cfg_attrs = constructor.get_cfg_attrs(constructor_span);
221                let output_type = constructor.output().map(quote::ToTokens::to_token_stream)
222                    .unwrap_or_else(|| quote! { () });
223                let input_bindings = generator::input_bindings(constructor.inputs());
224                let input_tuple_type = generator::input_types_tuple(constructor.inputs());
225                let input_tuple_bindings = generator::input_bindings_tuple(constructor.inputs());
226                let constructor_return_type = quote_spanned!(constructor_span=>
227                    <::ink::reflect::ConstructorOutputValue<#output_type>
228                        as ::ink::reflect::ConstructorOutput<#storage_ident>>
229                );
230                quote_spanned!(constructor_span =>
231                    #( #cfg_attrs )*
232                    impl ::ink::reflect::DispatchableConstructorInfo<#selector_id> for #storage_ident {
233                        type Input = #input_tuple_type;
234                        type Output = #output_type;
235                        type Storage = #storage_ident;
236                        type Error = #constructor_return_type::Error;
237                        const IS_RESULT: ::core::primitive::bool = #constructor_return_type::IS_RESULT;
238
239                        const CALLABLE: fn(Self::Input) -> Self::Output = |#input_tuple_bindings| {
240                            #storage_ident::#constructor_ident(#( #input_bindings ),* )
241                        };
242                        const PAYABLE: ::core::primitive::bool = #payable;
243                        const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
244                        const LABEL: &'static ::core::primitive::str = ::core::stringify!(#constructor_ident);
245                    }
246                )
247            });
248        quote_spanned!(span=>
249            #( #constructor_infos )*
250        )
251    }
252
253    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
254    ///
255    /// These trait implementations store relevant dispatch information for every
256    /// dispatchable ink! message of the ink! smart contract.
257    fn generate_dispatchable_message_infos(&self) -> TokenStream2 {
258        let span = self.contract.module().storage().span();
259        let abi = self.contract.config().abi();
260
261        let inherent_message_infos = self
262            .contract
263            .module()
264            .impls()
265            .filter(|item_impl| item_impl.trait_path().is_none())
266            .flat_map(|item_impl| item_impl.iter_messages())
267            .flat_map(|message| {
268                let mut message_infos = Vec::new();
269                if abi.is_ink() {
270                    message_infos
271                        .push(self.dispatchable_inherent_message_infos(&message));
272                }
273                if abi.is_solidity() {
274                    message_infos.push(self.solidity_message_info(&message, None));
275                }
276                message_infos
277            });
278        let trait_message_infos = self
279            .contract
280            .module()
281            .impls()
282            .filter_map(|item_impl| {
283                item_impl
284                    .trait_path()
285                    .map(|trait_path| {
286                        let trait_ident = item_impl.trait_ident().expect(
287                            "must have an ink! trait identifier if it is an ink! trait implementation"
288                        );
289                        iter::repeat((trait_ident, trait_path)).zip(item_impl.iter_messages())
290                    })
291            })
292            .flatten()
293            .flat_map(|((trait_ident, trait_path), message)| {
294                let mut message_infos = Vec::new();
295                if abi.is_ink() {
296                   message_infos.push(self.dispatchable_trait_message_infos(&message, trait_ident, trait_path));
297                }
298                if abi.is_solidity() {
299                    // NOTE: Solidity selectors are always computed from the call signature and input types.
300                    message_infos.push(self.solidity_message_info(&message, Some((trait_ident, trait_path))));
301                }
302                message_infos
303            });
304        quote_spanned!(span=>
305            #( #inherent_message_infos )*
306            #( #trait_message_infos )*
307        )
308    }
309
310    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
311    ///
312    /// These trait implementations store relevant dispatch information for every
313    /// inherent dispatchable ink! message of the ink! smart contract.
314    fn dispatchable_inherent_message_infos(
315        &self,
316        message: &CallableWithSelector<Message>,
317    ) -> TokenStream2 {
318        let storage_ident = self.contract.module().storage().ident();
319
320        let message_span = message.span();
321        let message_ident = message.ident();
322        let payable = message.is_payable();
323        let mutates = message.receiver().is_ref_mut();
324
325        let cfg_attrs = message.get_cfg_attrs(message_span);
326        let output_tuple_type = message
327            .output()
328            .map(quote::ToTokens::to_token_stream)
329            .unwrap_or_else(|| quote! { () });
330        let input_bindings = generator::input_bindings(message.inputs());
331        let input_tuple_type = generator::input_types_tuple(message.inputs());
332        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
333
334        let selector_id = message
335            .composed_selector()
336            .into_be_u32()
337            .hex_padded_suffixed();
338        let selector_bytes = message.composed_selector().hex_lits();
339
340        #[cfg(feature = "std")]
341        let return_type = quote! { () };
342        #[cfg(not(feature = "std"))]
343        let return_type = quote! { ! };
344
345        quote_spanned!(message_span=>
346            #( #cfg_attrs )*
347            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
348                type Input = #input_tuple_type;
349                type Output = #output_tuple_type;
350                type Storage = #storage_ident;
351
352                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
353                    |storage, #input_tuple_bindings| {
354                        #storage_ident::#message_ident( storage #( , #input_bindings )* )
355                    };
356                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
357                    |input| {
358                        <Self::Input as ::ink::scale::Decode>::decode(input)
359                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)
360                    };
361                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
362                    |flags, output| {
363                        ::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
364                            flags,
365                            // Currently no `LangError`s are raised at this level of the
366                            // dispatch logic so `Ok` is always returned to the caller.
367                            &::ink::MessageResult::Ok(output),
368                        )
369                    };
370                const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
371                const PAYABLE: ::core::primitive::bool = #payable;
372                const MUTATES: ::core::primitive::bool = #mutates;
373                const LABEL: &'static ::core::primitive::str = ::core::stringify!(#message_ident);
374                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
375            }
376        )
377    }
378
379    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
380    ///
381    /// These trait implementations store relevant dispatch information for every
382    /// trait implementation dispatchable ink! message of the ink! smart contract.
383    fn dispatchable_trait_message_infos(
384        &self,
385        message: &CallableWithSelector<Message>,
386        trait_ident: &Ident,
387        trait_path: &syn::Path,
388    ) -> TokenStream2 {
389        let storage_ident = self.contract.module().storage().ident();
390
391        let message_span = message.span();
392        let message_ident = message.ident();
393        let mutates = message.receiver().is_ref_mut();
394        let local_id = message.local_id().hex_padded_suffixed();
395        let payable = quote! {{
396            <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
397                as #trait_path>::__ink_TraitInfo
398                as ::ink::reflect::TraitMessageInfo<#local_id>>::PAYABLE
399        }};
400        let selector = quote! {{
401            <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
402                as #trait_path>::__ink_TraitInfo
403                as ::ink::reflect::TraitMessageInfo<#local_id>>::SELECTOR
404        }};
405        let selector_id = quote! {{
406            ::core::primitive::u32::from_be_bytes(#selector)
407        }};
408        let output_tuple_type = message
409            .output()
410            .map(quote::ToTokens::to_token_stream)
411            .unwrap_or_else(|| quote! { () });
412        let input_bindings = generator::input_bindings(message.inputs());
413        let input_tuple_type = generator::input_types_tuple(message.inputs());
414        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
415        let label = format!("{trait_ident}::{message_ident}");
416        let cfg_attrs = message.get_cfg_attrs(message_span);
417
418        #[cfg(feature = "std")]
419        let return_type = quote! { () };
420        #[cfg(not(feature = "std"))]
421        let return_type = quote! { ! };
422
423        quote_spanned!(message_span=>
424            #( #cfg_attrs )*
425            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
426                type Input = #input_tuple_type;
427                type Output = #output_tuple_type;
428                type Storage = #storage_ident;
429
430                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
431                    |storage, #input_tuple_bindings| {
432                        <#storage_ident as #trait_path>::#message_ident( storage #( , #input_bindings )* )
433                    };
434                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
435                    |input| {
436                        <Self::Input as ::ink::scale::Decode>::decode(input)
437                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)
438                    };
439                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
440                    |flags, output| {
441                        ::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
442                            flags,
443                            // Currently no `LangError`s are raised at this level of the
444                            // dispatch logic so `Ok` is always returned to the caller.
445                            &::ink::MessageResult::Ok(output),
446                        )
447                    };
448                const SELECTOR: [::core::primitive::u8; 4usize] = #selector;
449                const PAYABLE: ::core::primitive::bool = #payable;
450                const MUTATES: ::core::primitive::bool = #mutates;
451                const LABEL: &'static ::core::primitive::str = #label;
452                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
453            }
454        )
455    }
456
457    /// Generate code for [`ink::DispatchableMessageInfo`] trait implementations for
458    /// Solidity ABI compatible calls.
459    fn solidity_message_info(
460        &self,
461        message: &CallableWithSelector<Message>,
462        trait_info: Option<(&Ident, &syn::Path)>,
463    ) -> TokenStream2 {
464        let storage_ident = self.contract.module().storage().ident();
465        let message_span = message.span();
466        let message_ident = message.ident();
467        let payable = message.is_payable();
468        let mutates = message.receiver().is_ref_mut();
469
470        let cfg_attrs = message.get_cfg_attrs(message_span);
471        let output_tuple_type = message
472            .output()
473            .map(quote::ToTokens::to_token_stream)
474            .unwrap_or_else(|| quote! { () });
475        let input_bindings = generator::input_bindings(message.inputs());
476        let input_tuple_type = generator::input_types_tuple(message.inputs());
477        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
478
479        let selector_id = solidity_selector_id(message);
480        let selector_bytes = solidity_selector(message);
481
482        let (call_prefix, label) = match trait_info {
483            None => {
484                (
485                    quote! {
486                        #storage_ident
487                    },
488                    message_ident.to_string(),
489                )
490            }
491            Some((trait_ident, trait_path)) => {
492                (
493                    quote! {
494                        <#storage_ident as #trait_path>
495                    },
496                    format!("{trait_ident}::{message_ident}"),
497                )
498            }
499        };
500
501        // `alloy_sol_types::SolType` representation of input type(s).
502        let input_types_len = generator::input_types(message.inputs()).len();
503        let input_sol_tys = message.inputs().map(|input| {
504            let ty = &*input.ty;
505            let span = input.span();
506            let sol_ty = sol_type(ty);
507            quote_spanned!(span=>
508                #sol_ty
509            )
510        });
511        let input_sol_tuple = if input_types_len == 1 {
512            quote! {
513                #(#input_sol_tys)*
514            }
515        } else {
516            quote! {
517                (#(#input_sol_tys,)*)
518            }
519        };
520
521        // Convert from `alloy_sol_types::private::SolTypeValue` to input type(s).
522        let sol_to_input_arg_name = quote!(sol_input_value);
523        let sol_value_tuple_elems = message.inputs().enumerate().map(|(idx, input)| {
524            let ty = &*input.ty;
525            let span = input.span();
526            let value = if input_types_len == 1 {
527                sol_to_input_arg_name.clone()
528            } else {
529                let field_idx = syn::Index::from(idx);
530                quote! {
531                    #sol_to_input_arg_name.#field_idx
532                }
533            };
534            quote_spanned!(span=>
535                <#ty as ::core::convert::From<_>>::from(#value)
536            )
537        });
538        let sol_to_input_tuple_converter = match input_types_len {
539            0 => {
540                quote! {
541                    |_| ()
542                }
543            }
544            1 => {
545                quote! {
546                    |#sol_to_input_arg_name| #(#sol_value_tuple_elems)*
547                }
548            }
549            _ => {
550                quote! {
551                    |#sol_to_input_arg_name| (#(#sol_value_tuple_elems,)*)
552                }
553            }
554        };
555
556        #[cfg(feature = "std")]
557        let return_type = quote! { () };
558        #[cfg(not(feature = "std"))]
559        let return_type = quote! { ! };
560
561        quote_spanned!(message_span=>
562            #( #cfg_attrs )*
563            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
564                type Input = #input_tuple_type;
565                type Output = #output_tuple_type;
566                type Storage = #storage_ident;
567
568                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
569                    |storage, #input_tuple_bindings| {
570                        #call_prefix::#message_ident( storage #( , #input_bindings )* )
571                    };
572                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
573                    |input| {
574                        #[allow(clippy::useless_conversion)]
575                        <#input_sol_tuple as ::ink::alloy_sol_types::SolType>
576                            ::abi_decode(input, false)
577                            .map(#sol_to_input_tuple_converter).map_err(|_| ::ink::env::DispatchError::InvalidParameters)
578                    };
579                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
580                    |flags, output| {
581                        ::ink::env::return_value_solidity::<Self::Output>(
582                            flags,
583                            &output,
584                        )
585                    };
586                const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
587                const PAYABLE: ::core::primitive::bool = #payable;
588                const MUTATES: ::core::primitive::bool = #mutates;
589                const LABEL: &'static ::core::primitive::str = #label;
590                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Solidity;
591            }
592        )
593    }
594
595    /// Generates code for the entry points of the root ink! smart contract.
596    ///
597    /// This generates the `deploy` and `call` functions with which the smart
598    /// contract runtime mainly interacts with the ink! smart contract.
599    fn generate_entry_points(
600        &self,
601        constructors: &[ConstructorDispatchable],
602        messages: &[MessageDispatchable],
603    ) -> TokenStream2 {
604        let span = self.contract.module().storage().span();
605        let storage_ident = self.contract.module().storage().ident();
606        let any_constructor_accept_payment =
607            self.any_constructor_accepts_payment(constructors);
608        let any_message_accepts_payment = self.any_message_accepts_payment(messages);
609
610        let fn_call: syn::ItemFn = syn::parse_quote! {
611            #[cfg(target_arch = "riscv64")]
612            #[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
613            pub extern "C" fn call() {
614                internal_call()
615            }
616        };
617        let fn_deploy: syn::ItemFn = syn::parse_quote! {
618            #[cfg(target_arch = "riscv64")]
619            #[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
620            pub extern "C" fn deploy() {
621                internal_deploy()
622            }
623        };
624        quote_spanned!(span=>
625            #[allow(dead_code)] // clippy throws a false positive otherwise
626            #[allow(clippy::nonminimal_bool)]
627            #[cfg(target_arch = "riscv64")]
628            fn internal_deploy() {
629                if !#any_constructor_accept_payment {
630                    ::ink::codegen::deny_payment()
631                        .unwrap_or_else(|error| ::core::panic!("{}", error))
632                }
633
634                let dispatchable = match ::ink::env::decode_input::<
635                    <#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type,
636                >() {
637                    ::core::result::Result::Ok(decoded_dispatchable) => {
638                        decoded_dispatchable
639                    }
640                    ::core::result::Result::Err(_decoding_error) => {
641                        let error = ::ink::ConstructorResult::Err(::ink::LangError::CouldNotReadInput);
642
643                        // At this point we're unable to set the `Ok` variant to be the "real"
644                        // constructor output since we were unable to figure out what the caller wanted
645                        // to dispatch in the first place, so we set it to `()`.
646                        //
647                        // This is okay since we're going to only be encoding the `Err` variant
648                        // into the output buffer anyway.
649                        ::ink::env::return_value::<::ink::ConstructorResult<()>>(
650                            ::ink::env::ReturnFlags::REVERT,
651                            &error,
652                        );
653                    }
654                };
655
656                <<#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type
657                    as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
658                .unwrap_or_else(|error| {
659                    ::core::panic!("dispatching ink! constructor failed: {}", error)
660                })
661            }
662
663            #[allow(dead_code)] // clippy throws a false positive otherwise
664            #[allow(clippy::nonminimal_bool)]
665            #[cfg(target_arch = "riscv64")]
666            fn internal_call() {
667                if !#any_message_accepts_payment {
668                    ::ink::codegen::deny_payment()
669                        .unwrap_or_else(|error| ::core::panic!("{}", error))
670                }
671
672                let dispatchable = match ::ink::env::decode_input::<
673                    <#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type,
674                >() {
675                    ::core::result::Result::Ok(decoded_dispatchable) => {
676                        decoded_dispatchable
677                    }
678                    ::core::result::Result::Err(_decoding_error) => {
679                        let error = ::ink::MessageResult::Err(::ink::LangError::CouldNotReadInput);
680
681                        // At this point we're unable to set the `Ok` variant to be the "real"
682                        // message output since we were unable to figure out what the caller wanted
683                        // to dispatch in the first place, so we set it to `()`.
684                        //
685                        // This is okay since we're going to only be encoding the `Err` variant
686                        // into the output buffer anyway.
687                        ::ink::env::return_value::<::ink::MessageResult<()>>(
688                            ::ink::env::ReturnFlags::REVERT,
689                            &error,
690                        );
691                    }
692                };
693
694                <<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type
695                    as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
696                .unwrap_or_else(|error| {
697                    ::core::panic!("dispatching ink! message failed: {}", error)
698                })
699            }
700
701            #fn_call
702
703            #fn_deploy
704        )
705    }
706
707    /// Generates code for the ink! constructor decoder type of the ink! smart contract.
708    ///
709    /// This type can be used in order to decode the input bytes received by a call to
710    /// `deploy` into one of the available dispatchable ink! constructors and their
711    /// arguments.
712    fn generate_constructor_decoder_type(
713        &self,
714        constructors: &[ConstructorDispatchable],
715    ) -> TokenStream2 {
716        /// Expands into the token sequence to represent the
717        /// input type of the ink! constructor at the given index.
718        fn expand_constructor_input(
719            span: proc_macro2::Span,
720            storage_ident: &syn::Ident,
721            constructor_id: LitInt,
722        ) -> TokenStream2 {
723            quote_spanned!(span=>
724                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #constructor_id >>::Input
725            )
726        }
727
728        /// Returns the n-th ink! constructor identifier for the decoder type.
729        fn constructor_variant_ident(n: usize) -> syn::Ident {
730            quote::format_ident!("Constructor{}", n)
731        }
732
733        let span = self.contract.module().storage().span();
734        let storage_ident = self.contract.module().storage().ident();
735        let constructors_variants =
736            constructors.iter().enumerate().map(|(index, item)| {
737                let constructor_span = item.constructor.span();
738                let constructor_ident = constructor_variant_ident(index);
739                let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
740                let constructor_input = expand_constructor_input(
741                    constructor_span,
742                    storage_ident,
743                    item.id.clone(),
744                );
745                quote_spanned!(constructor_span=>
746                    #( #cfg_attrs )*
747                    #constructor_ident(#constructor_input)
748                )
749            });
750
751        let constructor_selector=
752            constructors.iter().enumerate().map(|(index, item)| {
753            let constructor_span = item.constructor.span();
754            let const_ident = format_ident!("CONSTRUCTOR_{}", index);
755            let id = item.id.clone();
756            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
757            quote_spanned!(span=>
758                #( #cfg_attrs )*
759                const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::SELECTOR;
760            )
761        });
762
763        let constructor_match = constructors.iter().enumerate().map(|(index, item)| {
764            let constructor_span = item.constructor.span();
765            let constructor_ident = constructor_variant_ident(index);
766            let const_ident = format_ident!("CONSTRUCTOR_{}", index);
767            let constructor_input = expand_constructor_input(
768                constructor_span,
769                storage_ident,
770                item.id.clone(),
771            );
772            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
773            quote_spanned!(constructor_span=>
774                #( #cfg_attrs )*
775                #const_ident => {
776                    ::core::result::Result::Ok(Self::#constructor_ident(
777                        <#constructor_input as ::ink::scale::Decode>::decode(input)
778                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
779                    ))
780                }
781            )
782        });
783        let possibly_wildcard_selector_constructor = match self
784            .query_wildcard_constructor()
785        {
786            Some(wildcard_index) => {
787                let item = &constructors[wildcard_index];
788                let constructor_span = item.constructor.span();
789                let constructor_ident = constructor_variant_ident(wildcard_index);
790                let constructor_input = expand_constructor_input(
791                    constructor_span,
792                    storage_ident,
793                    item.id.clone(),
794                );
795                quote! {
796                    ::core::result::Result::Ok(Self::#constructor_ident(
797                        <#constructor_input as ::ink::scale::Decode>::decode(input)
798                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
799                    ))
800                }
801            }
802            None => {
803                quote! {
804                    ::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
805                }
806            }
807        };
808
809        let constructor_execute = constructors.iter().enumerate().map(|(index, item)| {
810            let constructor_span = item.constructor.span();
811            let constructor_ident = constructor_variant_ident(index);
812            let id = item.id.clone();
813            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
814            let constructor_callable = quote_spanned!(constructor_span=>
815                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::CALLABLE
816            );
817            let constructor_output = quote_spanned!(constructor_span=>
818                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::Output
819            );
820            let deny_payment = quote_spanned!(constructor_span=>
821                !<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE
822            );
823            let constructor_value = quote_spanned!(constructor_span=>
824                <::ink::reflect::ConstructorOutputValue<#constructor_output>
825                    as ::ink::reflect::ConstructorOutput::<#storage_ident>>
826            );
827
828            let constructor_accept_payment_assignment =
829                self.any_constructor_accepts_payment(constructors);
830
831            quote_spanned!(constructor_span=>
832                #( #cfg_attrs )*
833                Self::#constructor_ident(input) => {
834                    if #constructor_accept_payment_assignment && #deny_payment {
835                        ::ink::codegen::deny_payment()?;
836                    }
837
838                    let result: #constructor_output = #constructor_callable(input);
839                    let output_value = ::ink::reflect::ConstructorOutputValue::new(result);
840                    let output_result = #constructor_value::as_result(&output_value);
841
842                    if let ::core::result::Result::Ok(contract) = output_result.as_ref() {
843                        ::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
844                            &<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
845                            contract,
846                        );
847                    }
848
849                    // NOTE: we can't use an if/else expression here
850                    // It fails inside quote_spanned! macro.
851                    // See https://github.com/rust-lang/rust-clippy/issues/6249
852                    let mut flag = ::ink::env::ReturnFlags::empty();
853                    if output_result.is_err() {
854                        flag = ::ink::env::ReturnFlags::REVERT;
855                    }
856
857                    ::ink::env::return_value::<
858                        ::ink::ConstructorResult<
859                            ::core::result::Result<(), &#constructor_value::Error>
860                        >,
861                    >(
862                        flag,
863                        // Currently no `LangError`s are raised at this level of the
864                        // dispatch logic so `Ok` is always returned to the caller.
865                        &::ink::ConstructorResult::Ok(output_result.map(|_| ())),
866                    );
867
868                    #[cfg(feature = "std")]
869                    return ::core::result::Result::Ok(());
870
871                    #[cfg(not(feature = "std"))]
872                    #[cfg_attr(not(feature = "std"), allow(unreachable_code))]
873                    {
874                        ::core::unreachable!("either `return_value` or the `return` before will already have returned");
875                    }
876                }
877            )
878        });
879        quote_spanned!(span=>
880            const _: () = {
881                #[allow(non_camel_case_types)]
882                pub enum __ink_ConstructorDecoder {
883                    #( #constructors_variants ),*
884                }
885
886                impl ::ink::env::DecodeDispatch for __ink_ConstructorDecoder {
887                    fn decode_dispatch(input: &mut &[::core::primitive::u8])
888                        -> ::core::result::Result<Self, ::ink::env::DispatchError> {
889                        #(
890                            #constructor_selector
891                        )*
892                        match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
893                            .map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
894                        {
895                            #( #constructor_match , )*
896                            _invalid => #possibly_wildcard_selector_constructor
897                        }
898                    }
899                }
900
901                impl ::ink::reflect::ExecuteDispatchable for __ink_ConstructorDecoder {
902                    #[allow(clippy::nonminimal_bool, dead_code)]
903                    fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink::env::DispatchError> {
904                        match self {
905                            #( #constructor_execute ),*
906                        }
907                    }
908                }
909
910                impl ::ink::reflect::ContractConstructorDecoder for #storage_ident {
911                    type Type = __ink_ConstructorDecoder;
912                }
913            };
914        )
915    }
916
917    /// Generates code for the ink! message decoder type of the ink! smart contract.
918    ///
919    /// This type can be used in order to decode the input bytes received by a call to
920    /// `call` into one of the available dispatchable ink! messages and their
921    /// arguments.
922    fn generate_message_decoder_type(
923        &self,
924        messages: &[MessageDispatchable],
925    ) -> TokenStream2 {
926        /// Expands into the token sequence to represent the
927        /// input type of the ink! message at the given index.
928        fn expand_message_input(
929            span: proc_macro2::Span,
930            storage_ident: &syn::Ident,
931            message_id: TokenStream2,
932        ) -> TokenStream2 {
933            quote_spanned!(span=>
934                <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #message_id >>::Input
935            )
936        }
937
938        /// Returns the n-th ink! message identifier for the decoder type.
939        fn message_variant_ident(n: usize) -> syn::Ident {
940            quote::format_ident!("Message{}", n)
941        }
942
943        let span = self.contract.module().storage().span();
944        let storage_ident = self.contract.module().storage().ident();
945        let message_variants = messages.iter().enumerate().map(|(index, item)| {
946            let message_span = item.message.span();
947            let message_ident = message_variant_ident(index);
948            let cfg_attrs = item.message.get_cfg_attrs(message_span);
949            let message_input =
950                expand_message_input(message_span, storage_ident, item.id.clone());
951            quote_spanned!(message_span=>
952                #( #cfg_attrs )*
953                #message_ident(#message_input)
954            )
955        });
956
957        let message_selector = messages
958            .iter()
959            .enumerate()
960            .map(|(index, item)|  {
961                let message_span = item.message.span();
962                let const_ident = format_ident!("MESSAGE_{}", index);
963                let cfg_attrs = item.message.get_cfg_attrs(message_span);
964                let id = item.id.clone();
965                quote_spanned!(span=>
966                    #( #cfg_attrs )*
967                    const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::SELECTOR;
968                )
969        });
970
971        let message_match =  messages
972            .iter()
973            .enumerate()
974            .map(|(index, item)| {
975                let message_ident = message_variant_ident(index);
976                let const_ident = format_ident!("MESSAGE_{}", index);
977                let message_span = item.message.span();
978                let cfg_attrs = item.message.get_cfg_attrs(message_span);
979                let id = item.id.clone();
980                quote_spanned!(message_span=>
981                   #( #cfg_attrs )*
982                    #const_ident => {
983                        ::core::result::Result::Ok(Self::#message_ident(
984                            <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::DECODE(input)?
985                        ))
986                    }
987                )
988        });
989        let possibly_wildcard_selector_message = match self.query_wildcard_message() {
990            Some(wildcard_index) => {
991                let item = messages
992                    .get(wildcard_index)
993                    .expect("unable to get wildcard index");
994                let message_span = item.message.span();
995                let message_ident = message_variant_ident(wildcard_index);
996                let message_input =
997                    expand_message_input(message_span, storage_ident, item.id.clone());
998                quote! {
999                    ::core::result::Result::Ok(Self::#message_ident(
1000                        <#message_input as ::ink::scale::Decode>::decode(input)
1001                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
1002                    ))
1003                }
1004            }
1005            None => {
1006                quote! {
1007                    ::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
1008                }
1009            }
1010        };
1011
1012        let message_execute = messages
1013            .iter()
1014            .enumerate()
1015            .map(|(index, item)| {
1016                let message_span = item.message.span();
1017                let message_ident = message_variant_ident(index);
1018                let id = item.id.clone();
1019                let cfg_attrs = item.message.get_cfg_attrs(message_span);
1020                let message_callable = quote_spanned!(message_span=>
1021                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::CALLABLE
1022                );
1023                let message_return = quote_spanned!(message_span=>
1024                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::RETURN
1025                );
1026                let message_output = quote_spanned!(message_span=>
1027                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::Output
1028                );
1029                let deny_payment = quote_spanned!(message_span=>
1030                    !<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE
1031                );
1032                let mutates_storage = quote_spanned!(message_span=>
1033                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::MUTATES
1034                );
1035
1036                let any_message_accepts_payment =
1037                    self.any_message_accepts_payment(messages);
1038
1039                quote_spanned!(message_span=>
1040                    #( #cfg_attrs )*
1041                    Self::#message_ident(input) => {
1042                        if #any_message_accepts_payment && #deny_payment {
1043                            ::ink::codegen::deny_payment()?;
1044                        }
1045
1046                        let result: #message_output = #message_callable(&mut contract, input);
1047                        let is_reverted = ::ink::is_result_type!(#message_output)
1048                            && ::ink::is_result_err!(result);
1049
1050                        // NOTE: we can't use an if/else expression here
1051                        // It fails inside quote_spanned! macro.
1052                        // See https://github.com/rust-lang/rust-clippy/issues/6249
1053                        let mut flag = ::ink::env::ReturnFlags::REVERT;
1054
1055                        // no need to push back results: transaction gets reverted anyway
1056                        if !is_reverted {
1057                            flag = ::ink::env::ReturnFlags::empty();
1058                            push_contract(contract, #mutates_storage);
1059                        }
1060
1061                        #message_return(flag, result);
1062
1063                        #[cfg(feature = "std")]
1064                        return ::core::result::Result::Ok(());
1065
1066                        #[cfg(not(feature = "std"))]
1067                        #[cfg_attr(not(feature = "std"), allow(unreachable_code))]
1068                        {
1069                            ::core::unreachable!("either `return_value` or the `return` before will already have returned");
1070                        }
1071                    }
1072                )
1073        });
1074
1075        quote_spanned!(span=>
1076            const _: () = {
1077                #[allow(non_camel_case_types)]
1078                pub enum __ink_MessageDecoder {
1079                    #( #message_variants ),*
1080                }
1081
1082                impl ::ink::env::DecodeDispatch for __ink_MessageDecoder {
1083                    fn decode_dispatch(input: &mut &[::core::primitive::u8])
1084                        -> ::core::result::Result<Self, ::ink::env::DispatchError> {
1085                        #(
1086                            #message_selector
1087                        )*
1088                        match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
1089                            .map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
1090                        {
1091                            #( #message_match , )*
1092                            _invalid => #possibly_wildcard_selector_message
1093                        }
1094                    }
1095                }
1096
1097                fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) {
1098                    if mutates {
1099                        ::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
1100                            &<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
1101                            &contract,
1102                        );
1103                    }
1104                }
1105
1106                impl ::ink::reflect::ExecuteDispatchable for __ink_MessageDecoder {
1107                    #[allow(clippy::nonminimal_bool, clippy::let_unit_value, dead_code)]
1108                    fn execute_dispatchable(
1109                        self
1110                    ) -> ::core::result::Result<(), ::ink::env::DispatchError> {
1111                        let key = <#storage_ident as ::ink::storage::traits::StorageKey>::KEY;
1112                        let mut contract: ::core::mem::ManuallyDrop<#storage_ident> =
1113                            ::core::mem::ManuallyDrop::new(
1114                                match ::ink::env::get_contract_storage(&key) {
1115                                    ::core::result::Result::Ok(::core::option::Option::Some(value)) => value,
1116                                    ::core::result::Result::Ok(::core::option::Option::None) => {
1117                                        ::core::panic!("storage entry was empty")
1118                                    },
1119                                    ::core::result::Result::Err(_) => {
1120                                        ::core::panic!("could not properly decode storage entry")
1121                                    },
1122                                }
1123                            );
1124
1125                        match self {
1126                            #( #message_execute ),*
1127                        }
1128                    }
1129                }
1130
1131                impl ::ink::reflect::ContractMessageDecoder for #storage_ident {
1132                    type Type = __ink_MessageDecoder;
1133                }
1134            };
1135        )
1136    }
1137
1138    /// Generates code to express if any dispatchable ink! message accepts payment.
1139    ///
1140    /// Generates code in the form of variable assignments
1141    /// which can be conditionally omitted
1142    /// in which case the default assignment `let message_{id} = false` exists.
1143    ///
1144    /// This information can be used to speed-up dispatch since denying of payment
1145    /// can be generalized to work before dispatch happens if none of the ink! messages
1146    /// accept payment anyways.
1147    fn any_message_accepts_payment(
1148        &self,
1149        messages: &[MessageDispatchable],
1150    ) -> TokenStream2 {
1151        let span = self.contract.module().storage().span();
1152        let storage_ident = self.contract.module().storage().ident();
1153        let message_is_payable =  messages
1154            .iter()
1155            .enumerate()
1156            .map(|(index, item)| {
1157            let message_span = item.message.span();
1158            let cfg_attrs = item.message.get_cfg_attrs(message_span);
1159            let id = item.id.clone();
1160            let ident = quote::format_ident!("message_{}", index);
1161            quote_spanned!(message_span=>
1162                {
1163                    let #ident = false;
1164                    #( #cfg_attrs )*
1165                    let #ident = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE;
1166                    #ident
1167                }
1168            )
1169        });
1170        quote_spanned!(span=>
1171            {
1172                false #( || #message_is_payable )*
1173            }
1174        )
1175    }
1176
1177    /// Generates code to express if any dispatchable ink! constructor accepts payment.
1178    ///
1179    /// Generates code in the form of variable assignments
1180    /// which can be conditionally omitted
1181    /// in which case the default assignment `let constructor_{id} = false` exists.
1182    ///
1183    /// This information can be used to speed-up dispatch since denying of payment
1184    /// can be generalized to work before dispatch happens if none of the ink!
1185    /// constructors accept payment anyways.
1186    fn any_constructor_accepts_payment(
1187        &self,
1188        constructors: &[ConstructorDispatchable],
1189    ) -> TokenStream2 {
1190        let span = self.contract.module().storage().span();
1191        let storage_ident = self.contract.module().storage().ident();
1192        let constructor_is_payable = constructors.iter().enumerate().map(|(index, item)| {
1193            let constructor_span = item.constructor.span();
1194            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
1195            let id = item.id.clone();
1196            let ident = quote::format_ident!("constructor_{}", index);
1197            quote_spanned!(constructor_span=>
1198                {
1199                    let #ident = false;
1200                    #( #cfg_attrs )*
1201                    let #ident = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE;
1202                    #ident
1203                }
1204            )
1205        });
1206        quote_spanned!(span=>
1207            {
1208                false #( || #constructor_is_payable )*
1209            }
1210        )
1211    }
1212
1213    /// Generates code for Solidity call info types for dispatchable ink! messages.
1214    ///
1215    /// See [`ink::alloy_sol_types::SolCall`]
1216    fn generate_solidity_call_info(&self) -> TokenStream2 {
1217        if self.contract.config().abi().is_solidity() {
1218            let span = self.contract.module().span();
1219            let call_tys = self
1220                .contract
1221                .module()
1222                .impls()
1223                .flat_map(|item_impl| item_impl.iter_messages())
1224                .map(|message| {
1225                    let span = message.span();
1226                    let ident = message.ident();
1227                    let ident_str = ident.to_string();
1228                    let call_type_ident = solidity_call_type_ident(&message);
1229
1230                    // Solidity call signature parts.
1231                    let sig_param_tys = message.inputs().map(|input| {
1232                        let ty = &*input.ty;
1233                        let span = input.span();
1234                        let sol_ty = sol_type(ty);
1235                        quote_spanned!(span=>
1236                            <#sol_ty as ::ink::alloy_sol_types::SolType>::SOL_NAME
1237                        )
1238                    });
1239                    let input_types_len = generator::input_types(message.inputs()).len();
1240                    let sig_arg_fmt_params = (0..input_types_len).map(|_| "{}").collect::<Vec<_>>().join(",");
1241                    let sig_fmt_lit = format!("{{}}({})", sig_arg_fmt_params);
1242
1243                    quote_spanned!(span=>
1244                        pub struct #call_type_ident;
1245
1246                        const _: () = {
1247                            impl #call_type_ident {
1248                                pub const SIGNATURE: &'static ::core::primitive::str = ::ink::codegen::const_format!(#sig_fmt_lit, #ident_str #(,#sig_param_tys)*);
1249                                pub const SELECTOR: [::core::primitive::u8; 4usize] = ::ink::codegen::sol_selector_bytes(Self::SIGNATURE);
1250                                pub const SELECTOR_ID: ::core::primitive::u32 = ::core::primitive::u32::from_be_bytes(Self::SELECTOR);
1251                            }
1252                        };
1253                    )
1254                });
1255            quote_spanned!(span=>
1256                #[allow(non_camel_case_types)]
1257                mod __ink_sol_interop__ {
1258                    #[allow(unused)]
1259                    use super::*;
1260
1261                    #(#call_tys)*
1262                }
1263            )
1264        } else {
1265            quote!()
1266        }
1267    }
1268}