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.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -989,26 +989,24 @@
#solidity_functions,
)*),
};
- if is_impl {
- 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());
- } else {
- 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());
- }
- #(
- #solidity_generators
- )*
- #(
- #solidity_event_generators
- )*
let mut out = string::new();
- // In solidity interface usage (is) should be preceeded by interface definition
- // HACK: this comment helps to sort it in a set
if #solidity_name.starts_with("Inline") {
out.push_str("/// @dev inlined interface\n");
}
let _ = interface.format(is_impl, &mut out, tc);
tc.collect(out);
+ #(
+ #solidity_event_generators
+ )*
+ #(
+ #solidity_generators
+ )*
+ if is_impl {
+ 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());
+ } else {
+ 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());
+ }
}
}
impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
crates/evm-coder/src/solidity.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//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation1819#[cfg(not(feature = "std"))]20use alloc::{21 string::String,22 vec::Vec,23 collections::BTreeMap,24 format,25};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29 fmt::{self, Write},30 marker::PhantomData,31 cell::{Cell, RefCell},32 cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39 /// Code => id40 /// id ordering is required to perform topo-sort on the resulting data41 structs: RefCell<BTreeMap<string, usize>>,42 anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43 id: Cell<usize>,44}45impl TypeCollector {46 pub fn new() -> Self {47 Self::default()48 }49 pub fn collect(&self, item: string) {50 let id = self.next_id();51 self.structs.borrow_mut().insert(item, id);52 }53 pub fn next_id(&self) -> usize {54 let v = self.id.get();55 self.id.set(v + 1);56 v57 }58 pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59 let names = T::names(self);60 if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61 return format!("Tuple{}", id);62 }63 let id = self.next_id();64 let mut str = String::new();65 writeln!(str, "/// @dev anonymous struct").unwrap();66 writeln!(str, "struct Tuple{} {{", id).unwrap();67 for (i, name) in names.iter().enumerate() {68 writeln!(str, "\t{} field_{};", name, i).unwrap();69 }70 writeln!(str, "}}").unwrap();71 self.collect(str);72 self.anonymous.borrow_mut().insert(names, id);73 format!("Tuple{}", id)74 }75 pub fn finish(self) -> Vec<string> {76 let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77 data.sort_by_key(|(_, id)| Reverse(*id));78 data.into_iter().map(|(code, _)| code).collect()79 }80}8182pub trait SolidityTypeName: 'static {83 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84 fn is_simple() -> bool;85 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;86 fn is_void() -> bool {87 false88 }89}90macro_rules! solidity_type_name {91 ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {92 $(93 impl SolidityTypeName for $ty {94 fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {95 write!(writer, $name)96 }97 fn is_simple() -> bool {98 $simple99 }100 fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {101 write!(writer, $default)102 }103 }104 )*105 };106}107108solidity_type_name! {109 uint8 => "uint8" true = "0",110 uint32 => "uint32" true = "0",111 uint64 => "uint64" true = "0",112 uint128 => "uint128" true = "0",113 uint256 => "uint256" true = "0",114 bytes4 => "bytes4" true = "bytes4(0)",115 address => "address" true = "0x0000000000000000000000000000000000000000",116 string => "string" false = "\"\"",117 bytes => "bytes" false = "hex\"\"",118 bool => "bool" true = "false",119}120impl SolidityTypeName for void {121 fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {122 Ok(())123 }124 fn is_simple() -> bool {125 true126 }127 fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {128 Ok(())129 }130 fn is_void() -> bool {131 true132 }133}134135mod sealed {136 pub trait CanBePlacedInVec {}137}138139impl sealed::CanBePlacedInVec for uint256 {}140impl sealed::CanBePlacedInVec for string {}141impl sealed::CanBePlacedInVec for address {}142143impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {144 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {145 T::solidity_name(writer, tc)?;146 write!(writer, "[]")147 }148 fn is_simple() -> bool {149 false150 }151 fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {152 write!(writer, "[]")153 }154}155156pub trait SolidityTupleType {157 fn names(tc: &TypeCollector) -> Vec<String>;158 fn len() -> usize;159}160161macro_rules! count {162 () => (0usize);163 ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));164}165166macro_rules! impl_tuples {167 ($($ident:ident)+) => {168 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}169 impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {170 fn names(tc: &TypeCollector) -> Vec<string> {171 let mut collected = Vec::with_capacity(Self::len());172 $({173 let mut out = string::new();174 $ident::solidity_name(&mut out, tc).expect("no fmt error");175 collected.push(out);176 })*;177 collected178 }179180 fn len() -> usize {181 count!($($ident)*)182 }183 }184 impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {185 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {186 write!(writer, "{}", tc.collect_tuple::<Self>())187 }188 fn is_simple() -> bool {189 false190 }191 #[allow(unused_assignments)]192 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {193 write!(writer, "{}(", tc.collect_tuple::<Self>())?;194 let mut first = true;195 $(196 if !first {197 write!(writer, ",")?;198 } else {199 first = false;200 }201 <$ident>::solidity_default(writer, tc)?;202 )*203 write!(writer, ")")204 }205 }206 };207}208209impl_tuples! {A}210impl_tuples! {A B}211impl_tuples! {A B C}212impl_tuples! {A B C D}213impl_tuples! {A B C D E}214impl_tuples! {A B C D E F}215impl_tuples! {A B C D E F G}216impl_tuples! {A B C D E F G H}217impl_tuples! {A B C D E F G H I}218impl_tuples! {A B C D E F G H I J}219220pub trait SolidityArguments {221 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;222 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;223 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;224 fn is_empty(&self) -> bool {225 self.len() == 0226 }227 fn len(&self) -> usize;228}229230#[derive(Default)]231pub struct UnnamedArgument<T>(PhantomData<*const T>);232233impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {234 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {235 if !T::is_void() {236 T::solidity_name(writer, tc)?;237 if !T::is_simple() {238 write!(writer, " memory")?;239 }240 Ok(())241 } else {242 Ok(())243 }244 }245 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {246 Ok(())247 }248 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {249 T::solidity_default(writer, tc)250 }251 fn len(&self) -> usize {252 if T::is_void() {253 0254 } else {255 1256 }257 }258}259260pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);261262impl<T> NamedArgument<T> {263 pub fn new(name: &'static str) -> Self {264 Self(name, Default::default())265 }266}267268impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {269 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {270 if !T::is_void() {271 T::solidity_name(writer, tc)?;272 if !T::is_simple() {273 write!(writer, " memory")?;274 }275 write!(writer, " {}", self.0)276 } else {277 Ok(())278 }279 }280 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {281 writeln!(writer, "\t\t{};", self.0)282 }283 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {284 T::solidity_default(writer, tc)285 }286 fn len(&self) -> usize {287 if T::is_void() {288 0289 } else {290 1291 }292 }293}294295pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);296297impl<T> SolidityEventArgument<T> {298 pub fn new(indexed: bool, name: &'static str) -> Self {299 Self(indexed, name, Default::default())300 }301}302303impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {304 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {305 if !T::is_void() {306 T::solidity_name(writer, tc)?;307 if self.0 {308 write!(writer, " indexed")?;309 }310 write!(writer, " {}", self.1)311 } else {312 Ok(())313 }314 }315 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {316 writeln!(writer, "\t\t{};", self.1)317 }318 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {319 T::solidity_default(writer, tc)320 }321 fn len(&self) -> usize {322 if T::is_void() {323 0324 } else {325 1326 }327 }328}329330impl SolidityArguments for () {331 fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {332 Ok(())333 }334 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {335 Ok(())336 }337 fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338 Ok(())339 }340 fn len(&self) -> usize {341 0342 }343}344345#[impl_for_tuples(1, 12)]346impl SolidityArguments for Tuple {347 for_tuples!( where #( Tuple: SolidityArguments ),* );348349 fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {350 let mut first = true;351 for_tuples!( #(352 if !Tuple.is_empty() {353 if !first {354 write!(writer, ", ")?;355 }356 first = false;357 Tuple.solidity_name(writer, tc)?;358 }359 )* );360 Ok(())361 }362 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {363 for_tuples!( #(364 Tuple.solidity_get(writer)?;365 )* );366 Ok(())367 }368 fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {369 if self.is_empty() {370 Ok(())371 } else if self.len() == 1 {372 for_tuples!( #(373 Tuple.solidity_default(writer, tc)?;374 )* );375 Ok(())376 } else {377 write!(writer, "(")?;378 let mut first = true;379 for_tuples!( #(380 if !Tuple.is_empty() {381 if !first {382 write!(writer, ", ")?;383 }384 first = false;385 Tuple.solidity_default(writer, tc)?;386 }387 )* );388 write!(writer, ")")?;389 Ok(())390 }391 }392 fn len(&self) -> usize {393 for_tuples!( #( Tuple.len() )+* )394 }395}396397pub trait SolidityFunctions {398 fn solidity_name(399 &self,400 is_impl: bool,401 writer: &mut impl fmt::Write,402 tc: &TypeCollector,403 ) -> fmt::Result;404}405406pub enum SolidityMutability {407 Pure,408 View,409 Mutable,410}411pub struct SolidityFunction<A, R> {412 pub docs: &'static [&'static str],413 pub selector_str: &'static str,414 pub selector: u32,415 pub name: &'static str,416 pub args: A,417 pub result: R,418 pub mutability: SolidityMutability,419}420impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {421 fn solidity_name(422 &self,423 is_impl: bool,424 writer: &mut impl fmt::Write,425 tc: &TypeCollector,426 ) -> fmt::Result {427 for doc in self.docs {428 writeln!(writer, "\t///{}", doc)?;429 }430 if !self.docs.is_empty() {431 writeln!(writer, "\t///")?;432 }433 writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;434 writeln!(writer, "\t/// or in textual repr: {}", self.selector_str)?;435 write!(writer, "\tfunction {}(", self.name)?;436 self.args.solidity_name(writer, tc)?;437 write!(writer, ")")?;438 if is_impl {439 write!(writer, " public")?;440 } else {441 write!(writer, " external")?;442 }443 match &self.mutability {444 SolidityMutability::Pure => write!(writer, " pure")?,445 SolidityMutability::View => write!(writer, " view")?,446 SolidityMutability::Mutable => {}447 }448 if !self.result.is_empty() {449 write!(writer, " returns (")?;450 self.result.solidity_name(writer, tc)?;451 write!(writer, ")")?;452 }453 if is_impl {454 writeln!(writer, " {{")?;455 writeln!(writer, "\t\trequire(false, stub_error);")?;456 self.args.solidity_get(writer)?;457 match &self.mutability {458 SolidityMutability::Pure => {}459 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,460 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,461 }462 if !self.result.is_empty() {463 write!(writer, "\t\treturn ")?;464 self.result.solidity_default(writer, tc)?;465 writeln!(writer, ";")?;466 }467 writeln!(writer, "\t}}")?;468 } else {469 writeln!(writer, ";")?;470 }471 Ok(())472 }473}474475#[impl_for_tuples(0, 24)]476impl SolidityFunctions for Tuple {477 for_tuples!( where #( Tuple: SolidityFunctions ),* );478479 fn solidity_name(480 &self,481 is_impl: bool,482 writer: &mut impl fmt::Write,483 tc: &TypeCollector,484 ) -> fmt::Result {485 let mut first = false;486 for_tuples!( #(487 Tuple.solidity_name(is_impl, writer, tc)?;488 )* );489 Ok(())490 }491}492493pub struct SolidityInterface<F: SolidityFunctions> {494 pub selector: bytes4,495 pub name: &'static str,496 pub is: &'static [&'static str],497 pub functions: F,498}499500impl<F: SolidityFunctions> SolidityInterface<F> {501 pub fn format(502 &self,503 is_impl: bool,504 out: &mut impl fmt::Write,505 tc: &TypeCollector,506 ) -> fmt::Result {507 const ZERO_BYTES: [u8; 4] = [0; 4];508 if self.selector != ZERO_BYTES {509 writeln!(510 out,511 "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",512 u32::from_be_bytes(self.selector)513 )?;514 }515 if is_impl {516 write!(out, "contract ")?;517 } else {518 write!(out, "interface ")?;519 }520 write!(out, "{}", self.name)?;521 if !self.is.is_empty() {522 write!(out, " is")?;523 for (i, n) in self.is.iter().enumerate() {524 if i != 0 {525 write!(out, ",")?;526 }527 write!(out, " {}", n)?;528 }529 }530 writeln!(out, " {{")?;531 self.functions.solidity_name(is_impl, out, tc)?;532 writeln!(out, "}}")?;533 Ok(())534 }535}536537pub struct SolidityEvent<A> {538 pub name: &'static str,539 pub args: A,540}541542impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {543 fn solidity_name(544 &self,545 _is_impl: bool,546 writer: &mut impl fmt::Write,547 tc: &TypeCollector,548 ) -> fmt::Result {549 write!(writer, "\tevent {}(", self.name)?;550 self.args.solidity_name(writer, tc)?;551 writeln!(writer, ");")552 }553}