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#[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}