difftreelog
refactor(evm-coder) toposort generated code
in: master
Previously, all items from evm-coder codegen was sorted lexically, which caused some problems, where lexically sorted names of contract were not in the same order as contract inherit chain Now they are sorted by insertion order, and macro-generated code now inserts them in order of definition, thus providing topological sort of output contract items
3 files changed
crates/evm-coder-macros/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/lib.rs
+++ b/crates/evm-coder-macros/src/lib.rs
@@ -22,7 +22,7 @@
use sha3::{Digest, Keccak256};
use syn::{
DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments,
- PathSegment, Type, parse_macro_input, spanned::Spanned, Attribute, parse::Parse,
+ PathSegment, Type, parse_macro_input, spanned::Spanned,
};
mod solidity_interface;
@@ -241,7 +241,7 @@
/// Event(#[indexed] uint32),
/// }
///
-/// #[solidity_interface(name = "MyContract", is(SuperContract), inline_is(InlineContract))]
+/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
/// impl Contract {
/// /// Multiply two numbers
/// #[weight(200 + a + b)]
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![allow(dead_code)]1819use quote::{quote, ToTokens};20use inflector::cases;21use std::fmt::Write;22use syn::{23 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,24 MetaNameValue, PatType, PathArguments, ReturnType, Type,25 spanned::Spanned,26 parse::{Parse, ParseStream},27 parenthesized, Token, LitInt, LitStr,28};2930use crate::{31 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,32 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,33 snake_ident_to_screaming,34};3536struct Is {37 name: Ident,38 pascal_call_name: Ident,39 snake_call_name: Ident,40 via: Option<(Type, Ident)>,41}42impl Is {43 fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {44 let name = &self.name;45 let pascal_call_name = &self.pascal_call_name;46 quote! {47 #name(#pascal_call_name #gen_ref)48 }49 }5051 fn expand_interface_id(&self) -> proc_macro2::TokenStream {52 let pascal_call_name = &self.pascal_call_name;53 quote! {54 interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());55 }56 }5758 fn expand_supports_interface(59 &self,60 generics: &proc_macro2::TokenStream,61 ) -> proc_macro2::TokenStream {62 let pascal_call_name = &self.pascal_call_name;63 quote! {64 <#pascal_call_name #generics>::supports_interface(interface_id)65 }66 }6768 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {69 let name = &self.name;70 quote! {71 Self::#name(call) => call.weight()72 }73 }7475 fn expand_variant_call(76 &self,77 call_name: &proc_macro2::Ident,78 generics: &proc_macro2::TokenStream,79 ) -> proc_macro2::TokenStream {80 let name = &self.name;81 let pascal_call_name = &self.pascal_call_name;82 let via_typ = self83 .via84 .as_ref()85 .map(|(t, _)| quote! {#t})86 .unwrap_or_else(|| quote! {Self});87 let via_map = self88 .via89 .as_ref()90 .map(|(_, i)| quote! {.#i()})91 .unwrap_or_default();92 quote! {93 #call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {94 call,95 caller: c.caller,96 value: c.value,97 })98 }99 }100101 fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {102 let name = &self.name;103 let pascal_call_name = &self.pascal_call_name;104 quote! {105 if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {106 return Ok(Some(Self::#name(parsed_call)))107 }108 }109 }110111 fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {112 let pascal_call_name = &self.pascal_call_name;113 quote! {114 <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);115 }116 }117118 fn expand_event_generator(&self) -> proc_macro2::TokenStream {119 let name = &self.name;120 quote! {121 #name::generate_solidity_interface(tc, is_impl);122 }123 }124}125126#[derive(Default)]127struct IsList(Vec<Is>);128impl Parse for IsList {129 fn parse(input: ParseStream) -> syn::Result<Self> {130 let mut out = vec![];131 loop {132 if input.is_empty() {133 break;134 }135 let name = input.parse::<Ident>()?;136 let lookahead = input.lookahead1();137 let via = if lookahead.peek(syn::token::Paren) {138 let contents;139 parenthesized!(contents in input);140 let method = contents.parse::<Ident>()?;141 contents.parse::<Token![,]>()?;142 let ty = contents.parse::<Type>()?;143 Some((ty, method))144 } else if lookahead.peek(Token![,]) {145 None146 } else if input.is_empty() {147 None148 } else {149 return Err(lookahead.error());150 };151 out.push(Is {152 pascal_call_name: pascal_ident_to_call(&name),153 snake_call_name: pascal_ident_to_snake_call(&name),154 name,155 via,156 });157 if input.peek(Token![,]) {158 input.parse::<Token![,]>()?;159 continue;160 } else {161 break;162 }163 }164 Ok(Self(out))165 }166}167168pub struct InterfaceInfo {169 name: Ident,170 is: IsList,171 inline_is: IsList,172 events: IsList,173 expect_selector: Option<u32>,174}175impl Parse for InterfaceInfo {176 fn parse(input: ParseStream) -> syn::Result<Self> {177 let mut name = None;178 let mut is = None;179 let mut inline_is = None;180 let mut events = None;181 let mut expect_selector = None;182 // TODO: create proc-macro to optimize proc-macro boilerplate? :D183 loop {184 let lookahead = input.lookahead1();185 if lookahead.peek(kw::name) {186 let k = input.parse::<kw::name>()?;187 input.parse::<Token![=]>()?;188 if name.replace(input.parse::<Ident>()?).is_some() {189 return Err(syn::Error::new(k.span(), "name is already set"));190 }191 } else if lookahead.peek(kw::is) {192 let k = input.parse::<kw::is>()?;193 let contents;194 parenthesized!(contents in input);195 if is.replace(contents.parse::<IsList>()?).is_some() {196 return Err(syn::Error::new(k.span(), "is is already set"));197 }198 } else if lookahead.peek(kw::inline_is) {199 let k = input.parse::<kw::inline_is>()?;200 let contents;201 parenthesized!(contents in input);202 if inline_is.replace(contents.parse::<IsList>()?).is_some() {203 return Err(syn::Error::new(k.span(), "inline_is is already set"));204 }205 } else if lookahead.peek(kw::events) {206 let k = input.parse::<kw::events>()?;207 let contents;208 parenthesized!(contents in input);209 if events.replace(contents.parse::<IsList>()?).is_some() {210 return Err(syn::Error::new(k.span(), "events is already set"));211 }212 } else if lookahead.peek(kw::expect_selector) {213 let k = input.parse::<kw::expect_selector>()?;214 input.parse::<Token![=]>()?;215 let value = input.parse::<LitInt>()?;216 if expect_selector217 .replace(value.base10_parse::<u32>()?)218 .is_some()219 {220 return Err(syn::Error::new(k.span(), "expect_selector is already set"));221 }222 } else if input.is_empty() {223 break;224 } else {225 return Err(lookahead.error());226 }227 if input.peek(Token![,]) {228 input.parse::<Token![,]>()?;229 } else {230 break;231 }232 }233 Ok(Self {234 name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,235 is: is.unwrap_or_default(),236 inline_is: inline_is.unwrap_or_default(),237 events: events.unwrap_or_default(),238 expect_selector,239 })240 }241}242243struct MethodInfo {244 rename_selector: Option<String>,245}246impl Parse for MethodInfo {247 fn parse(input: ParseStream) -> syn::Result<Self> {248 let mut rename_selector = None;249 let lookahead = input.lookahead1();250 if lookahead.peek(kw::rename_selector) {251 let k = input.parse::<kw::rename_selector>()?;252 input.parse::<Token![=]>()?;253 if rename_selector254 .replace(input.parse::<LitStr>()?.value())255 .is_some()256 {257 return Err(syn::Error::new(k.span(), "rename_selector is already set"));258 }259 }260 Ok(Self { rename_selector })261 }262}263264enum AbiType {265 // type266 Plain(Ident),267 // (type1,type2)268 Tuple(Vec<AbiType>),269 // type[]270 Vec(Box<AbiType>),271 // type[20]272 Array(Box<AbiType>, usize),273}274impl AbiType {275 fn try_from(value: &Type) -> syn::Result<Self> {276 let value = Self::try_maybe_special_from(value)?;277 if value.is_special() {278 return Err(syn::Error::new(value.span(), "unexpected special type"));279 }280 Ok(value)281 }282 fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {283 match value {284 Type::Array(arr) => {285 let wrapped = AbiType::try_from(&arr.elem)?;286 match &arr.len {287 Expr::Lit(l) => match &l.lit {288 Lit::Int(i) => {289 let num = i.base10_parse::<usize>()?;290 Ok(AbiType::Array(Box::new(wrapped), num as usize))291 }292 _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),293 },294 _ => Err(syn::Error::new(arr.len.span(), "should be literal")),295 }296 }297 Type::Path(_) => {298 let path = parse_path(value)?;299 let segment = parse_path_segment(path)?;300 if segment.ident == "Vec" {301 let args = match &segment.arguments {302 PathArguments::AngleBracketed(e) => e,303 _ => {304 return Err(syn::Error::new(305 segment.arguments.span(),306 "missing Vec generic",307 ))308 }309 };310 let args = &args.args;311 if args.len() != 1 {312 return Err(syn::Error::new(313 args.span(),314 "expected only one generic for vec",315 ));316 }317 let arg = args.first().expect("first arg");318319 let ty = match arg {320 GenericArgument::Type(ty) => ty,321 _ => {322 return Err(syn::Error::new(323 arg.span(),324 "expected first generic to be type",325 ))326 }327 };328329 let wrapped = AbiType::try_from(ty)?;330 Ok(Self::Vec(Box::new(wrapped)))331 } else {332 if !segment.arguments.is_empty() {333 return Err(syn::Error::new(334 segment.arguments.span(),335 "unexpected generic arguments for non-vec type",336 ));337 }338 Ok(Self::Plain(segment.ident.clone()))339 }340 }341 Type::Tuple(t) => {342 let mut out = Vec::with_capacity(t.elems.len());343 for el in t.elems.iter() {344 out.push(AbiType::try_from(el)?)345 }346 Ok(Self::Tuple(out))347 }348 _ => Err(syn::Error::new(349 value.span(),350 "unexpected type, only arrays, plain types and tuples are supported",351 )),352 }353 }354 fn is_value(&self) -> bool {355 matches!(self, Self::Plain(v) if v == "value")356 }357 fn is_caller(&self) -> bool {358 matches!(self, Self::Plain(v) if v == "caller")359 }360 fn is_special(&self) -> bool {361 self.is_caller() || self.is_value()362 }363 fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {364 match self {365 AbiType::Plain(t) => {366 write!(buf, "{}", t)367 }368 AbiType::Tuple(t) => {369 write!(buf, "(")?;370 for (i, t) in t.iter().enumerate() {371 if i != 0 {372 write!(buf, ",")?;373 }374 t.selector_ty_buf(buf)?;375 }376 write!(buf, ")")377 }378 AbiType::Vec(v) => {379 v.selector_ty_buf(buf)?;380 write!(buf, "[]")381 }382 AbiType::Array(v, len) => {383 v.selector_ty_buf(buf)?;384 write!(buf, "[{}]", len)385 }386 }387 }388 fn selector_ty(&self) -> String {389 let mut out = String::new();390 self.selector_ty_buf(&mut out).expect("no fmt error");391 out392 }393}394impl ToTokens for AbiType {395 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {396 match self {397 AbiType::Plain(t) => tokens.extend(quote! {#t}),398 AbiType::Tuple(t) => {399 tokens.extend(quote! {(400 #(#t),*401 )});402 }403 AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),404 AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),405 }406 }407}408409struct MethodArg {410 name: Ident,411 camel_name: String,412 ty: AbiType,413}414impl MethodArg {415 fn try_from(value: &PatType) -> syn::Result<Self> {416 let name = parse_ident_from_pat(&value.pat)?.clone();417 Ok(Self {418 camel_name: cases::camelcase::to_camel_case(&name.to_string()),419 name,420 ty: AbiType::try_maybe_special_from(&value.ty)?,421 })422 }423 fn is_value(&self) -> bool {424 self.ty.is_value()425 }426 fn is_caller(&self) -> bool {427 self.ty.is_caller()428 }429 fn is_special(&self) -> bool {430 self.ty.is_special()431 }432 fn selector_ty(&self) -> String {433 assert!(!self.is_special());434 self.ty.selector_ty()435 }436437 fn expand_call_def(&self) -> proc_macro2::TokenStream {438 assert!(!self.is_special());439 let name = &self.name;440 let ty = &self.ty;441442 quote! {443 #name: #ty444 }445 }446447 fn expand_parse(&self) -> proc_macro2::TokenStream {448 assert!(!self.is_special());449 let name = &self.name;450 quote! {451 #name: reader.abi_read()?452 }453 }454455 fn expand_call_arg(&self) -> proc_macro2::TokenStream {456 if self.is_value() {457 quote! {458 c.value.clone()459 }460 } else if self.is_caller() {461 quote! {462 c.caller.clone()463 }464 } else {465 let name = &self.name;466 quote! {467 #name468 }469 }470 }471472 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {473 let camel_name = &self.camel_name.to_string();474 let ty = &self.ty;475 quote! {476 <NamedArgument<#ty>>::new(#camel_name)477 }478 }479}480481#[derive(PartialEq)]482enum Mutability {483 Mutable,484 View,485 Pure,486}487488mod kw {489 syn::custom_keyword!(weight);490491 syn::custom_keyword!(via);492 syn::custom_keyword!(name);493 syn::custom_keyword!(is);494 syn::custom_keyword!(inline_is);495 syn::custom_keyword!(events);496 syn::custom_keyword!(expect_selector);497498 syn::custom_keyword!(rename_selector);499}500501struct Method {502 name: Ident,503 camel_name: String,504 pascal_name: Ident,505 screaming_name: Ident,506 selector_str: String,507 selector: u32,508 args: Vec<MethodArg>,509 has_normal_args: bool,510 mutability: Mutability,511 result: Type,512 weight: Option<Expr>,513 docs: Vec<String>,514}515impl Method {516 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {517 let mut info = MethodInfo {518 rename_selector: None,519 };520 let mut docs = Vec::new();521 let mut weight = None;522 for attr in &value.attrs {523 let ident = parse_ident_from_path(&attr.path, false)?;524 if ident == "solidity" {525 info = attr.parse_args::<MethodInfo>()?;526 } else if ident == "doc" {527 let args = attr.parse_meta().unwrap();528 let value = match args {529 Meta::NameValue(MetaNameValue {530 lit: Lit::Str(str), ..531 }) => str.value(),532 _ => unreachable!(),533 };534 docs.push(value);535 } else if ident == "weight" {536 weight = Some(attr.parse_args::<Expr>()?);537 }538 }539 let ident = &value.sig.ident;540 let ident_str = ident.to_string();541 if !cases::snakecase::is_snake_case(&ident_str) {542 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));543 }544545 let mut mutability = Mutability::Pure;546547 if let Some(FnArg::Receiver(receiver)) = value548 .sig549 .inputs550 .iter()551 .find(|arg| matches!(arg, FnArg::Receiver(_)))552 {553 if receiver.reference.is_none() {554 return Err(syn::Error::new(555 receiver.span(),556 "receiver should be by ref",557 ));558 }559 if receiver.mutability.is_some() {560 mutability = Mutability::Mutable;561 } else {562 mutability = Mutability::View;563 }564 }565 let mut args = Vec::new();566 for typ in value567 .sig568 .inputs569 .iter()570 .filter(|arg| matches!(arg, FnArg::Typed(_)))571 {572 let typ = match typ {573 FnArg::Typed(typ) => typ,574 _ => unreachable!(),575 };576 args.push(MethodArg::try_from(typ)?);577 }578579 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {580 return Err(syn::Error::new(581 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),582 "payable function should be mutable",583 ));584 }585586 let result = match &value.sig.output {587 ReturnType::Type(_, ty) => ty,588 _ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),589 };590 let result = parse_result_ok(result)?;591592 let camel_name = info593 .rename_selector594 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));595 let mut selector_str = camel_name.clone();596 selector_str.push('(');597 let mut has_normal_args = false;598 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {599 if i != 0 {600 selector_str.push(',');601 }602 write!(selector_str, "{}", arg.selector_ty()).unwrap();603 has_normal_args = true;604 }605 selector_str.push(')');606 let selector = fn_selector_str(&selector_str);607608 Ok(Self {609 name: ident.clone(),610 camel_name,611 pascal_name: snake_ident_to_pascal(ident),612 screaming_name: snake_ident_to_screaming(ident),613 selector_str,614 selector,615 args,616 has_normal_args,617 mutability,618 result: result.clone(),619 weight,620 docs,621 })622 }623 fn expand_call_def(&self) -> proc_macro2::TokenStream {624 let defs = self625 .args626 .iter()627 .filter(|a| !a.is_special())628 .map(|a| a.expand_call_def());629 let pascal_name = &self.pascal_name;630631 if self.has_normal_args {632 quote! {633 #pascal_name {634 #(635 #defs,636 )*637 }638 }639 } else {640 quote! {#pascal_name}641 }642 }643644 fn expand_const(&self) -> proc_macro2::TokenStream {645 let screaming_name = &self.screaming_name;646 let selector = u32::to_be_bytes(self.selector);647 let selector_str = &self.selector_str;648 quote! {649 #[doc = #selector_str]650 const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];651 }652 }653654 fn expand_interface_id(&self) -> proc_macro2::TokenStream {655 let screaming_name = &self.screaming_name;656 quote! {657 interface_id ^= u32::from_be_bytes(Self::#screaming_name);658 }659 }660661 fn expand_parse(&self) -> proc_macro2::TokenStream {662 let pascal_name = &self.pascal_name;663 let screaming_name = &self.screaming_name;664 if self.has_normal_args {665 let parsers = self666 .args667 .iter()668 .filter(|a| !a.is_special())669 .map(|a| a.expand_parse());670 quote! {671 Self::#screaming_name => return Ok(Some(Self::#pascal_name {672 #(673 #parsers,674 )*675 }))676 }677 } else {678 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }679 }680 }681682 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {683 let pascal_name = &self.pascal_name;684 let name = &self.name;685686 let matcher = if self.has_normal_args {687 let names = self688 .args689 .iter()690 .filter(|a| !a.is_special())691 .map(|a| &a.name);692693 quote! {{694 #(695 #names,696 )*697 }}698 } else {699 quote! {}700 };701702 let receiver = match self.mutability {703 Mutability::Mutable | Mutability::View => quote! {self.},704 Mutability::Pure => quote! {Self::},705 };706 let args = self.args.iter().map(|a| a.expand_call_arg());707708 quote! {709 #call_name::#pascal_name #matcher => {710 let result = #receiver #name(711 #(712 #args,713 )*714 )?;715 (&result).to_result()716 }717 }718 }719720 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {721 let pascal_name = &self.pascal_name;722 if let Some(weight) = &self.weight {723 let matcher = if self.has_normal_args {724 let names = self725 .args726 .iter()727 .filter(|a| !a.is_special())728 .map(|a| &a.name);729730 quote! {{731 #(732 #names,733 )*734 }}735 } else {736 quote! {}737 };738 quote! {739 Self::#pascal_name #matcher => (#weight).into()740 }741 } else {742 let matcher = if self.has_normal_args {743 quote! {{..}}744 } else {745 quote! {}746 };747 quote! {748 Self::#pascal_name #matcher => ().into()749 }750 }751 }752753 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {754 let camel_name = &self.camel_name;755 let mutability = match self.mutability {756 Mutability::Mutable => quote! {SolidityMutability::Mutable},757 Mutability::View => quote! { SolidityMutability::View },758 Mutability::Pure => quote! {SolidityMutability::Pure},759 };760 let result = &self.result;761762 let args = self763 .args764 .iter()765 .filter(|a| !a.is_special())766 .map(MethodArg::expand_solidity_argument);767 let docs = self.docs.iter();768 let selector_str = &self.selector_str;769 let selector = self.selector;770771 quote! {772 SolidityFunction {773 docs: &[#(#docs),*],774 selector_str: #selector_str,775 selector: #selector,776 name: #camel_name,777 mutability: #mutability,778 args: (779 #(780 #args,781 )*782 ),783 result: <UnnamedArgument<#result>>::default(),784 }785 }786 }787}788789fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {790 if gen.params.is_empty() {791 return quote! {};792 }793 let params = gen.params.iter().map(|p| match p {794 syn::GenericParam::Type(id) => {795 let v = &id.ident;796 quote! {#v}797 }798 syn::GenericParam::Lifetime(lt) => {799 let v = <.lifetime;800 quote! {#v}801 }802 syn::GenericParam::Const(c) => {803 let i = &c.ident;804 quote! {#i}805 }806 });807 quote! { #(#params),* }808}809fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {810 if gen.params.is_empty() {811 return quote! {};812 }813 let list = generics_list(gen);814 quote! { <#list> }815}816fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {817 let list = generics_list(gen);818 if gen.params.len() == 1 {819 quote! {#list}820 } else {821 quote! { (#list) }822 }823}824825pub struct SolidityInterface {826 generics: Generics,827 name: Box<syn::Type>,828 info: InterfaceInfo,829 methods: Vec<Method>,830}831impl SolidityInterface {832 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {833 let mut methods = Vec::new();834835 for item in &value.items {836 if let ImplItem::Method(method) = item {837 methods.push(Method::try_from(method)?)838 }839 }840 Ok(Self {841 generics: value.generics.clone(),842 name: value.self_ty.clone(),843 info,844 methods,845 })846 }847 pub fn expand(self) -> proc_macro2::TokenStream {848 let name = self.name;849850 let solidity_name = self.info.name.to_string();851 let call_name = pascal_ident_to_call(&self.info.name);852 let generics = self.generics;853 let gen_ref = generics_reference(&generics);854 let gen_data = generics_data(&generics);855 let gen_where = &generics.where_clause;856857 let call_sub = self858 .info859 .inline_is860 .0861 .iter()862 .chain(self.info.is.0.iter())863 .map(|c| Is::expand_call_def(c, &gen_ref));864 let call_parse = self865 .info866 .inline_is867 .0868 .iter()869 .chain(self.info.is.0.iter())870 .map(|is| Is::expand_parse(is, &gen_ref));871 let call_variants = self872 .info873 .inline_is874 .0875 .iter()876 .chain(self.info.is.0.iter())877 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));878 let weight_variants = self879 .info880 .inline_is881 .0882 .iter()883 .chain(self.info.is.0.iter())884 .map(Is::expand_variant_weight);885886 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);887 let supports_interface = self888 .info889 .is890 .0891 .iter()892 .map(|is| Is::expand_supports_interface(is, &gen_ref));893894 let calls = self.methods.iter().map(Method::expand_call_def);895 let consts = self.methods.iter().map(Method::expand_const);896 let interface_id = self.methods.iter().map(Method::expand_interface_id);897 let parsers = self.methods.iter().map(Method::expand_parse);898 let call_variants_this = self899 .methods900 .iter()901 .map(|m| Method::expand_variant_call(m, &call_name));902 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);903 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);904905 // TODO: Inline inline_is906 let solidity_is = self907 .info908 .is909 .0910 .iter()911 .chain(self.info.inline_is.0.iter())912 .map(|is| is.name.to_string());913 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());914 let solidity_generators = self915 .info916 .is917 .0918 .iter()919 .chain(self.info.inline_is.0.iter())920 .map(|is| Is::expand_generator(is, &gen_ref));921 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);922923 if let Some(expect_selector) = &self.info.expect_selector {924 if !self.info.inline_is.0.is_empty() {925 return syn::Error::new(926 name.span(),927 "expect_selector is not compatible with inline_is",928 )929 .to_compile_error();930 }931 let selector = self932 .methods933 .iter()934 .map(|m| m.selector)935 .fold(0, |a, b| a ^ b);936937 if *expect_selector != selector {938 let mut methods = String::new();939 for meth in self.methods.iter() {940 write!(methods, "\n- {}", meth.selector_str).expect("write to string");941 }942 return syn::Error::new(name.span(), format!("expected selector mismatch, expected {expect_selector:0>8x}, but implementation has {selector:0>8x}{methods}")).to_compile_error();943 }944 }945 // let methods = self.methods.iter().map(Method::solidity_def);946947 quote! {948 #[derive(Debug)]949 pub enum #call_name #gen_ref {950 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),951 #(952 #calls,953 )*954 #(955 #call_sub,956 )*957 }958 impl #gen_ref #call_name #gen_ref {959 #(960 #consts961 )*962 pub fn interface_id() -> ::evm_coder::types::bytes4 {963 let mut interface_id = 0;964 #(#interface_id)*965 #(#inline_interface_id)*966 u32::to_be_bytes(interface_id)967 }968 pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {969 interface_id != u32::to_be_bytes(0xffffff) && (970 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||971 interface_id == Self::interface_id()972 #(973 || #supports_interface974 )*975 )976 }977 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {978 use evm_coder::solidity::*;979 use core::fmt::Write;980 let interface = SolidityInterface {981 name: #solidity_name,982 selector: Self::interface_id(),983 is: &["Dummy", "ERC165", #(984 #solidity_is,985 )* #(986 #solidity_events_is,987 )* ],988 functions: (#(989 #solidity_functions,990 )*),991 };992 if is_impl {993 tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());994 } else {995 tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());996 }997 #(998 #solidity_generators999 )*1000 #(1001 #solidity_event_generators1002 )*10031004 let mut out = string::new();1005 // In solidity interface usage (is) should be preceeded by interface definition1006 // HACK: this comment helps to sort it in a set1007 if #solidity_name.starts_with("Inline") {1008 out.push_str("/// @dev inlined interface\n");1009 }1010 let _ = interface.format(is_impl, &mut out, tc);1011 tc.collect(out);1012 }1013 }1014 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1015 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1016 use ::evm_coder::abi::AbiRead;1017 match method_id {1018 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1019 ::evm_coder::ERC165Call::parse(method_id, reader)?1020 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1021 ),1022 #(1023 #parsers,1024 )*1025 _ => {},1026 }1027 #(1028 #call_parse1029 )else*1030 return Ok(None);1031 }1032 }1033 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1034 #gen_where1035 {1036 #[allow(unused_variables)]1037 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1038 match self {1039 #(1040 #weight_variants,1041 )*1042 // TODO: It should be very cheap, but not free1043 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),1044 #(1045 #weight_variants_this,1046 )*1047 }1048 }1049 }1050 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1051 #gen_where1052 {1053 #[allow(unreachable_code)] // In case of no inner calls1054 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1055 use ::evm_coder::abi::AbiWrite;1056 match c.call {1057 #(1058 #call_variants,1059 )*1060 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1061 let mut writer = ::evm_coder::abi::AbiWriter::default();1062 writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));1063 return Ok(writer.into());1064 }1065 _ => {},1066 }1067 let mut writer = ::evm_coder::abi::AbiWriter::default();1068 match c.call {1069 #(1070 #call_variants_this,1071 )*1072 _ => unreachable!()1073 }1074 }1075 }1076 }1077 }1078}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![allow(dead_code)]1819use quote::{quote, ToTokens};20use inflector::cases;21use std::fmt::Write;22use syn::{23 Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,24 MetaNameValue, PatType, PathArguments, ReturnType, Type,25 spanned::Spanned,26 parse::{Parse, ParseStream},27 parenthesized, Token, LitInt, LitStr,28};2930use crate::{31 fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,32 parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,33 snake_ident_to_screaming,34};3536struct Is {37 name: Ident,38 pascal_call_name: Ident,39 snake_call_name: Ident,40 via: Option<(Type, Ident)>,41}42impl Is {43 fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {44 let name = &self.name;45 let pascal_call_name = &self.pascal_call_name;46 quote! {47 #name(#pascal_call_name #gen_ref)48 }49 }5051 fn expand_interface_id(&self) -> proc_macro2::TokenStream {52 let pascal_call_name = &self.pascal_call_name;53 quote! {54 interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());55 }56 }5758 fn expand_supports_interface(59 &self,60 generics: &proc_macro2::TokenStream,61 ) -> proc_macro2::TokenStream {62 let pascal_call_name = &self.pascal_call_name;63 quote! {64 <#pascal_call_name #generics>::supports_interface(interface_id)65 }66 }6768 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {69 let name = &self.name;70 quote! {71 Self::#name(call) => call.weight()72 }73 }7475 fn expand_variant_call(76 &self,77 call_name: &proc_macro2::Ident,78 generics: &proc_macro2::TokenStream,79 ) -> proc_macro2::TokenStream {80 let name = &self.name;81 let pascal_call_name = &self.pascal_call_name;82 let via_typ = self83 .via84 .as_ref()85 .map(|(t, _)| quote! {#t})86 .unwrap_or_else(|| quote! {Self});87 let via_map = self88 .via89 .as_ref()90 .map(|(_, i)| quote! {.#i()})91 .unwrap_or_default();92 quote! {93 #call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {94 call,95 caller: c.caller,96 value: c.value,97 })98 }99 }100101 fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {102 let name = &self.name;103 let pascal_call_name = &self.pascal_call_name;104 quote! {105 if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {106 return Ok(Some(Self::#name(parsed_call)))107 }108 }109 }110111 fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {112 let pascal_call_name = &self.pascal_call_name;113 quote! {114 <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);115 }116 }117118 fn expand_event_generator(&self) -> proc_macro2::TokenStream {119 let name = &self.name;120 quote! {121 #name::generate_solidity_interface(tc, is_impl);122 }123 }124}125126#[derive(Default)]127struct IsList(Vec<Is>);128impl Parse for IsList {129 fn parse(input: ParseStream) -> syn::Result<Self> {130 let mut out = vec![];131 loop {132 if input.is_empty() {133 break;134 }135 let name = input.parse::<Ident>()?;136 let lookahead = input.lookahead1();137 let via = if lookahead.peek(syn::token::Paren) {138 let contents;139 parenthesized!(contents in input);140 let method = contents.parse::<Ident>()?;141 contents.parse::<Token![,]>()?;142 let ty = contents.parse::<Type>()?;143 Some((ty, method))144 } else if lookahead.peek(Token![,]) {145 None146 } else if input.is_empty() {147 None148 } else {149 return Err(lookahead.error());150 };151 out.push(Is {152 pascal_call_name: pascal_ident_to_call(&name),153 snake_call_name: pascal_ident_to_snake_call(&name),154 name,155 via,156 });157 if input.peek(Token![,]) {158 input.parse::<Token![,]>()?;159 continue;160 } else {161 break;162 }163 }164 Ok(Self(out))165 }166}167168pub struct InterfaceInfo {169 name: Ident,170 is: IsList,171 inline_is: IsList,172 events: IsList,173 expect_selector: Option<u32>,174}175impl Parse for InterfaceInfo {176 fn parse(input: ParseStream) -> syn::Result<Self> {177 let mut name = None;178 let mut is = None;179 let mut inline_is = None;180 let mut events = None;181 let mut expect_selector = None;182 // TODO: create proc-macro to optimize proc-macro boilerplate? :D183 loop {184 let lookahead = input.lookahead1();185 if lookahead.peek(kw::name) {186 let k = input.parse::<kw::name>()?;187 input.parse::<Token![=]>()?;188 if name.replace(input.parse::<Ident>()?).is_some() {189 return Err(syn::Error::new(k.span(), "name is already set"));190 }191 } else if lookahead.peek(kw::is) {192 let k = input.parse::<kw::is>()?;193 let contents;194 parenthesized!(contents in input);195 if is.replace(contents.parse::<IsList>()?).is_some() {196 return Err(syn::Error::new(k.span(), "is is already set"));197 }198 } else if lookahead.peek(kw::inline_is) {199 let k = input.parse::<kw::inline_is>()?;200 let contents;201 parenthesized!(contents in input);202 if inline_is.replace(contents.parse::<IsList>()?).is_some() {203 return Err(syn::Error::new(k.span(), "inline_is is already set"));204 }205 } else if lookahead.peek(kw::events) {206 let k = input.parse::<kw::events>()?;207 let contents;208 parenthesized!(contents in input);209 if events.replace(contents.parse::<IsList>()?).is_some() {210 return Err(syn::Error::new(k.span(), "events is already set"));211 }212 } else if lookahead.peek(kw::expect_selector) {213 let k = input.parse::<kw::expect_selector>()?;214 input.parse::<Token![=]>()?;215 let value = input.parse::<LitInt>()?;216 if expect_selector217 .replace(value.base10_parse::<u32>()?)218 .is_some()219 {220 return Err(syn::Error::new(k.span(), "expect_selector is already set"));221 }222 } else if input.is_empty() {223 break;224 } else {225 return Err(lookahead.error());226 }227 if input.peek(Token![,]) {228 input.parse::<Token![,]>()?;229 } else {230 break;231 }232 }233 Ok(Self {234 name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,235 is: is.unwrap_or_default(),236 inline_is: inline_is.unwrap_or_default(),237 events: events.unwrap_or_default(),238 expect_selector,239 })240 }241}242243struct MethodInfo {244 rename_selector: Option<String>,245}246impl Parse for MethodInfo {247 fn parse(input: ParseStream) -> syn::Result<Self> {248 let mut rename_selector = None;249 let lookahead = input.lookahead1();250 if lookahead.peek(kw::rename_selector) {251 let k = input.parse::<kw::rename_selector>()?;252 input.parse::<Token![=]>()?;253 if rename_selector254 .replace(input.parse::<LitStr>()?.value())255 .is_some()256 {257 return Err(syn::Error::new(k.span(), "rename_selector is already set"));258 }259 }260 Ok(Self { rename_selector })261 }262}263264enum AbiType {265 // type266 Plain(Ident),267 // (type1,type2)268 Tuple(Vec<AbiType>),269 // type[]270 Vec(Box<AbiType>),271 // type[20]272 Array(Box<AbiType>, usize),273}274impl AbiType {275 fn try_from(value: &Type) -> syn::Result<Self> {276 let value = Self::try_maybe_special_from(value)?;277 if value.is_special() {278 return Err(syn::Error::new(value.span(), "unexpected special type"));279 }280 Ok(value)281 }282 fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {283 match value {284 Type::Array(arr) => {285 let wrapped = AbiType::try_from(&arr.elem)?;286 match &arr.len {287 Expr::Lit(l) => match &l.lit {288 Lit::Int(i) => {289 let num = i.base10_parse::<usize>()?;290 Ok(AbiType::Array(Box::new(wrapped), num as usize))291 }292 _ => Err(syn::Error::new(arr.len.span(), "should be int literal")),293 },294 _ => Err(syn::Error::new(arr.len.span(), "should be literal")),295 }296 }297 Type::Path(_) => {298 let path = parse_path(value)?;299 let segment = parse_path_segment(path)?;300 if segment.ident == "Vec" {301 let args = match &segment.arguments {302 PathArguments::AngleBracketed(e) => e,303 _ => {304 return Err(syn::Error::new(305 segment.arguments.span(),306 "missing Vec generic",307 ))308 }309 };310 let args = &args.args;311 if args.len() != 1 {312 return Err(syn::Error::new(313 args.span(),314 "expected only one generic for vec",315 ));316 }317 let arg = args.first().expect("first arg");318319 let ty = match arg {320 GenericArgument::Type(ty) => ty,321 _ => {322 return Err(syn::Error::new(323 arg.span(),324 "expected first generic to be type",325 ))326 }327 };328329 let wrapped = AbiType::try_from(ty)?;330 Ok(Self::Vec(Box::new(wrapped)))331 } else {332 if !segment.arguments.is_empty() {333 return Err(syn::Error::new(334 segment.arguments.span(),335 "unexpected generic arguments for non-vec type",336 ));337 }338 Ok(Self::Plain(segment.ident.clone()))339 }340 }341 Type::Tuple(t) => {342 let mut out = Vec::with_capacity(t.elems.len());343 for el in t.elems.iter() {344 out.push(AbiType::try_from(el)?)345 }346 Ok(Self::Tuple(out))347 }348 _ => Err(syn::Error::new(349 value.span(),350 "unexpected type, only arrays, plain types and tuples are supported",351 )),352 }353 }354 fn is_value(&self) -> bool {355 matches!(self, Self::Plain(v) if v == "value")356 }357 fn is_caller(&self) -> bool {358 matches!(self, Self::Plain(v) if v == "caller")359 }360 fn is_special(&self) -> bool {361 self.is_caller() || self.is_value()362 }363 fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {364 match self {365 AbiType::Plain(t) => {366 write!(buf, "{}", t)367 }368 AbiType::Tuple(t) => {369 write!(buf, "(")?;370 for (i, t) in t.iter().enumerate() {371 if i != 0 {372 write!(buf, ",")?;373 }374 t.selector_ty_buf(buf)?;375 }376 write!(buf, ")")377 }378 AbiType::Vec(v) => {379 v.selector_ty_buf(buf)?;380 write!(buf, "[]")381 }382 AbiType::Array(v, len) => {383 v.selector_ty_buf(buf)?;384 write!(buf, "[{}]", len)385 }386 }387 }388 fn selector_ty(&self) -> String {389 let mut out = String::new();390 self.selector_ty_buf(&mut out).expect("no fmt error");391 out392 }393}394impl ToTokens for AbiType {395 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {396 match self {397 AbiType::Plain(t) => tokens.extend(quote! {#t}),398 AbiType::Tuple(t) => {399 tokens.extend(quote! {(400 #(#t),*401 )});402 }403 AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),404 AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),405 }406 }407}408409struct MethodArg {410 name: Ident,411 camel_name: String,412 ty: AbiType,413}414impl MethodArg {415 fn try_from(value: &PatType) -> syn::Result<Self> {416 let name = parse_ident_from_pat(&value.pat)?.clone();417 Ok(Self {418 camel_name: cases::camelcase::to_camel_case(&name.to_string()),419 name,420 ty: AbiType::try_maybe_special_from(&value.ty)?,421 })422 }423 fn is_value(&self) -> bool {424 self.ty.is_value()425 }426 fn is_caller(&self) -> bool {427 self.ty.is_caller()428 }429 fn is_special(&self) -> bool {430 self.ty.is_special()431 }432 fn selector_ty(&self) -> String {433 assert!(!self.is_special());434 self.ty.selector_ty()435 }436437 fn expand_call_def(&self) -> proc_macro2::TokenStream {438 assert!(!self.is_special());439 let name = &self.name;440 let ty = &self.ty;441442 quote! {443 #name: #ty444 }445 }446447 fn expand_parse(&self) -> proc_macro2::TokenStream {448 assert!(!self.is_special());449 let name = &self.name;450 quote! {451 #name: reader.abi_read()?452 }453 }454455 fn expand_call_arg(&self) -> proc_macro2::TokenStream {456 if self.is_value() {457 quote! {458 c.value.clone()459 }460 } else if self.is_caller() {461 quote! {462 c.caller.clone()463 }464 } else {465 let name = &self.name;466 quote! {467 #name468 }469 }470 }471472 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {473 let camel_name = &self.camel_name.to_string();474 let ty = &self.ty;475 quote! {476 <NamedArgument<#ty>>::new(#camel_name)477 }478 }479}480481#[derive(PartialEq)]482enum Mutability {483 Mutable,484 View,485 Pure,486}487488mod kw {489 syn::custom_keyword!(weight);490491 syn::custom_keyword!(via);492 syn::custom_keyword!(name);493 syn::custom_keyword!(is);494 syn::custom_keyword!(inline_is);495 syn::custom_keyword!(events);496 syn::custom_keyword!(expect_selector);497498 syn::custom_keyword!(rename_selector);499}500501struct Method {502 name: Ident,503 camel_name: String,504 pascal_name: Ident,505 screaming_name: Ident,506 selector_str: String,507 selector: u32,508 args: Vec<MethodArg>,509 has_normal_args: bool,510 mutability: Mutability,511 result: Type,512 weight: Option<Expr>,513 docs: Vec<String>,514}515impl Method {516 fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {517 let mut info = MethodInfo {518 rename_selector: None,519 };520 let mut docs = Vec::new();521 let mut weight = None;522 for attr in &value.attrs {523 let ident = parse_ident_from_path(&attr.path, false)?;524 if ident == "solidity" {525 info = attr.parse_args::<MethodInfo>()?;526 } else if ident == "doc" {527 let args = attr.parse_meta().unwrap();528 let value = match args {529 Meta::NameValue(MetaNameValue {530 lit: Lit::Str(str), ..531 }) => str.value(),532 _ => unreachable!(),533 };534 docs.push(value);535 } else if ident == "weight" {536 weight = Some(attr.parse_args::<Expr>()?);537 }538 }539 let ident = &value.sig.ident;540 let ident_str = ident.to_string();541 if !cases::snakecase::is_snake_case(&ident_str) {542 return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));543 }544545 let mut mutability = Mutability::Pure;546547 if let Some(FnArg::Receiver(receiver)) = value548 .sig549 .inputs550 .iter()551 .find(|arg| matches!(arg, FnArg::Receiver(_)))552 {553 if receiver.reference.is_none() {554 return Err(syn::Error::new(555 receiver.span(),556 "receiver should be by ref",557 ));558 }559 if receiver.mutability.is_some() {560 mutability = Mutability::Mutable;561 } else {562 mutability = Mutability::View;563 }564 }565 let mut args = Vec::new();566 for typ in value567 .sig568 .inputs569 .iter()570 .filter(|arg| matches!(arg, FnArg::Typed(_)))571 {572 let typ = match typ {573 FnArg::Typed(typ) => typ,574 _ => unreachable!(),575 };576 args.push(MethodArg::try_from(typ)?);577 }578579 if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {580 return Err(syn::Error::new(581 args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),582 "payable function should be mutable",583 ));584 }585586 let result = match &value.sig.output {587 ReturnType::Type(_, ty) => ty,588 _ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),589 };590 let result = parse_result_ok(result)?;591592 let camel_name = info593 .rename_selector594 .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));595 let mut selector_str = camel_name.clone();596 selector_str.push('(');597 let mut has_normal_args = false;598 for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {599 if i != 0 {600 selector_str.push(',');601 }602 write!(selector_str, "{}", arg.selector_ty()).unwrap();603 has_normal_args = true;604 }605 selector_str.push(')');606 let selector = fn_selector_str(&selector_str);607608 Ok(Self {609 name: ident.clone(),610 camel_name,611 pascal_name: snake_ident_to_pascal(ident),612 screaming_name: snake_ident_to_screaming(ident),613 selector_str,614 selector,615 args,616 has_normal_args,617 mutability,618 result: result.clone(),619 weight,620 docs,621 })622 }623 fn expand_call_def(&self) -> proc_macro2::TokenStream {624 let defs = self625 .args626 .iter()627 .filter(|a| !a.is_special())628 .map(|a| a.expand_call_def());629 let pascal_name = &self.pascal_name;630631 if self.has_normal_args {632 quote! {633 #pascal_name {634 #(635 #defs,636 )*637 }638 }639 } else {640 quote! {#pascal_name}641 }642 }643644 fn expand_const(&self) -> proc_macro2::TokenStream {645 let screaming_name = &self.screaming_name;646 let selector = u32::to_be_bytes(self.selector);647 let selector_str = &self.selector_str;648 quote! {649 #[doc = #selector_str]650 const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];651 }652 }653654 fn expand_interface_id(&self) -> proc_macro2::TokenStream {655 let screaming_name = &self.screaming_name;656 quote! {657 interface_id ^= u32::from_be_bytes(Self::#screaming_name);658 }659 }660661 fn expand_parse(&self) -> proc_macro2::TokenStream {662 let pascal_name = &self.pascal_name;663 let screaming_name = &self.screaming_name;664 if self.has_normal_args {665 let parsers = self666 .args667 .iter()668 .filter(|a| !a.is_special())669 .map(|a| a.expand_parse());670 quote! {671 Self::#screaming_name => return Ok(Some(Self::#pascal_name {672 #(673 #parsers,674 )*675 }))676 }677 } else {678 quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }679 }680 }681682 fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {683 let pascal_name = &self.pascal_name;684 let name = &self.name;685686 let matcher = if self.has_normal_args {687 let names = self688 .args689 .iter()690 .filter(|a| !a.is_special())691 .map(|a| &a.name);692693 quote! {{694 #(695 #names,696 )*697 }}698 } else {699 quote! {}700 };701702 let receiver = match self.mutability {703 Mutability::Mutable | Mutability::View => quote! {self.},704 Mutability::Pure => quote! {Self::},705 };706 let args = self.args.iter().map(|a| a.expand_call_arg());707708 quote! {709 #call_name::#pascal_name #matcher => {710 let result = #receiver #name(711 #(712 #args,713 )*714 )?;715 (&result).to_result()716 }717 }718 }719720 fn expand_variant_weight(&self) -> proc_macro2::TokenStream {721 let pascal_name = &self.pascal_name;722 if let Some(weight) = &self.weight {723 let matcher = if self.has_normal_args {724 let names = self725 .args726 .iter()727 .filter(|a| !a.is_special())728 .map(|a| &a.name);729730 quote! {{731 #(732 #names,733 )*734 }}735 } else {736 quote! {}737 };738 quote! {739 Self::#pascal_name #matcher => (#weight).into()740 }741 } else {742 let matcher = if self.has_normal_args {743 quote! {{..}}744 } else {745 quote! {}746 };747 quote! {748 Self::#pascal_name #matcher => ().into()749 }750 }751 }752753 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {754 let camel_name = &self.camel_name;755 let mutability = match self.mutability {756 Mutability::Mutable => quote! {SolidityMutability::Mutable},757 Mutability::View => quote! { SolidityMutability::View },758 Mutability::Pure => quote! {SolidityMutability::Pure},759 };760 let result = &self.result;761762 let args = self763 .args764 .iter()765 .filter(|a| !a.is_special())766 .map(MethodArg::expand_solidity_argument);767 let docs = self.docs.iter();768 let selector_str = &self.selector_str;769 let selector = self.selector;770771 quote! {772 SolidityFunction {773 docs: &[#(#docs),*],774 selector_str: #selector_str,775 selector: #selector,776 name: #camel_name,777 mutability: #mutability,778 args: (779 #(780 #args,781 )*782 ),783 result: <UnnamedArgument<#result>>::default(),784 }785 }786 }787}788789fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {790 if gen.params.is_empty() {791 return quote! {};792 }793 let params = gen.params.iter().map(|p| match p {794 syn::GenericParam::Type(id) => {795 let v = &id.ident;796 quote! {#v}797 }798 syn::GenericParam::Lifetime(lt) => {799 let v = <.lifetime;800 quote! {#v}801 }802 syn::GenericParam::Const(c) => {803 let i = &c.ident;804 quote! {#i}805 }806 });807 quote! { #(#params),* }808}809fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {810 if gen.params.is_empty() {811 return quote! {};812 }813 let list = generics_list(gen);814 quote! { <#list> }815}816fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {817 let list = generics_list(gen);818 if gen.params.len() == 1 {819 quote! {#list}820 } else {821 quote! { (#list) }822 }823}824825pub struct SolidityInterface {826 generics: Generics,827 name: Box<syn::Type>,828 info: InterfaceInfo,829 methods: Vec<Method>,830}831impl SolidityInterface {832 pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {833 let mut methods = Vec::new();834835 for item in &value.items {836 if let ImplItem::Method(method) = item {837 methods.push(Method::try_from(method)?)838 }839 }840 Ok(Self {841 generics: value.generics.clone(),842 name: value.self_ty.clone(),843 info,844 methods,845 })846 }847 pub fn expand(self) -> proc_macro2::TokenStream {848 let name = self.name;849850 let solidity_name = self.info.name.to_string();851 let call_name = pascal_ident_to_call(&self.info.name);852 let generics = self.generics;853 let gen_ref = generics_reference(&generics);854 let gen_data = generics_data(&generics);855 let gen_where = &generics.where_clause;856857 let call_sub = self858 .info859 .inline_is860 .0861 .iter()862 .chain(self.info.is.0.iter())863 .map(|c| Is::expand_call_def(c, &gen_ref));864 let call_parse = self865 .info866 .inline_is867 .0868 .iter()869 .chain(self.info.is.0.iter())870 .map(|is| Is::expand_parse(is, &gen_ref));871 let call_variants = self872 .info873 .inline_is874 .0875 .iter()876 .chain(self.info.is.0.iter())877 .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));878 let weight_variants = self879 .info880 .inline_is881 .0882 .iter()883 .chain(self.info.is.0.iter())884 .map(Is::expand_variant_weight);885886 let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);887 let supports_interface = self888 .info889 .is890 .0891 .iter()892 .map(|is| Is::expand_supports_interface(is, &gen_ref));893894 let calls = self.methods.iter().map(Method::expand_call_def);895 let consts = self.methods.iter().map(Method::expand_const);896 let interface_id = self.methods.iter().map(Method::expand_interface_id);897 let parsers = self.methods.iter().map(Method::expand_parse);898 let call_variants_this = self899 .methods900 .iter()901 .map(|m| Method::expand_variant_call(m, &call_name));902 let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);903 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);904905 // TODO: Inline inline_is906 let solidity_is = self907 .info908 .is909 .0910 .iter()911 .chain(self.info.inline_is.0.iter())912 .map(|is| is.name.to_string());913 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());914 let solidity_generators = self915 .info916 .is917 .0918 .iter()919 .chain(self.info.inline_is.0.iter())920 .map(|is| Is::expand_generator(is, &gen_ref));921 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);922923 if let Some(expect_selector) = &self.info.expect_selector {924 if !self.info.inline_is.0.is_empty() {925 return syn::Error::new(926 name.span(),927 "expect_selector is not compatible with inline_is",928 )929 .to_compile_error();930 }931 let selector = self932 .methods933 .iter()934 .map(|m| m.selector)935 .fold(0, |a, b| a ^ b);936937 if *expect_selector != selector {938 let mut methods = String::new();939 for meth in self.methods.iter() {940 write!(methods, "\n- {}", meth.selector_str).expect("write to string");941 }942 return syn::Error::new(name.span(), format!("expected selector mismatch, expected {expect_selector:0>8x}, but implementation has {selector:0>8x}{methods}")).to_compile_error();943 }944 }945 // let methods = self.methods.iter().map(Method::solidity_def);946947 quote! {948 #[derive(Debug)]949 pub enum #call_name #gen_ref {950 ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),951 #(952 #calls,953 )*954 #(955 #call_sub,956 )*957 }958 impl #gen_ref #call_name #gen_ref {959 #(960 #consts961 )*962 pub fn interface_id() -> ::evm_coder::types::bytes4 {963 let mut interface_id = 0;964 #(#interface_id)*965 #(#inline_interface_id)*966 u32::to_be_bytes(interface_id)967 }968 pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {969 interface_id != u32::to_be_bytes(0xffffff) && (970 interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||971 interface_id == Self::interface_id()972 #(973 || #supports_interface974 )*975 )976 }977 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {978 use evm_coder::solidity::*;979 use core::fmt::Write;980 let interface = SolidityInterface {981 name: #solidity_name,982 selector: Self::interface_id(),983 is: &["Dummy", "ERC165", #(984 #solidity_is,985 )* #(986 #solidity_events_is,987 )* ],988 functions: (#(989 #solidity_functions,990 )*),991 };992993 let mut out = string::new();994 if #solidity_name.starts_with("Inline") {995 out.push_str("/// @dev inlined interface\n");996 }997 let _ = interface.format(is_impl, &mut out, tc);998 tc.collect(out);999 #(1000 #solidity_event_generators1001 )*1002 #(1003 #solidity_generators1004 )*1005 if is_impl {1006 tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());1007 } else {1008 tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());1009 }1010 }1011 }1012 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1013 fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {1014 use ::evm_coder::abi::AbiRead;1015 match method_id {1016 ::evm_coder::ERC165Call::INTERFACE_ID => return Ok(1017 ::evm_coder::ERC165Call::parse(method_id, reader)?1018 .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))1019 ),1020 #(1021 #parsers,1022 )*1023 _ => {},1024 }1025 #(1026 #call_parse1027 )else*1028 return Ok(None);1029 }1030 }1031 impl #generics ::evm_coder::Weighted for #call_name #gen_ref1032 #gen_where1033 {1034 #[allow(unused_variables)]1035 fn weight(&self) -> ::evm_coder::execution::DispatchInfo {1036 match self {1037 #(1038 #weight_variants,1039 )*1040 // TODO: It should be very cheap, but not free1041 Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),1042 #(1043 #weight_variants_this,1044 )*1045 }1046 }1047 }1048 impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name1049 #gen_where1050 {1051 #[allow(unreachable_code)] // In case of no inner calls1052 fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {1053 use ::evm_coder::abi::AbiWrite;1054 match c.call {1055 #(1056 #call_variants,1057 )*1058 #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {1059 let mut writer = ::evm_coder::abi::AbiWriter::default();1060 writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));1061 return Ok(writer.into());1062 }1063 _ => {},1064 }1065 let mut writer = ::evm_coder::abi::AbiWriter::default();1066 match c.call {1067 #(1068 #call_variants_this,1069 )*1070 _ => unreachable!()1071 }1072 }1073 }1074 }1075 }1076}crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -14,26 +14,31 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation
+
#[cfg(not(feature = "std"))]
use alloc::{
string::String,
vec::Vec,
- collections::{BTreeSet, BTreeMap},
+ collections::BTreeMap,
format,
};
#[cfg(feature = "std")]
-use std::collections::{BTreeSet, BTreeMap};
+use std::collections::BTreeMap;
use core::{
fmt::{self, Write},
marker::PhantomData,
cell::{Cell, RefCell},
+ cmp::Reverse,
};
use impl_trait_for_tuples::impl_for_tuples;
use crate::types::*;
#[derive(Default)]
pub struct TypeCollector {
- structs: RefCell<BTreeSet<string>>,
+ /// Code => id
+ /// id ordering is required to perform topo-sort on the resulting data
+ structs: RefCell<BTreeMap<string, usize>>,
anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
id: Cell<usize>,
}
@@ -42,7 +47,8 @@
Self::default()
}
pub fn collect(&self, item: string) {
- self.structs.borrow_mut().insert(item);
+ let id = self.next_id();
+ self.structs.borrow_mut().insert(item, id);
}
pub fn next_id(&self) -> usize {
let v = self.id.get();
@@ -66,8 +72,10 @@
self.anonymous.borrow_mut().insert(names, id);
format!("Tuple{}", id)
}
- pub fn finish(self) -> BTreeSet<string> {
- self.structs.into_inner()
+ pub fn finish(self) -> Vec<string> {
+ let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
+ data.sort_by_key(|(_, id)| Reverse(*id));
+ data.into_iter().map(|(code, _)| code).collect()
}
}