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
148impl<C> ::core::ops::Deref for CallableWithSelector<'_, C> {
149 type Target = C;
150
151 fn deref(&self) -> &Self::Target {
152 self.callable
153 }
154}
155
156pub trait Callable {
161 fn kind(&self) -> CallableKind;
163
164 fn ident(&self) -> &Ident;
166
167 fn user_provided_selector(&self) -> Option<&ir::Selector>;
169
170 fn is_payable(&self) -> bool;
176
177 fn is_default(&self) -> bool;
183
184 fn has_wildcard_selector(&self) -> bool;
186
187 fn has_wildcard_complement_selector(&self) -> bool;
189
190 fn visibility(&self) -> Visibility;
192
193 fn inputs(&self) -> InputsIter<'_>;
195
196 fn inputs_span(&self) -> Span;
198
199 fn statements(&self) -> &[syn::Stmt];
201
202 fn name(&self) -> Option<&str>;
204}
205
206pub fn compose_selector<C>(item_impl: &ir::ItemImpl, callable: &C) -> ir::Selector
328where
329 C: Callable,
330{
331 if let Some(selector) = callable.user_provided_selector() {
332 return *selector
333 }
334 let preimage = compose_selector_preimage(item_impl, callable);
335 ir::Selector::compute(&preimage)
336}
337
338fn compose_selector_preimage<C>(item_impl: &ir::ItemImpl, callable: &C) -> Vec<u8>
339where
340 C: Callable,
341{
342 let callable_name = callable
343 .name()
344 .map(|name| name.as_bytes().to_vec())
345 .unwrap_or_else(|| callable.ident().to_string().into_bytes());
346 let namespace_bytes = item_impl
347 .namespace()
348 .map(|namespace| namespace.as_bytes().to_vec())
349 .unwrap_or_default();
350 let separator = &b"::"[..];
351 match item_impl.trait_path() {
352 None => {
353 if namespace_bytes.is_empty() {
355 callable_name
356 } else {
357 [namespace_bytes, callable_name].join(separator)
358 }
359 }
360 Some(path) => {
361 let path_bytes = if path.leading_colon.is_some() {
366 let mut str_repr = path.to_token_stream().to_string();
367 str_repr.retain(|c| !c.is_whitespace());
368 str_repr.into_bytes()
369 } else {
370 path.segments
371 .last()
372 .expect("encountered empty trait path")
373 .ident
374 .to_string()
375 .into_bytes()
376 };
377 if namespace_bytes.is_empty() {
378 [path_bytes, callable_name].join(separator)
379 } else {
380 [namespace_bytes, path_bytes, callable_name].join(separator)
381 }
382 }
383 }
384}
385
386pub(super) fn ensure_callable_invariants(
399 method_item: &syn::ImplItemFn,
400 kind: CallableKind,
401) -> Result<(), syn::Error> {
402 let bad_visibility = match &method_item.vis {
403 syn::Visibility::Inherited => None,
404 syn::Visibility::Restricted(vis_restricted) => Some(vis_restricted.span()),
405 syn::Visibility::Public(_) => None,
406 };
407 if let Some(bad_visibility) = bad_visibility {
408 return Err(format_err!(
409 bad_visibility,
410 "ink! {}s must have public or inherited visibility",
411 kind
412 ))
413 }
414 if !method_item.sig.generics.params.is_empty() {
415 return Err(format_err_spanned!(
416 method_item.sig.generics.params,
417 "ink! {}s must not be generic",
418 kind,
419 ))
420 }
421 if method_item.sig.constness.is_some() {
422 return Err(format_err_spanned!(
423 method_item.sig.constness,
424 "ink! {}s must not be const",
425 kind,
426 ))
427 }
428 if method_item.sig.asyncness.is_some() {
429 return Err(format_err_spanned!(
430 method_item.sig.asyncness,
431 "ink! {}s must not be async",
432 kind,
433 ))
434 }
435 if method_item.sig.unsafety.is_some() {
436 return Err(format_err_spanned!(
437 method_item.sig.unsafety,
438 "ink! {}s must not be unsafe",
439 kind,
440 ))
441 }
442 if method_item.sig.abi.is_some() {
443 return Err(format_err_spanned!(
444 method_item.sig.abi,
445 "ink! {}s must not have explicit ABI",
446 kind,
447 ))
448 }
449 if method_item.sig.variadic.is_some() {
450 return Err(format_err_spanned!(
451 method_item.sig.variadic,
452 "ink! {}s must not be variadic",
453 kind,
454 ))
455 }
456
457 if let Some(arg) = method_item.sig.inputs.iter().find(|input| {
458 match input {
459 syn::FnArg::Typed(pat) => !matches!(*pat.pat, syn::Pat::Ident(_)),
460 _ => false,
461 }
462 }) {
463 return Err(format_err_spanned!(
464 arg,
465 "ink! {} arguments must have an identifier",
466 kind
467 ))
468 }
469 Ok(())
470}
471
472#[derive(Debug, Clone)]
474pub enum Visibility {
475 Public(syn::Token![pub]),
476 Inherited,
477}
478
479impl quote::ToTokens for Visibility {
480 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
481 match self {
482 Self::Public(vis_public) => vis_public.to_tokens(tokens),
483 Self::Inherited => (),
484 }
485 }
486}
487
488impl Visibility {
489 pub fn is_pub(&self) -> bool {
496 matches!(self, Self::Public(_))
497 }
498
499 pub fn is_inherited(&self) -> bool {
505 matches!(self, Self::Inherited)
506 }
507
508 pub fn span(&self) -> Option<Span> {
510 match self {
511 Self::Public(vis_public) => Some(vis_public.span()),
512 Self::Inherited => None,
513 }
514 }
515}
516
517pub struct InputsIter<'a> {
521 iter: syn::punctuated::Iter<'a, syn::FnArg>,
522}
523
524impl<'a> InputsIter<'a> {
525 pub(crate) fn new<P>(inputs: &'a syn::punctuated::Punctuated<syn::FnArg, P>) -> Self {
527 Self {
528 iter: inputs.iter(),
529 }
530 }
531}
532
533impl<'a> From<&'a ir::Message> for InputsIter<'a> {
534 fn from(message: &'a ir::Message) -> Self {
535 Self::new(&message.item.sig.inputs)
536 }
537}
538
539impl<'a> From<&'a ir::Constructor> for InputsIter<'a> {
540 fn from(constructor: &'a ir::Constructor) -> Self {
541 Self::new(&constructor.item.sig.inputs)
542 }
543}
544
545impl<'a> Iterator for InputsIter<'a> {
546 type Item = &'a syn::PatType;
547
548 fn next(&mut self) -> Option<Self::Item> {
549 'repeat: loop {
550 match self.iter.next() {
551 None => return None,
552 Some(syn::FnArg::Typed(pat_typed)) => return Some(pat_typed),
553 Some(syn::FnArg::Receiver(_)) => continue 'repeat,
554 }
555 }
556 }
557}
558
559impl ExactSizeIterator for InputsIter<'_> {
560 fn len(&self) -> usize {
561 self.iter.len()
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use core::fmt::Debug;
569
570 pub enum ExpectedSelector {
571 Raw([u8; 4]),
572 Blake2(Vec<u8>),
573 }
574
575 impl From<[u8; 4]> for ExpectedSelector {
576 fn from(raw_selector: [u8; 4]) -> Self {
577 ExpectedSelector::Raw(raw_selector)
578 }
579 }
580
581 impl From<Vec<u8>> for ExpectedSelector {
582 fn from(blake2_input: Vec<u8>) -> Self {
583 ExpectedSelector::Blake2(blake2_input)
584 }
585 }
586
587 impl ExpectedSelector {
588 pub fn expected_selector(self) -> ir::Selector {
589 match self {
590 Self::Raw(raw_selector) => ir::Selector::from(raw_selector),
591 Self::Blake2(blake2_input) => ir::Selector::compute(&blake2_input),
592 }
593 }
594 }
595
596 fn assert_compose_selector<C, S>(
599 item_impl: syn::ItemImpl,
600 item_method: syn::ImplItemFn,
601 expected_selector: S,
602 ) where
603 C: Callable + TryFrom<syn::ImplItemFn>,
604 <C as TryFrom<syn::ImplItemFn>>::Error: Debug,
605 S: Into<ExpectedSelector>,
606 {
607 assert_eq!(
608 compose_selector(
609 &<ir::ItemImpl as TryFrom<syn::ItemImpl>>::try_from(item_impl).unwrap(),
610 &<C as TryFrom<syn::ImplItemFn>>::try_from(item_method).unwrap(),
611 ),
612 expected_selector.into().expected_selector(),
613 )
614 }
615
616 #[test]
617 fn inherent_selector_works() {
618 assert_compose_selector::<ir::Message, _>(
619 syn::parse_quote! {
620 #[ink(impl)]
621 impl MyStorage {}
622 },
623 syn::parse_quote! {
624 #[ink(message)]
625 fn my_message(&self) {}
626 },
627 b"my_message".to_vec(),
628 );
629 }
630
631 #[test]
632 fn trait_selector_works() {
633 assert_compose_selector::<ir::Message, _>(
634 syn::parse_quote! {
635 #[ink(impl)]
636 impl MyTrait for MyStorage {}
637 },
638 syn::parse_quote! {
639 #[ink(message)]
640 fn my_message(&self) {}
641 },
642 b"MyTrait::my_message".to_vec(),
643 );
644 assert_compose_selector::<ir::Message, _>(
645 syn::parse_quote! {
646 #[ink(impl)]
647 impl ::my::full::path::MyTrait for MyStorage {}
648 },
649 syn::parse_quote! {
650 #[ink(message)]
651 fn my_message(&self) {}
652 },
653 b"::my::full::path::MyTrait::my_message".to_vec(),
654 );
655 assert_compose_selector::<ir::Message, _>(
656 syn::parse_quote! {
657 #[ink(impl)]
658 impl relative::path_to::MyTrait for MyStorage {}
659 },
660 syn::parse_quote! {
661 #[ink(message)]
662 fn my_message(&self) {}
663 },
664 b"MyTrait::my_message".to_vec(),
665 );
666 }
667
668 #[test]
669 fn namespaced_selector_works() {
670 assert_compose_selector::<ir::Message, _>(
671 syn::parse_quote! {
672 #[ink(impl, namespace = "my_namespace")]
673 impl MyStorage {}
674 },
675 syn::parse_quote! {
676 #[ink(message)]
677 fn my_message(&self) {}
678 },
679 b"my_namespace::my_message".to_vec(),
680 );
681 }
682
683 #[test]
684 fn custom_selector_works() {
685 assert_compose_selector::<ir::Message, _>(
686 syn::parse_quote! {
687 #[ink(impl)]
688 impl MyStorage {}
689 },
690 syn::parse_quote! {
691 #[ink(message, selector = 0xDEADBEEF)]
692 fn my_message(&self) {}
693 },
694 [0xDE, 0xAD, 0xBE, 0xEF],
695 );
696 assert_compose_selector::<ir::Message, _>(
697 syn::parse_quote! {
698 #[ink(impl)]
699 impl MyTrait for MyStorage {}
700 },
701 syn::parse_quote! {
702 #[ink(message, selector = 0xDEADBEEF)]
703 fn my_message(&self) {}
704 },
705 [0xDE, 0xAD, 0xBE, 0xEF],
706 );
707 }
708
709 #[test]
710 fn name_override_selector_works() {
711 assert_compose_selector::<ir::Message, _>(
712 syn::parse_quote! {
713 #[ink(impl)]
714 impl MyStorage {}
715 },
716 syn::parse_quote! {
717 #[ink(message, name = "myMessage")]
718 fn my_message(&self) {}
719 },
720 b"myMessage".to_vec(),
721 );
722 }
723
724 #[test]
725 fn custom_selector_precedence_works() {
726 assert_compose_selector::<ir::Message, _>(
728 syn::parse_quote! {
729 #[ink(impl)]
730 impl MyStorage {}
731 },
732 syn::parse_quote! {
733 #[ink(message, name = "myMessage", selector = 0xDEADBEEF)]
734 fn my_message(&self) {}
735 },
736 [0xDE, 0xAD, 0xBE, 0xEF],
737 );
738 }
739}