-
Notifications
You must be signed in to change notification settings - Fork 480
Implement wildcard selector, add proxy example
#1020
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 37 commits
64901a9
2eaf655
bd7eea1
7b7c43b
4677a59
fbe82d2
2e633e3
23e2baf
75886af
0299f4d
1d102b0
7f1a7ea
7dba798
66a3455
f643a30
1b0c396
5cb1f3c
ee3d140
3ed386c
724ff9f
f4a2765
dc2a9fd
17067e7
a7e624a
f1429cf
0b7cf6e
cc078ea
c2a2697
99a4ff8
a2ba30a
9f58738
1a7b33c
00f360d
1bc3bb6
98da359
b498308
36a374c
d55c84b
fbc6097
7698ad5
c297f50
21bb558
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,8 +25,11 @@ use core::{ | |
| result::Result, | ||
| }; | ||
| use proc_macro2::{ | ||
| Group as Group2, | ||
| Ident, | ||
| Span, | ||
| TokenStream as TokenStream2, | ||
| TokenTree as TokenTree2, | ||
| }; | ||
| use std::collections::HashMap; | ||
| use syn::spanned::Spanned; | ||
|
|
@@ -265,7 +268,7 @@ impl InkAttribute { | |
| } | ||
|
|
||
| /// Returns the selector of the ink! attribute if any. | ||
cmichi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| pub fn selector(&self) -> Option<ir::Selector> { | ||
| pub fn selector(&self) -> Option<SelectorOrWildcard> { | ||
| self.args().find_map(|arg| { | ||
| if let ir::AttributeArg::Selector(selector) = arg.kind() { | ||
| return Some(*selector) | ||
|
|
@@ -280,6 +283,16 @@ impl InkAttribute { | |
| .any(|arg| matches!(arg.kind(), AttributeArg::Payable)) | ||
| } | ||
|
|
||
| /// Returns `true` if the ink! attribute contains the wildcard selector. | ||
| pub fn has_wildcard_selector(&self) -> bool { | ||
| self.args().any(|arg| { | ||
| matches!( | ||
| arg.kind(), | ||
| AttributeArg::Selector(SelectorOrWildcard::Wildcard) | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| /// Returns `true` if the ink! attribute contains the `anonymous` argument. | ||
| pub fn is_anonymous(&self) -> bool { | ||
| self.args() | ||
|
|
@@ -342,6 +355,7 @@ pub enum AttributeArgKind { | |
| Constructor, | ||
| /// `#[ink(payable)]` | ||
| Payable, | ||
| /// `#[ink(selector = _)]` | ||
| /// `#[ink(selector = 0xDEADBEEF)]` | ||
| Selector, | ||
| /// `#[ink(extension = N: u32)]` | ||
|
|
@@ -395,11 +409,15 @@ pub enum AttributeArg { | |
| /// Applied on ink! constructors or messages in order to specify that they | ||
| /// can receive funds from callers. | ||
| Payable, | ||
| /// `#[ink(selector = 0xDEADBEEF)]` | ||
| /// Can be either one of: | ||
| /// | ||
| /// Applied on ink! constructors or messages to manually control their | ||
| /// selectors. | ||
| Selector(Selector), | ||
| /// - `#[ink(selector = 0xDEADBEEF)]` | ||
| /// Applied on ink! constructors or messages to manually control their | ||
| /// selectors. | ||
| /// - `#[ink(selector = _)]` | ||
| /// Applied on ink! messages to define a fallback messages that is invoked | ||
| /// if no other ink! message matches a given selector. | ||
| Selector(SelectorOrWildcard), | ||
| /// `#[ink(namespace = "my_namespace")]` | ||
| /// | ||
| /// Applied on ink! trait implementation blocks to disambiguate other trait | ||
|
|
@@ -449,7 +467,7 @@ impl core::fmt::Display for AttributeArgKind { | |
| Self::Constructor => write!(f, "constructor"), | ||
| Self::Payable => write!(f, "payable"), | ||
| Self::Selector => { | ||
| write!(f, "selector = S:[u8; 4]") | ||
| write!(f, "selector = S:[u8; 4] || _") | ||
| } | ||
| Self::Extension => { | ||
| write!(f, "extension = N:u32)") | ||
|
|
@@ -495,9 +513,7 @@ impl core::fmt::Display for AttributeArg { | |
| Self::Message => write!(f, "message"), | ||
| Self::Constructor => write!(f, "constructor"), | ||
| Self::Payable => write!(f, "payable"), | ||
| Self::Selector(selector) => { | ||
| write!(f, "selector = {:?}", selector.to_bytes()) | ||
| } | ||
| Self::Selector(selector) => core::fmt::Display::fmt(&selector, f), | ||
| Self::Extension(extension) => { | ||
| write!(f, "extension = {:?}", extension.into_u32()) | ||
| } | ||
|
|
@@ -511,6 +527,32 @@ impl core::fmt::Display for AttributeArg { | |
| } | ||
| } | ||
|
|
||
| /// Either a wildcard selector or a specified selector. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
| pub enum SelectorOrWildcard { | ||
| /// A wildcard selector. If no other selector matches, the message/constructor | ||
| /// annotated with the wildcard selector will be invoked. | ||
| Wildcard, | ||
| /// A user provided selector. | ||
| UserProvided(ir::Selector), | ||
| } | ||
|
|
||
| impl SelectorOrWildcard { | ||
| /// Create a new `SelectorOrWildcard::Selector` from the supplied bytes. | ||
| fn selector(bytes: [u8; 4]) -> SelectorOrWildcard { | ||
| SelectorOrWildcard::UserProvided(Selector::from(bytes)) | ||
| } | ||
| } | ||
|
|
||
| impl core::fmt::Display for SelectorOrWildcard { | ||
| fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { | ||
| match self { | ||
| Self::UserProvided(selector) => core::fmt::Debug::fmt(&selector, f), | ||
| Self::Wildcard => write!(f, "_"), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// An ink! namespace applicable to a trait implementation block. | ||
| #[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
| pub struct Namespace { | ||
|
|
@@ -723,13 +765,66 @@ impl From<InkAttribute> for Attribute { | |
| } | ||
| } | ||
|
|
||
| /// This function replaces occurrences of a `TokenTree::Ident` of the sequence | ||
| /// `selector = _` with the sequence `selector = "_"`. | ||
| /// | ||
| /// This is done because `syn::Attribute::parse_meta` does not support parsing a | ||
| /// verbatim like `_`. For this we would need to switch to `syn::Attribute::parse_args`, | ||
| /// which requires a more in-depth rewrite of our IR parsing. | ||
| fn transform_wildcard_selector_to_string(group: Group2) -> TokenTree2 { | ||
| let mut found_selector = false; | ||
| let mut found_equal = false; | ||
|
|
||
| let new_group: TokenStream2 = group | ||
| .stream() | ||
| .into_iter() | ||
| .map(|tt| { | ||
| match tt { | ||
| TokenTree2::Group(grp) => transform_wildcard_selector_to_string(grp), | ||
| TokenTree2::Ident(ident) | ||
| if found_selector && found_equal && ident == "_" => | ||
| { | ||
| let mut lit = proc_macro2::Literal::string("_"); | ||
| lit.set_span(ident.span()); | ||
| found_selector = false; | ||
| found_equal = false; | ||
| TokenTree2::Literal(lit) | ||
| } | ||
| TokenTree2::Ident(ident) if ident == "selector" => { | ||
| found_selector = true; | ||
| TokenTree2::Ident(ident) | ||
| } | ||
| TokenTree2::Punct(punct) if punct.as_char() == '=' => { | ||
| found_equal = true; | ||
| TokenTree2::Punct(punct) | ||
| } | ||
| _ => tt, | ||
| } | ||
| }) | ||
| .collect(); | ||
| TokenTree2::Group(Group2::new(group.delimiter(), new_group)) | ||
| } | ||
|
|
||
| impl TryFrom<syn::Attribute> for InkAttribute { | ||
| type Error = syn::Error; | ||
|
|
||
| fn try_from(attr: syn::Attribute) -> Result<Self, Self::Error> { | ||
| fn try_from(mut attr: syn::Attribute) -> Result<Self, Self::Error> { | ||
| if !attr.path.is_ident("ink") { | ||
| return Err(format_err_spanned!(attr, "unexpected non-ink! attribute")) | ||
| } | ||
|
|
||
| let ts: TokenStream2 = attr | ||
| .tokens | ||
| .into_iter() | ||
| .map(|tt| { | ||
| match tt { | ||
| TokenTree2::Group(grp) => transform_wildcard_selector_to_string(grp), | ||
| _ => tt, | ||
| } | ||
| }) | ||
| .collect(); | ||
|
Comment on lines
+816
to
+825
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very fragile because it changes syntax without looking at the semantics. #[compose(Bar, _)]
#[precondition x == Some(_)]Since with this implementation we accept
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it's fragile, the case you mentioned is not possible though, since the impl of |
||
| attr.tokens = ts; | ||
|
|
||
| match attr.parse_meta().map_err(|_| { | ||
| format_err_spanned!(attr, "unexpected ink! attribute structure") | ||
| })? { | ||
|
|
@@ -809,6 +904,23 @@ impl TryFrom<syn::NestedMeta> for AttributeFrag { | |
| match &meta { | ||
| syn::Meta::NameValue(name_value) => { | ||
| if name_value.path.is_ident("selector") { | ||
| if let syn::Lit::Str(lit_str) = &name_value.lit { | ||
| let argument = lit_str.value(); | ||
| // We've pre-processed the verbatim `_` to `"_"`. This was done | ||
| // because `syn::Attribute::parse_meta` does not support verbatim. | ||
| if argument != "_" { | ||
| return Err(format_err!( | ||
| name_value, | ||
| "#[ink(selector = ..)] attributes with string inputs are deprecated. \ | ||
| use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]." | ||
| )) | ||
| } | ||
| return Ok(AttributeFrag { | ||
| ast: meta, | ||
| arg: AttributeArg::Selector(SelectorOrWildcard::Wildcard), | ||
| }) | ||
| } | ||
|
Comment on lines
+907
to
+922
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd appreciate if we create a follow-up issue that fixes this work around with a proper implementation that customizes the argument parsing. |
||
|
|
||
| if let syn::Lit::Int(lit_int) = &name_value.lit { | ||
| let selector_u32 = lit_int.base10_parse::<u32>() | ||
| .map_err(|error| { | ||
|
|
@@ -821,16 +933,9 @@ impl TryFrom<syn::NestedMeta> for AttributeFrag { | |
| let selector = Selector::from(selector_u32.to_be_bytes()); | ||
| return Ok(AttributeFrag { | ||
| ast: meta, | ||
| arg: AttributeArg::Selector(selector), | ||
| arg: AttributeArg::Selector(SelectorOrWildcard::UserProvided(selector)), | ||
| }) | ||
| } | ||
| if let syn::Lit::Str(_) = &name_value.lit { | ||
| return Err(format_err!( | ||
| name_value, | ||
| "#[ink(selector = ..)] attributes with string inputs are deprecated. \ | ||
| use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]." | ||
| )) | ||
| } | ||
| return Err(format_err!(name_value, "expecteded 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE]")) | ||
| } | ||
| if name_value.path.is_ident("namespace") { | ||
|
|
@@ -1116,15 +1221,15 @@ mod tests { | |
| #[ink(selector = 42)] | ||
| }, | ||
| Ok(test::Attribute::Ink(vec![AttributeArg::Selector( | ||
| Selector::from([0, 0, 0, 42]), | ||
| SelectorOrWildcard::UserProvided(Selector::from([0, 0, 0, 42])), | ||
| )])), | ||
| ); | ||
| assert_attribute_try_from( | ||
| syn::parse_quote! { | ||
| #[ink(selector = 0xDEADBEEF)] | ||
| }, | ||
| Ok(test::Attribute::Ink(vec![AttributeArg::Selector( | ||
| Selector::from([0xDE, 0xAD, 0xBE, 0xEF]), | ||
| SelectorOrWildcard::selector([0xDE, 0xAD, 0xBE, 0xEF]), | ||
| )])), | ||
| ); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.