git.delta.rocks / unique-network / refs/commits / 7d5b680cd73d

difftreelog

refactor(evm-coder) toposort generated code

Yaroslav Bolyukin2022-07-12parent: #50d005e.patch.diff
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

modifiedcrates/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)]
modifiedcrates/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 {
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
after · crates/evm-coder/src/solidity.rs
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//! 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}