ink_ir/ir/item_impl/
callable.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
15//! Utilities and helper routines that are useful for both ink! messages
16//! and ink! constructors.
17
18use crate::ir;
19use core::fmt;
20use proc_macro2::{
21    Ident,
22    Span,
23};
24use quote::ToTokens as _;
25use syn::spanned::Spanned as _;
26
27/// The kind of externally callable smart contract entity.
28#[derive(Debug, Copy, Clone)]
29pub enum CallableKind {
30    /// An ink! message externally callable.
31    Message,
32    /// An ink! constructor externally callable.
33    Constructor,
34}
35
36impl fmt::Display for CallableKind {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Message => write!(f, "message"),
40            Self::Constructor => write!(f, "constructor"),
41        }
42    }
43}
44
45/// Wrapper for a callable that adds its composed selector.
46#[derive(Debug)]
47pub struct CallableWithSelector<'a, C> {
48    /// The composed selector computed by the associated implementation block
49    /// and the given callable.
50    composed_selector: ir::Selector,
51    /// The parent implementation block.
52    item_impl: &'a ir::ItemImpl,
53    /// The actual callable.
54    callable: &'a C,
55}
56
57impl<C> Copy for CallableWithSelector<'_, C> {}
58impl<C> Clone for CallableWithSelector<'_, C> {
59    fn clone(&self) -> Self {
60        *self
61    }
62}
63
64impl<'a, C> CallableWithSelector<'a, C>
65where
66    C: Callable,
67{
68    /// Creates a new wrapper around the given callable and parent `impl` block.
69    pub(super) fn new(item_impl: &'a ir::ItemImpl, callable: &'a C) -> Self {
70        Self {
71            composed_selector: compose_selector(item_impl, callable),
72            item_impl,
73            callable,
74        }
75    }
76}
77
78impl<'a, C> CallableWithSelector<'a, C> {
79    /// Returns the composed selector of the ink! callable the `impl` block.
80    pub fn composed_selector(&self) -> ir::Selector {
81        self.composed_selector
82    }
83
84    /// Returns a shared reference to the underlying callable.
85    pub fn callable(&self) -> &'a C {
86        self.callable
87    }
88
89    /// Returns the parent implementation block of the ink! callable.
90    pub fn item_impl(&self) -> &'a ir::ItemImpl {
91        self.item_impl
92    }
93}
94
95impl<C> Callable for CallableWithSelector<'_, C>
96where
97    C: Callable,
98{
99    fn kind(&self) -> CallableKind {
100        <C as Callable>::kind(self.callable)
101    }
102
103    fn ident(&self) -> &Ident {
104        <C as Callable>::ident(self.callable)
105    }
106
107    fn user_provided_selector(&self) -> Option<&ir::Selector> {
108        <C as Callable>::user_provided_selector(self.callable)
109    }
110
111    fn is_payable(&self) -> bool {
112        <C as Callable>::is_payable(self.callable)
113    }
114
115    fn is_default(&self) -> bool {
116        <C as Callable>::is_default(self.callable)
117    }
118
119    fn has_wildcard_selector(&self) -> bool {
120        <C as Callable>::has_wildcard_selector(self.callable)
121    }
122
123    fn has_wildcard_complement_selector(&self) -> bool {
124        <C as Callable>::has_wildcard_complement_selector(self.callable)
125    }
126
127    fn visibility(&self) -> Visibility {
128        <C as Callable>::visibility(self.callable)
129    }
130
131    fn inputs(&self) -> InputsIter<'_> {
132        <C as Callable>::inputs(self.callable)
133    }
134
135    fn inputs_span(&self) -> Span {
136        <C as Callable>::inputs_span(self.callable)
137    }
138
139    fn statements(&self) -> &[syn::Stmt] {
140        <C as Callable>::statements(self.callable)
141    }
142
143    fn name(&self) -> Option<&str> {
144        <C as Callable>::name(self.callable)
145    }
146
147    fn normalized_name(&self) -> String {
148        self.name()
149            .map(ToString::to_string)
150            .unwrap_or_else(|| self.ident().to_string())
151    }
152}
153
154impl<C> ::core::ops::Deref for CallableWithSelector<'_, C> {
155    type Target = C;
156
157    fn deref(&self) -> &Self::Target {
158        self.callable
159    }
160}
161
162/// An ink! callable.
163///
164/// This is either an ink! message or an ink! constructor.
165/// Used to share common behavior between different callable types.
166pub trait Callable {
167    /// Returns the kind of the ink! callable.
168    fn kind(&self) -> CallableKind;
169
170    /// Returns the identifier of the ink! callable.
171    fn ident(&self) -> &Ident;
172
173    /// Returns the selector of the ink! callable if any has been manually set.
174    fn user_provided_selector(&self) -> Option<&ir::Selector>;
175
176    /// Returns `true` if the ink! callable is flagged as payable.
177    ///
178    /// # Note
179    ///
180    /// Flagging as payable is done using the `#[ink(payable)]` attribute.
181    fn is_payable(&self) -> bool;
182
183    /// Returns `true` if the ink! callable is flagged as default.
184    ///
185    /// # Note
186    ///
187    /// Flagging as default is done using the `#[ink(default)]` attribute.
188    fn is_default(&self) -> bool;
189
190    /// Returns `true` if the ink! callable is flagged as a wildcard selector.
191    fn has_wildcard_selector(&self) -> bool;
192
193    /// Returns `true` if the ink! callable is flagged as a wildcard complement selector.
194    fn has_wildcard_complement_selector(&self) -> bool;
195
196    /// Returns the visibility of the ink! callable.
197    fn visibility(&self) -> Visibility;
198
199    /// Returns an iterator yielding all input parameters of the ink! callable.
200    fn inputs(&self) -> InputsIter<'_>;
201
202    /// Returns the span of the inputs of the ink! callable.
203    fn inputs_span(&self) -> Span;
204
205    /// Returns a slice over shared references to the statements of the callable.
206    fn statements(&self) -> &[syn::Stmt];
207
208    /// Returns the function name override (if any).
209    fn name(&self) -> Option<&str>;
210
211    /// Returns the "normalized" function name
212    ///
213    /// # Note
214    /// This returns the name override (if provided), otherwise the identifier is
215    /// returned.
216    fn normalized_name(&self) -> String;
217}
218
219/// Returns the composed selector of the ink! callable.
220///
221/// Composition takes into account the given [`ir::ItemImpl`].
222///
223/// # Details
224///
225/// Given
226///
227/// - the identifier `i` of the callable
228/// - the optionally set selector `s` of the callable
229/// - the `impl` blocks trait path in case it implements a trait, `P`
230/// - 16 kB blocks optional user provided namespace `S`
231///
232/// Then the selector is composed in the following way:
233///
234/// - If `s` is given we simply return `s`.
235/// - Otherwise if `T` is not `None` (trait `impl` block) we concatenate `S`, `T` and `i`
236///   with `::` as separator if `T` refers to a full-path. If `T` refers to a relative
237///   path or is just an identifier we only take its last segment `p` (e.g. the trait's
238///   identifier) into consideration and use it instead of `P` in the above concatenation.
239///   In the following we refer to the resulting concatenation as `C`.
240/// - Now we take the BLAKE-2 hash of `C` which results in 32 bytes of output and take the
241///   first 4 bytes that are returned in order as the composed selector.
242///
243/// # Examples
244///
245/// ## Overriding the composed selector
246///
247/// Given
248///
249/// ```no_compile
250/// impl MyStorage {
251///     #[ink(message, selector = 0xDEADBEEF)]
252///     fn my_message(&self) {}
253/// }
254/// ```
255///
256/// …then the selector of `my_message` is simply `0xDEADBEEF` since it overrides
257/// the composed selector.
258///
259/// ## Inherent implementation block
260///
261/// Given
262///
263/// ```no_compile
264/// impl MyStorage {
265///     #[ink(message)]
266///     fn my_message(&self) {}
267/// }
268/// ```
269///
270/// …then the selector of `my_message` is composed such as:
271/// ```no_compile
272/// BLAKE2("my_message".to_string().as_bytes())[0..4]
273/// ```
274///
275/// ## Trait implementation block
276///
277/// Given
278///
279/// ```no_compile
280/// impl MyTrait for MyStorage {
281///     #[ink(message)]
282///     fn my_message(&self) {}
283/// }
284/// ```
285///
286/// …then the selector of `my_message` is composed such as:
287/// ```no_compile
288/// BLAKE2("MyTrait::my_message".to_string().as_bytes())[0..4]
289/// ```
290///
291/// ## Using full path for trait
292///
293/// Given
294///
295/// ```no_compile
296/// impl ::my_full::long_path::MyTrait for MyStorage {
297///     #[ink(message)]
298///     fn my_message(&self) {}
299/// }
300/// ```
301///
302/// …then the selector of `my_message` is composed such as:
303/// ```no_compile
304/// BLAKE2("::my_full::long_path::MyTrait::my_message".to_string().as_bytes())[0..4]
305/// ```
306///
307/// ## Using a namespace
308///
309/// Given
310///
311/// ```no_compile
312/// #[ink(namespace = "my_namespace")]
313/// impl MyTrait for MyStorage {
314///     #[ink(message)]
315///     fn my_message(&self) {}
316/// }
317/// ```
318///
319/// …then the selector of `my_message` is composed such as:
320/// ```no_compile
321/// BLAKE2("my_namespace::MyTrait::my_message".to_string().as_bytes())[0..4]
322/// ```
323///
324/// ## Note
325///
326/// All above examples work similarly for ink! constructors interchangeably.
327///
328/// ## Usage Recommendations
329///
330/// These recommendation mainly apply to trait implementation blocks:
331///
332/// - The recommendation by the ink! team is to use the full-path approach wherever
333///   possible; OR import the trait and use only its identifier with an additional
334///   namespace if required to disambiguate selectors.
335/// - Try not to intermix the above recommendations.
336/// - Avoid directly setting the selector of an ink! message or constructor. Only do this
337///   if nothing else helps and you need a very specific selector, e.g. in case of
338///   backwards compatibility.
339/// - Do not use the namespace unless required to disambiguate.
340pub fn compose_selector<C>(item_impl: &ir::ItemImpl, callable: &C) -> ir::Selector
341where
342    C: Callable,
343{
344    if let Some(selector) = callable.user_provided_selector() {
345        return *selector
346    }
347    let preimage = compose_selector_preimage(item_impl, callable);
348    ir::Selector::compute(&preimage)
349}
350
351fn compose_selector_preimage<C>(item_impl: &ir::ItemImpl, callable: &C) -> Vec<u8>
352where
353    C: Callable,
354{
355    let callable_name = callable.normalized_name().into_bytes();
356    let namespace_bytes = item_impl
357        .namespace()
358        .map(|namespace| namespace.as_bytes().to_vec())
359        .unwrap_or_default();
360    let separator = &b"::"[..];
361    match item_impl.trait_path() {
362        None => {
363            // Inherent implementation block:
364            if namespace_bytes.is_empty() {
365                callable_name
366            } else {
367                [namespace_bytes, callable_name].join(separator)
368            }
369        }
370        Some(path) => {
371            // Trait implementation block:
372            //
373            // We need to separate between full-path, e.g. `::my::full::Path`
374            // starting with `::` and relative paths for the composition.
375            let path_bytes = if path.leading_colon.is_some() {
376                let mut str_repr = path.to_token_stream().to_string();
377                str_repr.retain(|c| !c.is_whitespace());
378                str_repr.into_bytes()
379            } else {
380                path.segments
381                    .last()
382                    .expect("encountered empty trait path")
383                    .ident
384                    .to_string()
385                    .into_bytes()
386            };
387            if namespace_bytes.is_empty() {
388                [path_bytes, callable_name].join(separator)
389            } else {
390                [namespace_bytes, path_bytes, callable_name].join(separator)
391            }
392        }
393    }
394}
395
396/// Ensures that common invariants of externally callable ink! entities are met.
397///
398/// # Errors
399///
400/// In case any of the common externally callable invariants are not met:
401/// - This is `true` if the externally callable is:
402///  - generic
403///  - `const` (compile-time evaluable)
404///  - `async` (asynchronous WebAssembly smart contract calling is not allowed)
405///  - `unsafe` (caller provided assertions not yet stable)
406/// - Furthermore this is `true` if the externally callable is defined for a non default
407///   ABI (e.g. `extern "C"`) or does not have valid visibility.
408pub(super) fn ensure_callable_invariants(
409    method_item: &syn::ImplItemFn,
410    kind: CallableKind,
411) -> Result<(), syn::Error> {
412    let bad_visibility = match &method_item.vis {
413        syn::Visibility::Inherited => None,
414        syn::Visibility::Restricted(vis_restricted) => Some(vis_restricted.span()),
415        syn::Visibility::Public(_) => None,
416    };
417    if let Some(bad_visibility) = bad_visibility {
418        return Err(format_err!(
419            bad_visibility,
420            "ink! {}s must have public or inherited visibility",
421            kind
422        ))
423    }
424    if !method_item.sig.generics.params.is_empty() {
425        return Err(format_err_spanned!(
426            method_item.sig.generics.params,
427            "ink! {}s must not be generic",
428            kind,
429        ))
430    }
431    if method_item.sig.constness.is_some() {
432        return Err(format_err_spanned!(
433            method_item.sig.constness,
434            "ink! {}s must not be const",
435            kind,
436        ))
437    }
438    if method_item.sig.asyncness.is_some() {
439        return Err(format_err_spanned!(
440            method_item.sig.asyncness,
441            "ink! {}s must not be async",
442            kind,
443        ))
444    }
445    if method_item.sig.unsafety.is_some() {
446        return Err(format_err_spanned!(
447            method_item.sig.unsafety,
448            "ink! {}s must not be unsafe",
449            kind,
450        ))
451    }
452    if method_item.sig.abi.is_some() {
453        return Err(format_err_spanned!(
454            method_item.sig.abi,
455            "ink! {}s must not have explicit ABI",
456            kind,
457        ))
458    }
459    if method_item.sig.variadic.is_some() {
460        return Err(format_err_spanned!(
461            method_item.sig.variadic,
462            "ink! {}s must not be variadic",
463            kind,
464        ))
465    }
466
467    if let Some(arg) = method_item.sig.inputs.iter().find(|input| {
468        match input {
469            syn::FnArg::Typed(pat) => !matches!(*pat.pat, syn::Pat::Ident(_)),
470            _ => false,
471        }
472    }) {
473        return Err(format_err_spanned!(
474            arg,
475            "ink! {} arguments must have an identifier",
476            kind
477        ))
478    }
479    Ok(())
480}
481
482/// The visibility of an ink! message or constructor.
483#[derive(Debug, Clone)]
484pub enum Visibility {
485    Public(syn::Token![pub]),
486    Inherited,
487}
488
489impl quote::ToTokens for Visibility {
490    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
491        match self {
492            Self::Public(vis_public) => vis_public.to_tokens(tokens),
493            Self::Inherited => (),
494        }
495    }
496}
497
498impl Visibility {
499    /// Returns `true` if the visibility of the ink! message of constructor is public
500    /// (`pub`).
501    ///
502    /// # Note
503    ///
504    /// Messages in normal implementation blocks must have public visibility.
505    pub fn is_pub(&self) -> bool {
506        matches!(self, Self::Public(_))
507    }
508
509    /// Returns `true` if the visibility of the ink! message of constructor is inherited.
510    ///
511    /// # Note
512    ///
513    /// Messages in trait implementation blocks must have inherited visibility.
514    pub fn is_inherited(&self) -> bool {
515        matches!(self, Self::Inherited)
516    }
517
518    /// Returns the associated span if any.
519    pub fn span(&self) -> Option<Span> {
520        match self {
521            Self::Public(vis_public) => Some(vis_public.span()),
522            Self::Inherited => None,
523        }
524    }
525}
526
527/// Iterator over the input parameters of an ink! message or constructor.
528///
529/// Does not yield the self receiver of ink! messages.
530pub struct InputsIter<'a> {
531    iter: syn::punctuated::Iter<'a, syn::FnArg>,
532}
533
534impl<'a> InputsIter<'a> {
535    /// Creates a new inputs iterator over the given `syn` punctuation.
536    pub(crate) fn new<P>(inputs: &'a syn::punctuated::Punctuated<syn::FnArg, P>) -> Self {
537        Self {
538            iter: inputs.iter(),
539        }
540    }
541}
542
543impl<'a> From<&'a ir::Message> for InputsIter<'a> {
544    fn from(message: &'a ir::Message) -> Self {
545        Self::new(&message.item.sig.inputs)
546    }
547}
548
549impl<'a> From<&'a ir::Constructor> for InputsIter<'a> {
550    fn from(constructor: &'a ir::Constructor) -> Self {
551        Self::new(&constructor.item.sig.inputs)
552    }
553}
554
555impl<'a> Iterator for InputsIter<'a> {
556    type Item = &'a syn::PatType;
557
558    fn next(&mut self) -> Option<Self::Item> {
559        'repeat: loop {
560            match self.iter.next() {
561                None => return None,
562                Some(syn::FnArg::Typed(pat_typed)) => return Some(pat_typed),
563                Some(syn::FnArg::Receiver(_)) => continue 'repeat,
564            }
565        }
566    }
567}
568
569impl ExactSizeIterator for InputsIter<'_> {
570    fn len(&self) -> usize {
571        self.iter.len()
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578    use core::fmt::Debug;
579
580    pub enum ExpectedSelector {
581        Raw([u8; 4]),
582        Blake2(Vec<u8>),
583    }
584
585    impl From<[u8; 4]> for ExpectedSelector {
586        fn from(raw_selector: [u8; 4]) -> Self {
587            ExpectedSelector::Raw(raw_selector)
588        }
589    }
590
591    impl From<Vec<u8>> for ExpectedSelector {
592        fn from(blake2_input: Vec<u8>) -> Self {
593            ExpectedSelector::Blake2(blake2_input)
594        }
595    }
596
597    impl ExpectedSelector {
598        pub fn expected_selector(self) -> ir::Selector {
599            match self {
600                Self::Raw(raw_selector) => ir::Selector::from(raw_selector),
601                Self::Blake2(blake2_input) => ir::Selector::compute(&blake2_input),
602            }
603        }
604    }
605
606    /// Asserts that the given ink! implementation block and the given ink!
607    /// message result in the same composed selector as the expected bytes.
608    fn assert_compose_selector<C, S>(
609        item_impl: syn::ItemImpl,
610        item_method: syn::ImplItemFn,
611        expected_selector: S,
612    ) where
613        C: Callable + TryFrom<syn::ImplItemFn>,
614        <C as TryFrom<syn::ImplItemFn>>::Error: Debug,
615        S: Into<ExpectedSelector>,
616    {
617        assert_eq!(
618            compose_selector(
619                &<ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(item_impl).unwrap(),
620                &<C as TryFrom<syn::ImplItemFn>>::try_from(item_method).unwrap(),
621            ),
622            expected_selector.into().expected_selector(),
623        )
624    }
625
626    #[test]
627    fn inherent_selector_works() {
628        assert_compose_selector::<ir::Message, _>(
629            syn::parse_quote! {
630                #[ink(impl)]
631                impl MyStorage {}
632            },
633            syn::parse_quote! {
634                #[ink(message)]
635                fn my_message(&self) {}
636            },
637            b"my_message".to_vec(),
638        );
639    }
640
641    #[test]
642    fn trait_selector_works() {
643        assert_compose_selector::<ir::Message, _>(
644            syn::parse_quote! {
645                #[ink(impl)]
646                impl MyTrait for MyStorage {}
647            },
648            syn::parse_quote! {
649                #[ink(message)]
650                fn my_message(&self) {}
651            },
652            b"MyTrait::my_message".to_vec(),
653        );
654        assert_compose_selector::<ir::Message, _>(
655            syn::parse_quote! {
656                #[ink(impl)]
657                impl ::my::full::path::MyTrait for MyStorage {}
658            },
659            syn::parse_quote! {
660                #[ink(message)]
661                fn my_message(&self) {}
662            },
663            b"::my::full::path::MyTrait::my_message".to_vec(),
664        );
665        assert_compose_selector::<ir::Message, _>(
666            syn::parse_quote! {
667                #[ink(impl)]
668                impl relative::path_to::MyTrait for MyStorage {}
669            },
670            syn::parse_quote! {
671                #[ink(message)]
672                fn my_message(&self) {}
673            },
674            b"MyTrait::my_message".to_vec(),
675        );
676    }
677
678    #[test]
679    fn namespaced_selector_works() {
680        assert_compose_selector::<ir::Message, _>(
681            syn::parse_quote! {
682                #[ink(impl, namespace = "my_namespace")]
683                impl MyStorage {}
684            },
685            syn::parse_quote! {
686                #[ink(message)]
687                fn my_message(&self) {}
688            },
689            b"my_namespace::my_message".to_vec(),
690        );
691    }
692
693    #[test]
694    fn custom_selector_works() {
695        assert_compose_selector::<ir::Message, _>(
696            syn::parse_quote! {
697                #[ink(impl)]
698                impl MyStorage {}
699            },
700            syn::parse_quote! {
701                #[ink(message, selector = 0xDEADBEEF)]
702                fn my_message(&self) {}
703            },
704            [0xDE, 0xAD, 0xBE, 0xEF],
705        );
706        assert_compose_selector::<ir::Message, _>(
707            syn::parse_quote! {
708                #[ink(impl)]
709                impl MyTrait for MyStorage {}
710            },
711            syn::parse_quote! {
712                #[ink(message, selector = 0xDEADBEEF)]
713                fn my_message(&self) {}
714            },
715            [0xDE, 0xAD, 0xBE, 0xEF],
716        );
717    }
718
719    #[test]
720    fn name_override_selector_works() {
721        assert_compose_selector::<ir::Message, _>(
722            syn::parse_quote! {
723                #[ink(impl)]
724                impl MyStorage {}
725            },
726            syn::parse_quote! {
727                #[ink(message, name = "myMessage")]
728                fn my_message(&self) {}
729            },
730            b"myMessage".to_vec(),
731        );
732    }
733
734    #[test]
735    fn custom_selector_precedence_works() {
736        // Custom selector has higher precedence than name override.
737        assert_compose_selector::<ir::Message, _>(
738            syn::parse_quote! {
739                #[ink(impl)]
740                impl MyStorage {}
741            },
742            syn::parse_quote! {
743                #[ink(message, name = "myMessage", selector = 0xDEADBEEF)]
744                fn my_message(&self) {}
745            },
746            [0xDE, 0xAD, 0xBE, 0xEF],
747        );
748    }
749}