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
989 #solidity_functions,989 #solidity_functions,
990 )*),990 )*),
991 };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_generators
999 )*
1000 #(
1001 #solidity_event_generators
1002 )*
1003992
1004 let mut out = string::new();993 let mut out = string::new();
1005 // In solidity interface usage (is) should be preceeded by interface definition
1006 // HACK: this comment helps to sort it in a set
1007 if #solidity_name.starts_with("Inline") {994 if #solidity_name.starts_with("Inline") {
1008 out.push_str("/// @dev inlined interface\n");995 out.push_str("/// @dev inlined interface\n");
1009 }996 }
1010 let _ = interface.format(is_impl, &mut out, tc);997 let _ = interface.format(is_impl, &mut out, tc);
1011 tc.collect(out);998 tc.collect(out);
999 #(
1000 #solidity_event_generators
1001 )*
1002 #(
1003 #solidity_generators
1004 )*
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 }
1012 }1010 }
1013 }1011 }
1014 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {1012 impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
modifiedcrates/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()
 	}
 }