1use crate::ir;
19use core::fmt;
20use proc_macro2::{
21 Ident,
22 Span,
23};
24use quote::ToTokens as _;
25use syn::spanned::Spanned as _;
26
27#[derive(Debug, Copy, Clone)]
29pub enum CallableKind {
30 Message,
32 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#[derive(Debug)]
47pub struct CallableWithSelector<'a, C> {
48 composed_selector: ir::Selector,
51 item_impl: &'a ir::ItemImpl,
53 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 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 pub fn composed_selector(&self) -> ir::Selector {
81 self.composed_selector
82 }
83
84 pub fn callable(&self) -> &'a C {
86 self.callable
87 }
88
89 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
162pub trait Callable {
167 fn kind(&self) -> CallableKind;
169
170 fn ident(&self) -> &Ident;
172
173 fn user_provided_selector(&self) -> Option<&ir::Selector>;
175
176 fn is_payable(&self) -> bool;
182
183 fn is_default(&self) -> bool;
189
190 fn has_wildcard_selector(&self) -> bool;
192
193 fn has_wildcard_complement_selector(&self) -> bool;
195
196 fn visibility(&self) -> Visibility;
198
199 fn inputs(&self) -> InputsIter<'_>;
201
202 fn inputs_span(&self) -> Span;
204
205 fn statements(&self) -> &[syn::Stmt];
207
208 fn name(&self) -> Option<&str>;
210
211 fn normalized_name(&self) -> String;
217}
218
219pub 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 if namespace_bytes.is_empty() {
365 callable_name
366 } else {
367 [namespace_bytes, callable_name].join(separator)
368 }
369 }
370 Some(path) => {
371 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
396pub(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#[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 pub fn is_pub(&self) -> bool {
506 matches!(self, Self::Public(_))
507 }
508
509 pub fn is_inherited(&self) -> bool {
515 matches!(self, Self::Inherited)
516 }
517
518 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
527pub struct InputsIter<'a> {
531 iter: syn::punctuated::Iter<'a, syn::FnArg>,
532}
533
534impl<'a> InputsIter<'a> {
535 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 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 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}