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
before · 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#[cfg(not(feature = "std"))]18use alloc::{19	string::String,20	vec::Vec,21	collections::{BTreeSet, BTreeMap},22	format,23};24#[cfg(feature = "std")]25use std::collections::{BTreeSet, BTreeMap};26use core::{27	fmt::{self, Write},28	marker::PhantomData,29	cell::{Cell, RefCell},30};31use impl_trait_for_tuples::impl_for_tuples;32use crate::types::*;3334#[derive(Default)]35pub struct TypeCollector {36	structs: RefCell<BTreeSet<string>>,37	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,38	id: Cell<usize>,39}40impl TypeCollector {41	pub fn new() -> Self {42		Self::default()43	}44	pub fn collect(&self, item: string) {45		self.structs.borrow_mut().insert(item);46	}47	pub fn next_id(&self) -> usize {48		let v = self.id.get();49		self.id.set(v + 1);50		v51	}52	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {53		let names = T::names(self);54		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {55			return format!("Tuple{}", id);56		}57		let id = self.next_id();58		let mut str = String::new();59		writeln!(str, "/// @dev anonymous struct").unwrap();60		writeln!(str, "struct Tuple{} {{", id).unwrap();61		for (i, name) in names.iter().enumerate() {62			writeln!(str, "\t{} field_{};", name, i).unwrap();63		}64		writeln!(str, "}}").unwrap();65		self.collect(str);66		self.anonymous.borrow_mut().insert(names, id);67		format!("Tuple{}", id)68	}69	pub fn finish(self) -> BTreeSet<string> {70		self.structs.into_inner()71	}72}7374pub trait SolidityTypeName: 'static {75	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;76	fn is_simple() -> bool;77	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;78	fn is_void() -> bool {79		false80	}81}82macro_rules! solidity_type_name {83    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {84        $(85            impl SolidityTypeName for $ty {86                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {87                    write!(writer, $name)88                }89				fn is_simple() -> bool {90					$simple91				}92				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {93					write!(writer, $default)94				}95            }96        )*97    };98}99100solidity_type_name! {101	uint8 => "uint8" true = "0",102	uint32 => "uint32" true = "0",103	uint64 => "uint64" true = "0",104	uint128 => "uint128" true = "0",105	uint256 => "uint256" true = "0",106	bytes4 => "bytes4" true = "bytes4(0)",107	address => "address" true = "0x0000000000000000000000000000000000000000",108	string => "string" false = "\"\"",109	bytes => "bytes" false = "hex\"\"",110	bool => "bool" true = "false",111}112impl SolidityTypeName for void {113	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {114		Ok(())115	}116	fn is_simple() -> bool {117		true118	}119	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {120		Ok(())121	}122	fn is_void() -> bool {123		true124	}125}126127mod sealed {128	pub trait CanBePlacedInVec {}129}130131impl sealed::CanBePlacedInVec for uint256 {}132impl sealed::CanBePlacedInVec for string {}133impl sealed::CanBePlacedInVec for address {}134135impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {136	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {137		T::solidity_name(writer, tc)?;138		write!(writer, "[]")139	}140	fn is_simple() -> bool {141		false142	}143	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {144		write!(writer, "[]")145	}146}147148pub trait SolidityTupleType {149	fn names(tc: &TypeCollector) -> Vec<String>;150	fn len() -> usize;151}152153macro_rules! count {154    () => (0usize);155    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));156}157158macro_rules! impl_tuples {159	($($ident:ident)+) => {160		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}161		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {162			fn names(tc: &TypeCollector) -> Vec<string> {163				let mut collected = Vec::with_capacity(Self::len());164				$({165					let mut out = string::new();166					$ident::solidity_name(&mut out, tc).expect("no fmt error");167					collected.push(out);168				})*;169				collected170			}171172			fn len() -> usize {173				count!($($ident)*)174			}175		}176		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {177			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {178				write!(writer, "{}", tc.collect_tuple::<Self>())179			}180			fn is_simple() -> bool {181				false182			}183			#[allow(unused_assignments)]184			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {185				write!(writer, "{}(", tc.collect_tuple::<Self>())?;186				let mut first = true;187				$(188					if !first {189						write!(writer, ",")?;190					} else {191						first = false;192					}193					<$ident>::solidity_default(writer, tc)?;194				)*195				write!(writer, ")")196			}197		}198	};199}200201impl_tuples! {A}202impl_tuples! {A B}203impl_tuples! {A B C}204impl_tuples! {A B C D}205impl_tuples! {A B C D E}206impl_tuples! {A B C D E F}207impl_tuples! {A B C D E F G}208impl_tuples! {A B C D E F G H}209impl_tuples! {A B C D E F G H I}210impl_tuples! {A B C D E F G H I J}211212pub trait SolidityArguments {213	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;214	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;215	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;216	fn is_empty(&self) -> bool {217		self.len() == 0218	}219	fn len(&self) -> usize;220}221222#[derive(Default)]223pub struct UnnamedArgument<T>(PhantomData<*const T>);224225impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {226	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {227		if !T::is_void() {228			T::solidity_name(writer, tc)?;229			if !T::is_simple() {230				write!(writer, " memory")?;231			}232			Ok(())233		} else {234			Ok(())235		}236	}237	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {238		Ok(())239	}240	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241		T::solidity_default(writer, tc)242	}243	fn len(&self) -> usize {244		if T::is_void() {245			0246		} else {247			1248		}249	}250}251252pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);253254impl<T> NamedArgument<T> {255	pub fn new(name: &'static str) -> Self {256		Self(name, Default::default())257	}258}259260impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {261	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {262		if !T::is_void() {263			T::solidity_name(writer, tc)?;264			if !T::is_simple() {265				write!(writer, " memory")?;266			}267			write!(writer, " {}", self.0)268		} else {269			Ok(())270		}271	}272	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {273		writeln!(writer, "\t\t{};", self.0)274	}275	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276		T::solidity_default(writer, tc)277	}278	fn len(&self) -> usize {279		if T::is_void() {280			0281		} else {282			1283		}284	}285}286287pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);288289impl<T> SolidityEventArgument<T> {290	pub fn new(indexed: bool, name: &'static str) -> Self {291		Self(indexed, name, Default::default())292	}293}294295impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {296	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {297		if !T::is_void() {298			T::solidity_name(writer, tc)?;299			if self.0 {300				write!(writer, " indexed")?;301			}302			write!(writer, " {}", self.1)303		} else {304			Ok(())305		}306	}307	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {308		writeln!(writer, "\t\t{};", self.1)309	}310	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311		T::solidity_default(writer, tc)312	}313	fn len(&self) -> usize {314		if T::is_void() {315			0316		} else {317			1318		}319	}320}321322impl SolidityArguments for () {323	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {324		Ok(())325	}326	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {327		Ok(())328	}329	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {330		Ok(())331	}332	fn len(&self) -> usize {333		0334	}335}336337#[impl_for_tuples(1, 12)]338impl SolidityArguments for Tuple {339	for_tuples!( where #( Tuple: SolidityArguments ),* );340341	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {342		let mut first = true;343		for_tuples!( #(344            if !Tuple.is_empty() {345                if !first {346                    write!(writer, ", ")?;347                }348                first = false;349                Tuple.solidity_name(writer, tc)?;350            }351        )* );352		Ok(())353	}354	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {355		for_tuples!( #(356            Tuple.solidity_get(writer)?;357        )* );358		Ok(())359	}360	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {361		if self.is_empty() {362			Ok(())363		} else if self.len() == 1 {364			for_tuples!( #(365				Tuple.solidity_default(writer, tc)?;366			)* );367			Ok(())368		} else {369			write!(writer, "(")?;370			let mut first = true;371			for_tuples!( #(372				if !Tuple.is_empty() {373					if !first {374						write!(writer, ", ")?;375					}376					first = false;377					Tuple.solidity_default(writer, tc)?;378				}379			)* );380			write!(writer, ")")?;381			Ok(())382		}383	}384	fn len(&self) -> usize {385		for_tuples!( #( Tuple.len() )+* )386	}387}388389pub trait SolidityFunctions {390	fn solidity_name(391		&self,392		is_impl: bool,393		writer: &mut impl fmt::Write,394		tc: &TypeCollector,395	) -> fmt::Result;396}397398pub enum SolidityMutability {399	Pure,400	View,401	Mutable,402}403pub struct SolidityFunction<A, R> {404	pub docs: &'static [&'static str],405	pub selector_str: &'static str,406	pub selector: u32,407	pub name: &'static str,408	pub args: A,409	pub result: R,410	pub mutability: SolidityMutability,411}412impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {413	fn solidity_name(414		&self,415		is_impl: bool,416		writer: &mut impl fmt::Write,417		tc: &TypeCollector,418	) -> fmt::Result {419		for doc in self.docs {420			writeln!(writer, "\t///{}", doc)?;421		}422		if !self.docs.is_empty() {423			writeln!(writer, "\t///")?;424		}425		writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;426		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;427		write!(writer, "\tfunction {}(", self.name)?;428		self.args.solidity_name(writer, tc)?;429		write!(writer, ")")?;430		if is_impl {431			write!(writer, " public")?;432		} else {433			write!(writer, " external")?;434		}435		match &self.mutability {436			SolidityMutability::Pure => write!(writer, " pure")?,437			SolidityMutability::View => write!(writer, " view")?,438			SolidityMutability::Mutable => {}439		}440		if !self.result.is_empty() {441			write!(writer, " returns (")?;442			self.result.solidity_name(writer, tc)?;443			write!(writer, ")")?;444		}445		if is_impl {446			writeln!(writer, " {{")?;447			writeln!(writer, "\t\trequire(false, stub_error);")?;448			self.args.solidity_get(writer)?;449			match &self.mutability {450				SolidityMutability::Pure => {}451				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,452				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,453			}454			if !self.result.is_empty() {455				write!(writer, "\t\treturn ")?;456				self.result.solidity_default(writer, tc)?;457				writeln!(writer, ";")?;458			}459			writeln!(writer, "\t}}")?;460		} else {461			writeln!(writer, ";")?;462		}463		Ok(())464	}465}466467#[impl_for_tuples(0, 24)]468impl SolidityFunctions for Tuple {469	for_tuples!( where #( Tuple: SolidityFunctions ),* );470471	fn solidity_name(472		&self,473		is_impl: bool,474		writer: &mut impl fmt::Write,475		tc: &TypeCollector,476	) -> fmt::Result {477		let mut first = false;478		for_tuples!( #(479            Tuple.solidity_name(is_impl, writer, tc)?;480        )* );481		Ok(())482	}483}484485pub struct SolidityInterface<F: SolidityFunctions> {486	pub selector: bytes4,487	pub name: &'static str,488	pub is: &'static [&'static str],489	pub functions: F,490}491492impl<F: SolidityFunctions> SolidityInterface<F> {493	pub fn format(494		&self,495		is_impl: bool,496		out: &mut impl fmt::Write,497		tc: &TypeCollector,498	) -> fmt::Result {499		const ZERO_BYTES: [u8; 4] = [0; 4];500		if self.selector != ZERO_BYTES {501			writeln!(502				out,503				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",504				u32::from_be_bytes(self.selector)505			)?;506		}507		if is_impl {508			write!(out, "contract ")?;509		} else {510			write!(out, "interface ")?;511		}512		write!(out, "{}", self.name)?;513		if !self.is.is_empty() {514			write!(out, " is")?;515			for (i, n) in self.is.iter().enumerate() {516				if i != 0 {517					write!(out, ",")?;518				}519				write!(out, " {}", n)?;520			}521		}522		writeln!(out, " {{")?;523		self.functions.solidity_name(is_impl, out, tc)?;524		writeln!(out, "}}")?;525		Ok(())526	}527}528529pub struct SolidityEvent<A> {530	pub name: &'static str,531	pub args: A,532}533534impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {535	fn solidity_name(536		&self,537		_is_impl: bool,538		writer: &mut impl fmt::Write,539		tc: &TypeCollector,540	) -> fmt::Result {541		write!(writer, "\tevent {}(", self.name)?;542		self.args.solidity_name(writer, tc)?;543		writeln!(writer, ");")544	}545}