From c0dc82ced77e2d5b0e7a29945ecda11b28ab4d11 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Wed, 13 Nov 2019 14:29:22 +0100 Subject: [PATCH 001/130] Update stale.yml Update config Update .github/workflows/stale.yml Co-Authored-By: Daniel Kirchner --- .github/workflows/stale.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..6904a16e5 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,17 @@ +name: Mark stale pull requests + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-pr-message: "A reminder that this pull request has had no activity for 30 days" + stale-pr-label: "no-pr-activity" + days-before-stale: 14 From 8f2a94bdfff2a971fe51584e44d46fa57277ae88 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 14 Nov 2019 18:24:16 +0100 Subject: [PATCH 002/130] Set version to 0.5.14. --- CMakeLists.txt | 2 +- Changelog.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07f26e291..49b2d19c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.5.13") +set(PROJECT_VERSION "0.5.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/Changelog.md b/Changelog.md index 3ffe69997..9ecd036e1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,15 @@ +### 0.5.14 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + + ### 0.5.13 (2019-11-14) Language Features: From e70ef0b820a7d7b3185549bef50af2f28c36f33a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 Nov 2019 21:29:56 +0100 Subject: [PATCH 003/130] Simplify patterns. --- libevmasm/RuleList.h | 382 ++++++++++++----------- libevmasm/SimplificationRule.h | 172 +++++----- libevmasm/SimplificationRules.cpp | 2 +- libevmasm/SimplificationRules.h | 8 +- libyul/optimiser/SimplificationRules.cpp | 2 +- libyul/optimiser/SimplificationRules.h | 8 +- 6 files changed, 310 insertions(+), 264 deletions(-) diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index e67f614ae..01b2fa963 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -67,47 +67,48 @@ std::vector> simplificationRuleListPart1( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // arithmetic on constants - {{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, - {{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, - {{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, - {{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, - {{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, - {{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, - {{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false}, - {{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, - {{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, - {{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, - {{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false}, - {{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false}, - {{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false}, - {{Pattern::Builtins::BYTE, {A, B}}, [=]{ + {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, + {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, + {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, + {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, + {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, + {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, + {Builtins::NOT(A), [=]{ return ~A.d(); }, false}, + {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, + {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, + {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, + {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, + {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, + {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, + {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, + {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, + {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, + {Builtins::BYTE(A, B), [=]{ return A.d() >= Pattern::WordSize / 8 ? 0 : (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; }, false}, - {{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, - {{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, - {{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word { + {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, + {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, + {Builtins::SIGNEXTEND(A, B), [=]() -> Word { if (A.d() >= Pattern::WordSize / 8 - 1) return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; Word mask = (Word(1) << testBit) - 1; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; }, false}, - {{Pattern::Builtins::SHL, {A, B}}, [=]{ + {Builtins::SHL(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return shlWorkaround(B.d(), unsigned(A.d())); }, false}, - {{Pattern::Builtins::SHR, {A, B}}, [=]{ + {Builtins::SHR(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return B.d() >> unsigned(A.d()); @@ -115,6 +116,7 @@ std::vector> simplificationRuleListPart1( }; } + template std::vector> simplificationRuleListPart2( Pattern, @@ -125,50 +127,51 @@ std::vector> simplificationRuleListPart2( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // invariants involving known constants - {{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false}, - {{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, - {{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, - {{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, - {{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, - {{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, - {{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, - {{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false}, - {{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false} + {Builtins::ADD(X, 0), [=]{ return X; }, false}, + {Builtins::ADD(0, X), [=]{ return X; }, false}, + {Builtins::SUB(X, 0), [=]{ return X; }, false}, + {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, + {Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, + {Builtins::MUL(0, X), [=]{ return Word(0); }, true}, + {Builtins::MUL(X, 1), [=]{ return X; }, false}, + {Builtins::MUL(1, X), [=]{ return X; }, false}, + {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, + {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, + {Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, + {Builtins::DIV(0, X), [=]{ return Word(0); }, true}, + {Builtins::DIV(X, 1), [=]{ return X; }, false}, + {Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, + {Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, + {Builtins::SDIV(X, 1), [=]{ return X; }, false}, + {Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, + {Builtins::AND(~Word(0), X), [=]{ return X; }, false}, + {Builtins::AND(X, 0), [=]{ return Word(0); }, true}, + {Builtins::AND(0, X), [=]{ return Word(0); }, true}, + {Builtins::OR(X, 0), [=]{ return X; }, false}, + {Builtins::OR(0, X), [=]{ return X; }, false}, + {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, + {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, + {Builtins::XOR(X, 0), [=]{ return X; }, false}, + {Builtins::XOR(0, X), [=]{ return X; }, false}, + {Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, + {Builtins::MOD(0, X), [=]{ return Word(0); }, true}, + {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, + {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, + {Builtins::SHL(0, X), [=]{ return X; }, false}, + {Builtins::SHR(0, X), [=]{ return X; }, false}, + {Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, + {Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, + {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, + {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, + {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, + {Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, + {Builtins::GT(0, X), [=]{ return Word(0); }, true}, + {Builtins::LT(X, 0), [=]{ return Word(0); }, true}, + {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, + {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} }; } @@ -182,18 +185,19 @@ std::vector> simplificationRuleListPart3( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // operations involving an expression and itself - {{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true}, - {{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true} + {Builtins::AND(X, X), [=]{ return X; }, true}, + {Builtins::OR(X, X), [=]{ return X; }, true}, + {Builtins::XOR(X, X), [=]{ return Word(0); }, true}, + {Builtins::SUB(X, X), [=]{ return Word(0); }, true}, + {Builtins::EQ(X, X), [=]{ return Word(1); }, true}, + {Builtins::LT(X, X), [=]{ return Word(0); }, true}, + {Builtins::SLT(X, X), [=]{ return Word(0); }, true}, + {Builtins::GT(X, X), [=]{ return Word(0); }, true}, + {Builtins::SGT(X, X), [=]{ return Word(0); }, true}, + {Builtins::MOD(X, X), [=]{ return Word(0); }, true} }; } @@ -207,25 +211,26 @@ std::vector> simplificationRuleListPart4( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // logical instruction combinations - {{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false}, - {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true}, + {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, + {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, + {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, + {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, + {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, + {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, + {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, + {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, + {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, + {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, + {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, + {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, + {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, + {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, }; } @@ -240,6 +245,7 @@ std::vector> simplificationRuleListPart5( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; std::vector> rules; @@ -248,15 +254,15 @@ std::vector> simplificationRuleListPart5( { Word value = Word(1) << i; rules.push_back({ - {Pattern::Builtins::MOD, {X, value}}, - [=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; }, + Builtins::MOD(X, value), + [=]() -> Pattern { return Builtins::AND(X, value - 1); }, false }); } // Replace SHL >=256, X with 0 rules.push_back({ - {Pattern::Builtins::SHL, {A, X}}, + Builtins::SHL(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize; } @@ -264,7 +270,7 @@ std::vector> simplificationRuleListPart5( // Replace SHR >=256, X with 0 rules.push_back({ - {Pattern::Builtins::SHR, {A, X}}, + Builtins::SHR(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize; } @@ -272,29 +278,29 @@ std::vector> simplificationRuleListPart5( // Replace BYTE(A, X), A >= 32 with 0 rules.push_back({ - {Pattern::Builtins::BYTE, {A, X}}, + Builtins::BYTE(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize / 8; } }); - for (auto const& op: std::vector{ - Pattern::Builtins::ADDRESS, - Pattern::Builtins::CALLER, - Pattern::Builtins::ORIGIN, - Pattern::Builtins::COINBASE + for (auto instr: { + Instruction::ADDRESS, + Instruction::CALLER, + Instruction::ORIGIN, + Instruction::COINBASE }) { assertThrow(Pattern::WordSize > 160, OptimizerException, ""); Word const mask = (Word(1) << 160) - 1; rules.push_back({ - {Pattern::Builtins::AND, {{op, mask}}}, - [=]() -> Pattern { return op; }, + Builtins::AND(Pattern{instr}, mask), + [=]() -> Pattern { return {instr}; }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, op}}}, - [=]() -> Pattern { return op; }, + Builtins::AND(mask, Pattern{instr}), + [=]() -> Pattern { return {instr}; }, false }); } @@ -311,30 +317,35 @@ std::vector> simplificationRuleListPart6( Pattern Y ) { + using Builtins = typename Pattern::Builtins; + std::vector> rules; // Double negation of opcodes with boolean result - for (auto const& op: std::vector{ - Pattern::Builtins::EQ, - Pattern::Builtins::LT, - Pattern::Builtins::SLT, - Pattern::Builtins::GT, - Pattern::Builtins::SGT + for (auto instr: { + Instruction::EQ, + Instruction::LT, + Instruction::SLT, + Instruction::GT, + Instruction::SGT }) + { + typename Builtins::PatternGeneratorInstance op{instr}; rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}}, - [=]() -> Pattern { return {op, {X, Y}}; }, + Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), + [=]() -> Pattern { return op(X, Y); }, false }); + } rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, + Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), + [=]() -> Pattern { return Builtins::ISZERO(X); }, false }); rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}}, - [=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; }, + Builtins::ISZERO(Builtins::XOR(X, Y)), + [=]() -> Pattern { return Builtins::EQ(X, Y); }, false }); @@ -351,42 +362,44 @@ std::vector> simplificationRuleListPart7( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; + std::vector> rules; // Associative operations - for (auto const& opFun: std::vector>>{ - {Pattern::Builtins::ADD, std::plus()}, - {Pattern::Builtins::MUL, std::multiplies()}, - {Pattern::Builtins::AND, std::bit_and()}, - {Pattern::Builtins::OR, std::bit_or()}, - {Pattern::Builtins::XOR, std::bit_xor()} + for (auto&& instrAndFunc: std::vector>>{ + {Instruction::ADD, std::plus()}, + {Instruction::MUL, std::multiplies()}, + {Instruction::AND, std::bit_and()}, + {Instruction::OR, std::bit_or()}, + {Instruction::XOR, std::bit_xor()} }) { - auto op = opFun.first; - auto fun = opFun.second; + typename Builtins::PatternGeneratorInstance op{instrAndFunc.first}; + std::function fun = instrAndFunc.second; // Moving constants to the outside, order matters here - we first add rules // for constants and then for non-constants. // xa can be (X, A) or (A, X) - for (auto xa: {std::vector{X, A}, std::vector{A, X}}) + for (auto const& opXA: {op(X, A), op(A, X)}) { rules += std::vector>{{ // (X+A)+B -> X+(A+B) - {op, {{op, xa}, B}}, - [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, + op(opXA, B), + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, false }, { // (X+A)+Y -> (X+Y)+A - {op, {{op, xa}, Y}}, - [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }, + op(opXA, Y), + [=]() -> Pattern { return op(op(X, Y), A); }, false }, { // B+(X+A) -> X+(A+B) - {op, {B, {op, xa}}}, - [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, + op(B, opXA), + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, false }, { // Y+(X+A) -> (Y+X)+A - {op, {Y, {op, xa}}}, - [=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; }, + op(Y, opXA), + [=]() -> Pattern { return op(op(Y, X), A); }, false }}; } @@ -395,13 +408,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHL by constant rules.push_back({ // SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X) - {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, + Builtins::SHL(B, Builtins::SHL(A, X)), [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); if (sum >= Pattern::WordSize) - return {Pattern::Builtins::AND, {X, Word(0)}}; + return Builtins::AND(X, Word(0)); else - return {Pattern::Builtins::SHL, {Word(sum), X}}; + return Builtins::SHL(Word(sum), X); }, false }); @@ -409,13 +422,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHR by constant rules.push_back({ // SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X) - {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, + Builtins::SHR(B, Builtins::SHR(A, X)), [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); if (sum >= Pattern::WordSize) - return {Pattern::Builtins::AND, {X, Word(0)}}; + return Builtins::AND(X, Word(0)); else - return {Pattern::Builtins::SHR, {Word(sum), X}}; + return Builtins::SHR(Word(sum), X); }, false }); @@ -423,16 +436,16 @@ std::vector> simplificationRuleListPart7( // Combine SHL-SHR by constant rules.push_back({ // SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, + Builtins::SHR(B, Builtins::SHL(A, X)), [=]() -> Pattern { Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d()); if (A.d() > B.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHL(A.d() - B.d(), X), mask); else if (B.d() > A.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHR(B.d() - A.d(), X), mask); else - return {Pattern::Builtins::AND, {X, mask}}; + return Builtins::AND(X, mask); }, false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } @@ -441,41 +454,42 @@ std::vector> simplificationRuleListPart7( // Combine SHR-SHL by constant rules.push_back({ // SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, + Builtins::SHL(B, Builtins::SHR(A, X)), [=]() -> Pattern { Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d())); if (A.d() > B.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHR(A.d() - B.d(), X), mask); else if (B.d() > A.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHL(B.d() - A.d(), X), mask); else - return {Pattern::Builtins::AND, {X, mask}}; + return Builtins::AND(X, mask); }, false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); // Move AND with constant across SHL and SHR by constant - for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR}) + for (auto instr: {Instruction::SHL, Instruction::SHR}) { + typename Builtins::PatternGeneratorInstance shiftOp{instr}; auto replacement = [=]() -> Pattern { Word mask = - shiftOp == Pattern::Builtins::SHL ? + instr == Instruction::SHL ? shlWorkaround(A.d(), unsigned(B.d())) : A.d() >> unsigned(B.d()); - return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}}; + return Builtins::AND(shiftOp(B.d(), X), std::move(mask)); }; rules.push_back({ // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}}, + shiftOp(B, Builtins::AND(X, A)), replacement, false, [=] { return B.d() < Pattern::WordSize; } }); rules.push_back({ // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}}, + shiftOp(B, Builtins::AND(A, X)), replacement, false, [=] { return B.d() < Pattern::WordSize; } @@ -484,27 +498,27 @@ std::vector> simplificationRuleListPart7( rules.push_back({ // MUL(X, SHL(Y, 1)) -> SHL(Y, X) - {Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, + Builtins::MUL(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { - return {Pattern::Builtins::SHL, {Y, X}}; + return Builtins::SHL(Y, X); }, // Actually only changes the order, does not remove. true }); rules.push_back({ // MUL(SHL(X, 1), Y) -> SHL(X, Y) - {Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}}, + Builtins::MUL(Builtins::SHL(X, Word(1)), Y), [=]() -> Pattern { - return {Pattern::Builtins::SHL, {X, Y}}; + return Builtins::SHL(X, Y); }, false }); rules.push_back({ // DIV(X, SHL(Y, 1)) -> SHR(Y, X) - {Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, + Builtins::DIV(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { - return {Pattern::Builtins::SHR, {Y, X}}; + return Builtins::SHR(Y, X); }, // Actually only changes the order, does not remove. true @@ -519,16 +533,16 @@ std::vector> simplificationRuleListPart7( rules.push_back({ // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) - {Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}}, - [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, + Builtins::AND(A, Builtins::SHR(B, X)), + [=]() -> Pattern { return Builtins::SHR(B, X); }, false, feasibilityFunction }); rules.push_back({ // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) - {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}}, - [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, + Builtins::AND(Builtins::SHR(B, X), A), + [=]() -> Pattern { return Builtins::SHR(B, X); }, false, feasibilityFunction }); @@ -545,34 +559,35 @@ std::vector> simplificationRuleListPart8( Pattern Y ) { + using Builtins = typename Pattern::Builtins; std::vector> rules; // move constants across subtractions rules += std::vector>{ { // X - A -> X + (-A) - {Pattern::Builtins::SUB, {X, A}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; }, + Builtins::SUB(X, A), + [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, false }, { // (X + A) - Y -> (X - Y) + A - {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, + Builtins::SUB(Builtins::ADD(X, A), Y), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, false }, { // (A + X) - Y -> (X - Y) + A - {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, + Builtins::SUB(Builtins::ADD(A, X), Y), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, false }, { // X - (Y + A) -> (X - Y) + (-A) - {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, + Builtins::SUB(X, Builtins::ADD(Y, A)), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, false }, { // X - (A + Y) -> (X - Y) + (-A) - {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, + Builtins::SUB(X, Builtins::ADD(A, Y)), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, false } }; @@ -591,30 +606,31 @@ std::vector> simplificationRuleListPart9( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; std::vector> rules; assertThrow(Pattern::WordSize > 160, OptimizerException, ""); Word const mask = (Word(1) << 160) - 1; // CREATE rules.push_back({ - {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, + Builtins::AND(Builtins::CREATE(W, X, Y), mask), + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, + Builtins::AND(mask, Builtins::CREATE(W, X, Y)), + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, false }); // CREATE2 rules.push_back({ - {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, + Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask), + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, + Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)), + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, false }); diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index b35b89551..f12b859cd 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include namespace dev @@ -55,84 +56,105 @@ struct SimplificationRule std::function feasible; }; +template struct EVMBuiltins { using InstrType = Instruction; - static auto constexpr STOP = Instruction::STOP; - static auto constexpr ADD = Instruction::ADD; - static auto constexpr SUB = Instruction::SUB; - static auto constexpr MUL = Instruction::MUL; - static auto constexpr DIV = Instruction::DIV; - static auto constexpr SDIV = Instruction::SDIV; - static auto constexpr MOD = Instruction::MOD; - static auto constexpr SMOD = Instruction::SMOD; - static auto constexpr EXP = Instruction::EXP; - static auto constexpr NOT = Instruction::NOT; - static auto constexpr LT = Instruction::LT; - static auto constexpr GT = Instruction::GT; - static auto constexpr SLT = Instruction::SLT; - static auto constexpr SGT = Instruction::SGT; - static auto constexpr EQ = Instruction::EQ; - static auto constexpr ISZERO = Instruction::ISZERO; - static auto constexpr AND = Instruction::AND; - static auto constexpr OR = Instruction::OR; - static auto constexpr XOR = Instruction::XOR; - static auto constexpr BYTE = Instruction::BYTE; - static auto constexpr SHL = Instruction::SHL; - static auto constexpr SHR = Instruction::SHR; - static auto constexpr SAR = Instruction::SAR; - static auto constexpr ADDMOD = Instruction::ADDMOD; - static auto constexpr MULMOD = Instruction::MULMOD; - static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND; - static auto constexpr KECCAK256 = Instruction::KECCAK256; - static auto constexpr ADDRESS = Instruction::ADDRESS; - static auto constexpr BALANCE = Instruction::BALANCE; - static auto constexpr ORIGIN = Instruction::ORIGIN; - static auto constexpr CALLER = Instruction::CALLER; - static auto constexpr CALLVALUE = Instruction::CALLVALUE; - static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD; - static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE; - static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY; - static auto constexpr CODESIZE = Instruction::CODESIZE; - static auto constexpr CODECOPY = Instruction::CODECOPY; - static auto constexpr GASPRICE = Instruction::GASPRICE; - static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE; - static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY; - static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE; - static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY; - static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH; - static auto constexpr BLOCKHASH = Instruction::BLOCKHASH; - static auto constexpr COINBASE = Instruction::COINBASE; - static auto constexpr TIMESTAMP = Instruction::TIMESTAMP; - static auto constexpr NUMBER = Instruction::NUMBER; - static auto constexpr DIFFICULTY = Instruction::DIFFICULTY; - static auto constexpr GASLIMIT = Instruction::GASLIMIT; - static auto constexpr CHAINID = Instruction::CHAINID; - static auto constexpr SELFBALANCE = Instruction::SELFBALANCE; - static auto constexpr POP = Instruction::POP; - static auto constexpr MLOAD = Instruction::MLOAD; - static auto constexpr MSTORE = Instruction::MSTORE; - static auto constexpr MSTORE8 = Instruction::MSTORE8; - static auto constexpr SLOAD = Instruction::SLOAD; - static auto constexpr SSTORE = Instruction::SSTORE; - static auto constexpr PC = Instruction::PC; - static auto constexpr MSIZE = Instruction::MSIZE; - static auto constexpr GAS = Instruction::GAS; - static auto constexpr LOG0 = Instruction::LOG0; - static auto constexpr LOG1 = Instruction::LOG1; - static auto constexpr LOG2 = Instruction::LOG2; - static auto constexpr LOG3 = Instruction::LOG3; - static auto constexpr LOG4 = Instruction::LOG4; - static auto constexpr CREATE = Instruction::CREATE; - static auto constexpr CALL = Instruction::CALL; - static auto constexpr CALLCODE = Instruction::CALLCODE; - static auto constexpr STATICCALL = Instruction::STATICCALL; - static auto constexpr RETURN = Instruction::RETURN; - static auto constexpr DELEGATECALL = Instruction::DELEGATECALL; - static auto constexpr CREATE2 = Instruction::CREATE2; - static auto constexpr REVERT = Instruction::REVERT; - static auto constexpr INVALID = Instruction::INVALID; - static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT; + + template + struct PatternGenerator + { + template constexpr Pattern operator()(Args&&... _args) const + { + return {inst, {std::forward(_args)...}}; + }; + }; + + struct PatternGeneratorInstance + { + Instruction instruction; + template constexpr Pattern operator()(Args&&... _args) const + { + return {instruction, {std::forward(_args)...}}; + }; + }; + + + static auto constexpr STOP = PatternGenerator{}; + static auto constexpr ADD = PatternGenerator{}; + static auto constexpr SUB = PatternGenerator{}; + static auto constexpr MUL = PatternGenerator{}; + static auto constexpr DIV = PatternGenerator{}; + static auto constexpr SDIV = PatternGenerator{}; + static auto constexpr MOD = PatternGenerator{}; + static auto constexpr SMOD = PatternGenerator{}; + static auto constexpr EXP = PatternGenerator{}; + static auto constexpr NOT = PatternGenerator{}; + static auto constexpr LT = PatternGenerator{}; + static auto constexpr GT = PatternGenerator{}; + static auto constexpr SLT = PatternGenerator{}; + static auto constexpr SGT = PatternGenerator{}; + static auto constexpr EQ = PatternGenerator{}; + static auto constexpr ISZERO = PatternGenerator{}; + static auto constexpr AND = PatternGenerator{}; + static auto constexpr OR = PatternGenerator{}; + static auto constexpr XOR = PatternGenerator{}; + static auto constexpr BYTE = PatternGenerator{}; + static auto constexpr SHL = PatternGenerator{}; + static auto constexpr SHR = PatternGenerator{}; + static auto constexpr SAR = PatternGenerator{}; + static auto constexpr ADDMOD = PatternGenerator{}; + static auto constexpr MULMOD = PatternGenerator{}; + static auto constexpr SIGNEXTEND = PatternGenerator{}; + static auto constexpr KECCAK256 = PatternGenerator{}; + static auto constexpr ADDRESS = PatternGenerator{}; + static auto constexpr BALANCE = PatternGenerator{}; + static auto constexpr ORIGIN = PatternGenerator{}; + static auto constexpr CALLER = PatternGenerator{}; + static auto constexpr CALLVALUE = PatternGenerator{}; + static auto constexpr CALLDATALOAD = PatternGenerator{}; + static auto constexpr CALLDATASIZE = PatternGenerator{}; + static auto constexpr CALLDATACOPY = PatternGenerator{}; + static auto constexpr CODESIZE = PatternGenerator{}; + static auto constexpr CODECOPY = PatternGenerator{}; + static auto constexpr GASPRICE = PatternGenerator{}; + static auto constexpr EXTCODESIZE = PatternGenerator{}; + static auto constexpr EXTCODECOPY = PatternGenerator{}; + static auto constexpr RETURNDATASIZE = PatternGenerator{}; + static auto constexpr RETURNDATACOPY = PatternGenerator{}; + static auto constexpr EXTCODEHASH = PatternGenerator{}; + static auto constexpr BLOCKHASH = PatternGenerator{}; + static auto constexpr COINBASE = PatternGenerator{}; + static auto constexpr TIMESTAMP = PatternGenerator{}; + static auto constexpr NUMBER = PatternGenerator{}; + static auto constexpr DIFFICULTY = PatternGenerator{}; + static auto constexpr GASLIMIT = PatternGenerator{}; + static auto constexpr CHAINID = PatternGenerator{}; + static auto constexpr SELFBALANCE = PatternGenerator{}; + static auto constexpr POP = PatternGenerator{}; + static auto constexpr MLOAD = PatternGenerator{}; + static auto constexpr MSTORE = PatternGenerator{}; + static auto constexpr MSTORE8 = PatternGenerator{}; + static auto constexpr SLOAD = PatternGenerator{}; + static auto constexpr SSTORE = PatternGenerator{}; + static auto constexpr PC = PatternGenerator{}; + static auto constexpr MSIZE = PatternGenerator{}; + static auto constexpr GAS = PatternGenerator{}; + static auto constexpr LOG0 = PatternGenerator{}; + static auto constexpr LOG1 = PatternGenerator{}; + static auto constexpr LOG2 = PatternGenerator{}; + static auto constexpr LOG3 = PatternGenerator{}; + static auto constexpr LOG4 = PatternGenerator{}; + static auto constexpr CREATE = PatternGenerator{}; + static auto constexpr CALL = PatternGenerator{}; + static auto constexpr CALLCODE = PatternGenerator{}; + static auto constexpr STATICCALL = PatternGenerator{}; + static auto constexpr RETURN = PatternGenerator{}; + static auto constexpr DELEGATECALL = PatternGenerator{}; + static auto constexpr CREATE2 = PatternGenerator{}; + static auto constexpr REVERT = PatternGenerator{}; + static auto constexpr INVALID = PatternGenerator{}; + static auto constexpr SELFDESTRUCT = PatternGenerator{}; }; } diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index ca201862a..28b64078a 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -99,7 +99,7 @@ Rules::Rules() assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); } -Pattern::Pattern(Instruction _instruction, std::vector const& _arguments): +Pattern::Pattern(Instruction _instruction, std::initializer_list _arguments): m_type(Operation), m_instruction(_instruction), m_arguments(_arguments) diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index a8c782b26..a01507411 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -26,6 +26,8 @@ #include #include +#include + #include #include @@ -87,18 +89,20 @@ public: using Expression = ExpressionClasses::Expression; using Id = ExpressionClasses::Id; - using Builtins = dev::eth::EVMBuiltins; + using Builtins = dev::eth::EVMBuiltins; static constexpr size_t WordSize = 256; using Word = u256; // Matches a specific constant value. Pattern(unsigned _value): Pattern(u256(_value)) {} + Pattern(int _value): Pattern(u256(_value)) {} + Pattern(long unsigned _value): Pattern(u256(_value)) {} // Matches a specific constant value. Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(std::make_shared(_value)) {} // Matches a specific assembly item type or anything if not given. Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {} // Matches a given instruction with given arguments - Pattern(Instruction _instruction, std::vector const& _arguments = {}); + Pattern(Instruction _instruction, std::initializer_list _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 96f225f8c..0575ac578 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -113,7 +113,7 @@ SimplificationRules::SimplificationRules() assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); } -yul::Pattern::Pattern(dev::eth::Instruction _instruction, vector const& _arguments): +yul::Pattern::Pattern(dev::eth::Instruction _instruction, initializer_list _arguments): m_kind(PatternKind::Operation), m_instruction(_instruction), m_arguments(_arguments) diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 1caf323d8..d07807d89 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -85,7 +87,7 @@ enum class PatternKind class Pattern { public: - using Builtins = dev::eth::EVMBuiltins; + using Builtins = dev::eth::EVMBuiltins; static constexpr size_t WordSize = 256; using Word = dev::u256; @@ -93,10 +95,12 @@ public: Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} // Matches a specific constant value. Pattern(unsigned _value): Pattern(dev::u256(_value)) {} + Pattern(int _value): Pattern(dev::u256(_value)) {} + Pattern(long unsigned _value): Pattern(dev::u256(_value)) {} // Matches a specific constant value. Pattern(dev::u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared(_value)) {} // Matches a given instruction with given arguments - Pattern(dev::eth::Instruction _instruction, std::vector const& _arguments = {}); + Pattern(dev::eth::Instruction _instruction, std::initializer_list _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. From a0ae36ba70117ccfe02a3f8529d294ce169b93f1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 14 Nov 2019 15:43:02 +0100 Subject: [PATCH 004/130] Add selector member to public and external library functions. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 14 ++++++++ .../library_delegatecall_guard_pure.sol | 32 +++++++++++++++++++ ...library_delegatecall_guard_view_needed.sol | 32 +++++++++++++++++++ ...ary_delegatecall_guard_view_not_needed.sol | 31 ++++++++++++++++++ ...ary_delegatecall_guard_view_staticcall.sol | 31 ++++++++++++++++++ .../libraries/library_function_selectors.sol | 30 +++++++++++++++++ .../library_function_selectors_struct.sol | 27 ++++++++++++++++ .../types/library_function_selector.sol | 13 ++++++++ .../library_function_selector_internal.sol | 10 ++++++ ...brary_function_selector_private_inside.sol | 8 +++++ ...rary_function_selector_private_outside.sol | 10 ++++++ .../library_function_selector_view_pure.sol | 10 ++++++ 13 files changed, 249 insertions(+) create mode 100644 test/libsolidity/semanticTests/libraries/library_delegatecall_guard_pure.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_needed.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_not_needed.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_staticcall.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_function_selectors.sol create mode 100644 test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol create mode 100644 test/libsolidity/syntaxTests/types/library_function_selector.sol create mode 100644 test/libsolidity/syntaxTests/types/library_function_selector_internal.sol create mode 100644 test/libsolidity/syntaxTests/types/library_function_selector_private_inside.sol create mode 100644 test/libsolidity/syntaxTests/types/library_function_selector_private_outside.sol create mode 100644 test/libsolidity/syntaxTests/types/library_function_selector_view_pure.sol diff --git a/Changelog.md b/Changelog.md index 9ecd036e1..4232e2c77 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.5.14 (unreleased) Language Features: + * Allow to obtain the selector of public or external library functions via a member ``.selector``. Compiler Features: diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f5e315829..5bd3b87ea 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2962,6 +2962,20 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con ); return members; } + case Kind::DelegateCall: + { + auto const* functionDefinition = dynamic_cast(m_declaration); + solAssert(functionDefinition, ""); + solAssert(functionDefinition->visibility() != Declaration::Visibility::Private, ""); + if (functionDefinition->visibility() != Declaration::Visibility::Internal) + { + auto const* contract = dynamic_cast(m_declaration->scope()); + solAssert(contract, ""); + solAssert(contract->isLibrary(), ""); + return {{"selector", TypeProvider::fixedBytes(4)}}; + } + return {}; + } default: return MemberList::MemberMap(); } diff --git a/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_pure.sol b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_pure.sol new file mode 100644 index 000000000..902ca66c1 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_pure.sol @@ -0,0 +1,32 @@ +library L { + function f(uint256[] storage x) public pure returns (uint256) { + return 23; + } +} +contract C { + uint256[] y; + string x; + constructor() public { y.push(42); } + function f() public view returns (uint256) { + return L.f(y); + } + function g() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } + function h() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> 23 +// g() -> true, 23 +// h() -> true, 23 diff --git a/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_needed.sol b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_needed.sol new file mode 100644 index 000000000..b9254c811 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_needed.sol @@ -0,0 +1,32 @@ +library L { + function f(uint256[] storage x) public view returns (uint256) { + return x.length; + } +} +contract C { + uint256[] y; + string x; + constructor() public { y.push(42); } + function f() public view returns (uint256) { + return L.f(y); + } + function g() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } + function h() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> 1 +// g() -> true, 1 +// h() -> true, 0 # this is bad - this should fail! # diff --git a/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_not_needed.sol b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_not_needed.sol new file mode 100644 index 000000000..450f74f1d --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_not_needed.sol @@ -0,0 +1,31 @@ +library L { + function f(uint256[] storage x) public view returns (uint256) { + return 84; + } +} +contract C { + uint256[] y; + constructor() public { y.push(42); } + function f() public view returns (uint256) { + return L.f(y); + } + function g() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } + function h() public returns (bool, uint256) { + uint256 ys; + assembly { ys := y_slot } + (bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, ys)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> 84 +// g() -> true, 84 +// h() -> true, 84 diff --git a/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_staticcall.sol b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_staticcall.sol new file mode 100644 index 000000000..dc394f7fa --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_delegatecall_guard_view_staticcall.sol @@ -0,0 +1,31 @@ +contract D { + uint public x; + constructor() public { x = 42; } +} +library L { + function f(D d) public view returns (uint256) { + return d.x(); + } +} +contract C { + D d; + constructor() public { d = new D(); } + function f() public view returns (uint256) { + return L.f(d); + } + function g() public returns (bool, uint256) { + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, d)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } + function h() public returns (bool, uint256) { + (bool success, bytes memory data) = address(L).call(abi.encodeWithSelector(L.f.selector, d)); + return (success, success ? abi.decode(data,(uint256)) : 0); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> 42 +// g() -> true, 42 +// h() -> true, 42 diff --git a/test/libsolidity/semanticTests/libraries/library_function_selectors.sol b/test/libsolidity/semanticTests/libraries/library_function_selectors.sol new file mode 100644 index 000000000..305ccf39b --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_function_selectors.sol @@ -0,0 +1,30 @@ +library L { + function f(uint256 x) external returns (uint) { return x; } + function g(uint256[] storage s) external returns (uint) { return s.length; } + function h(uint256[] memory m) public returns (uint) { return m.length; } +} +contract C { + uint256[] s; + constructor() public { while (s.length < 42) s.push(0); } + function f() public returns (bool, bool, uint256) { + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, 7)); + return (L.f.selector == bytes4(keccak256("f(uint256)")), success, abi.decode(data, (uint256))); + } + function g() public returns (bool, bool, uint256) { + uint256 s_ptr; + assembly { s_ptr := s_slot } + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.g.selector, s_ptr)); + return (L.g.selector == bytes4(keccak256("g(uint256[] storage)")), success, abi.decode(data, (uint256))); + } + function h() public returns (bool, bool, uint256) { + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.h.selector, new uint256[](23))); + return (L.h.selector == bytes4(keccak256("h(uint256[])")), success, abi.decode(data, (uint256))); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> true, true, 7 +// g() -> true, true, 42 +// h() -> true, true, 23 diff --git a/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol b/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol new file mode 100644 index 000000000..f0c71dec7 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol @@ -0,0 +1,27 @@ +pragma experimental ABIEncoderV2; +library L { + struct S { uint256 a; } + function f(S storage s) external returns (uint) { return s.a; } + function g(S memory m) public returns (uint) { return m.a; } +} +contract C { + L.S s; + constructor() public { s.a = 42; } + + function f() public returns (bool, bool, uint256) { + uint256 s_ptr; + assembly { s_ptr := s_slot } + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.f.selector, s_ptr)); + return (L.f.selector == bytes4(keccak256("f(L.S storage)")), success, abi.decode(data, (uint256))); + } + function g() public returns (bool, bool, uint256) { + (bool success, bytes memory data) = address(L).delegatecall(abi.encodeWithSelector(L.g.selector, L.S(23))); + return (L.g.selector == bytes4(keccak256("g(L.S)")), success, abi.decode(data, (uint256))); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// library: L +// f() -> true, true, 42 +// g() -> true, true, 23 diff --git a/test/libsolidity/syntaxTests/types/library_function_selector.sol b/test/libsolidity/syntaxTests/types/library_function_selector.sol new file mode 100644 index 000000000..b24f134a6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_function_selector.sol @@ -0,0 +1,13 @@ +library L { + function f(uint256) external {} + function g(uint256[] storage) external {} + function h(uint256[] memory) public {} +} +contract C { + function f() public pure returns (bytes4 a, bytes4 b, bytes4 c, bytes4 d) { + a = L.f.selector; + b = L.g.selector; + c = L.h.selector; + d = L.h.selector; + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/library_function_selector_internal.sol b/test/libsolidity/syntaxTests/types/library_function_selector_internal.sol new file mode 100644 index 000000000..8f155867e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_function_selector_internal.sol @@ -0,0 +1,10 @@ +library L { + function f(uint256) internal {} +} +contract C { + function f() public pure returns (bytes4) { + return L.f.selector; + } +} +// ---- +// TypeError: (126-138): Member "selector" not found or not visible after argument-dependent lookup in function (uint256). diff --git a/test/libsolidity/syntaxTests/types/library_function_selector_private_inside.sol b/test/libsolidity/syntaxTests/types/library_function_selector_private_inside.sol new file mode 100644 index 000000000..825953196 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_function_selector_private_inside.sol @@ -0,0 +1,8 @@ +library L { + function f(uint256) private {} + function g(uint256) public returns (uint256) { + return f.selector; + } +} +// ---- +// TypeError: (113-123): Member "selector" not found or not visible after argument-dependent lookup in function (uint256). diff --git a/test/libsolidity/syntaxTests/types/library_function_selector_private_outside.sol b/test/libsolidity/syntaxTests/types/library_function_selector_private_outside.sol new file mode 100644 index 000000000..b5785f88f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_function_selector_private_outside.sol @@ -0,0 +1,10 @@ +library L { + function f(uint256) private {} +} +contract C { + function f() public pure returns (bytes4) { + return L.f.selector; + } +} +// ---- +// TypeError: (125-128): Member "f" not found or not visible after argument-dependent lookup in type(library L). diff --git a/test/libsolidity/syntaxTests/types/library_function_selector_view_pure.sol b/test/libsolidity/syntaxTests/types/library_function_selector_view_pure.sol new file mode 100644 index 000000000..02ace71f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_function_selector_view_pure.sol @@ -0,0 +1,10 @@ +library L { + function f(uint256) external pure {} + function g(uint256) external view {} +} +contract C { + function f() public pure returns (bytes4, bytes4) { + return (L.f.selector, L.g.selector); + } +} +// ---- From 9115a0f52521445e5b0bb91e68e62de683e84e27 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 14 Nov 2019 17:56:10 +0100 Subject: [PATCH 005/130] Some documentation. --- docs/contracts/libraries.rst | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index bfeebdb77..2e77af746 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -208,6 +208,48 @@ Restrictions for libraries in comparison to contracts: (These might be lifted at a later point.) +.. _library-selectors: + +Function Signatures and Selectors in Libraries +============================================== + +While external calls to public or external library functions are possible, the calling convention for such calls +is considered to be internal to Solidity and not the same as specified for the regular :ref:`contract ABI`. +External library functions support more argument types than external contract functions, for example recursive structs +and storage pointers. For that reason, the function signatures used to compute the 4-byte selector are computed +following an internal naming schema and arguments of types not supported in the contract ABI use an internal encoding. + +The following identifiers are used for the types in the signatures: + + - Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI. + - Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and + ``[M]`` for fixed-size arrays of ``M`` elements. + - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. + - Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space + followed by ``storage`` to it. + +The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a +``uint256`` value referring to the storage slot to which they point. + +Similarly to the contract ABI, the selector consists of the first four bytes of the Keccak256-hash of the signature. +Its value can be obtained from Solidity using the ``.selector`` member as follows: + +:: + + pragma solidity >0.5.13 <0.7.0; + + library L { + function f(uint256) external {} + } + + contract C { + function g() public pure returns (bytes4) { + return L.f.selector; + } + } + + + .. _call-protection: Call Protection For Libraries From 505b5deaf92b3ca787bb43445d2d12e17530bee8 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 12 Nov 2019 13:14:07 +0100 Subject: [PATCH 006/130] Add chainid and selfbalance opcodes to yul proto converter and enable it in the yul proto differential fuzzer. --- test/tools/ossfuzz/protoToYul.cpp | 6 ++++++ test/tools/ossfuzz/yulProto.proto | 2 ++ test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 6 +++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 345797afb..6fe9c2452 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -437,6 +437,12 @@ void ProtoConverter::visit(NullaryOp const& _x) case NullaryOp::GASLIMIT: m_output << "gaslimit()"; break; + case NullaryOp::SELFBALANCE: + m_output << "selfbalance()"; + break; + case NullaryOp::CHAINID: + m_output << "chainid()"; + break; } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 6871a63ac..b7ce8c57a 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -237,6 +237,8 @@ message NullaryOp { NUMBER = 14; DIFFICULTY = 15; GASLIMIT = 16; + SELFBALANCE = 17; + CHAINID = 18; } required NOp op = 1; } diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 557073766..57441e942 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -70,7 +70,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) // AssemblyStack entry point AssemblyStack stack( - langutil::EVMVersion(), + langutil::EVMVersion(langutil::EVMVersion::istanbul()), AssemblyStack::Language::StrictAssembly, dev::solidity::OptimiserSettings::full() ); @@ -95,7 +95,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( os1, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())) ); if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) @@ -105,7 +105,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())), (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) ); From d818746e0c3391ee95c42c20f3b42c05afd387e2 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 15 Nov 2019 14:48:11 +0100 Subject: [PATCH 007/130] [SMTChecker] Fix ICE in abi.decode --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 85 ++++++++++--------- libsolidity/formal/SMTEncoder.h | 2 + libsolidity/formal/SymbolicVariables.cpp | 20 +++-- libsolidity/formal/SymbolicVariables.h | 2 - .../smtCheckerTests/complex/MerkleProof.sol | 2 + .../complex/slither/external_function.sol | 2 +- .../complex/warn_on_struct.sol | 1 + .../functions/abi_encode_functions.sol | 2 + .../functions/functions_library_1.sol | 2 + .../functions/functions_library_1_fail.sol | 1 + .../special/abi_decode_memory_v2.sol | 15 ++++ .../abi_decode_memory_v2_value_types.sol | 20 +++++ .../special/abi_decode_simple.sol | 24 ++++++ .../typecast/enum_from_uint.sol | 1 + .../smtCheckerTests/types/address_call.sol | 3 + .../types/address_delegatecall.sol | 3 + .../types/address_staticcall.sol | 3 + .../smtCheckerTests/types/struct_1.sol | 2 + .../types/tuple_return_branch.sol | 2 + 20 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol create mode 100644 test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol create mode 100644 test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol diff --git a/Changelog.md b/Changelog.md index 9ecd036e1..201d792ed 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * SMTChecker: Fix internal error when using ``abi.decode``. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 98d6519fb..5fd364f8b 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -208,17 +208,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) solAssert(symbTuple, ""); auto const& components = symbTuple->components(); auto const& declarations = _varDecl.declarations(); - if (!components.empty()) - { - solAssert(components.size() == declarations.size(), ""); - for (unsigned i = 0; i < declarations.size(); ++i) - if ( - components.at(i) && - declarations.at(i) && - m_context.knownVariable(*declarations.at(i)) - ) - assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); - } + solAssert(components.size() == declarations.size(), ""); + for (unsigned i = 0; i < declarations.size(); ++i) + if ( + components.at(i) && + declarations.at(i) && + m_context.knownVariable(*declarations.at(i)) + ) + assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); } } else if (m_context.knownVariable(*_varDecl.declarations().front())) @@ -320,24 +317,23 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) { auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); - if (symbTuple->components().empty()) + auto const& symbComponents = symbTuple->components(); + auto const& tupleComponents = _tuple.components(); + solAssert(symbComponents.size() == _tuple.components().size(), ""); + for (unsigned i = 0; i < symbComponents.size(); ++i) { - vector> components; - for (auto const& component: _tuple.components()) - if (component) - { - if (auto varDecl = identifierToVariable(*component)) - components.push_back(m_context.variable(*varDecl)); - else - { - solAssert(m_context.knownExpression(*component), ""); - components.push_back(m_context.expression(*component)); - } - } + auto sComponent = symbComponents.at(i); + auto tComponent = tupleComponents.at(i); + if (sComponent && tComponent) + { + if (auto varDecl = identifierToVariable(*tComponent)) + m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl)); else - components.push_back(nullptr); - solAssert(components.size() == _tuple.components().size(), ""); - symbTuple->setComponents(move(components)); + { + solAssert(m_context.knownExpression(*tComponent), ""); + m_context.addAssertion(sComponent->currentValue() == expr(*tComponent)); + } + } } } else @@ -609,12 +605,22 @@ void SMTEncoder::endVisit(Identifier const& _identifier) defineExpr(_identifier, m_context.thisAddress()); m_uninterpretedTerms.insert(&_identifier); } - else if (smt::isSupportedType(_identifier.annotation().type->category())) - // TODO: handle MagicVariableDeclaration here - m_errorReporter.warning( - _identifier.location(), - "Assertion checker does not yet support the type of this variable." - ); + else if ( + _identifier.annotation().type->category() != Type::Category::Modifier + ) + createExpr(_identifier); +} + +void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) +{ + auto const& typeType = dynamic_cast(*_typeName.annotation().type); + auto result = smt::newSymbolicVariable( + *TypeProvider::uint256(), + typeType.actualType()->toString(false), + m_context + ); + solAssert(!result.first && result.second, ""); + m_context.createExpression(_typeName, result.second); } void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) @@ -1503,15 +1509,18 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) { auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_funCall)); solAssert(symbTuple, ""); - if (symbTuple->components().empty()) + auto const& symbComponents = symbTuple->components(); + solAssert(symbComponents.size() == returnParams.size(), ""); + for (unsigned i = 0; i < symbComponents.size(); ++i) { - vector> components; - for (auto param: returnParams) + auto sComponent = symbComponents.at(i); + auto param = returnParams.at(i); + solAssert(param, ""); + if (sComponent) { solAssert(m_context.knownVariable(*param), ""); - components.push_back(m_context.variable(*param)); + m_context.addAssertion(sComponent->currentValue() == currentValue(*param)); } - symbTuple->setComponents(move(components)); } } else if (returnParams.size() == 1) diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 37a7cea24..4f8c5ac8c 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -82,6 +83,7 @@ protected: void endVisit(BinaryOperation const& _node) override; void endVisit(FunctionCall const& _node) override; void endVisit(Identifier const& _node) override; + void endVisit(ElementaryTypeNameExpression const& _node) override; void endVisit(Literal const& _node) override; void endVisit(Return const& _node) override; bool visit(MemberAccess const& _node) override; diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 3a814ec7d..d0163d381 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable( SymbolicVariable(_type, _type, move(_uniqueName), _context) { solAssert(isTuple(m_type->category()), ""); -} - -void SymbolicTupleVariable::setComponents(vector> _components) -{ - solAssert(m_components.empty(), ""); - auto const& tupleType = dynamic_cast(m_type); - solAssert(_components.size() == tupleType->components().size(), ""); - m_components = move(_components); + auto const& tupleType = dynamic_cast(*m_type); + auto const& componentsTypes = tupleType.components(); + for (unsigned i = 0; i < componentsTypes.size(); ++i) + if (componentsTypes.at(i)) + { + string componentName = m_uniqueName + "_component_" + to_string(i); + auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context); + solAssert(result.second, ""); + m_components.emplace_back(move(result.second)); + } + else + m_components.emplace_back(nullptr); } diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index 9ae637490..4d9fcc2f7 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -250,8 +250,6 @@ public: return m_components; } - void setComponents(std::vector> _components); - private: std::vector> m_components; }; diff --git a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol index e4d8f4cfe..955d5fd1e 100644 --- a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol +++ b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol @@ -35,6 +35,8 @@ library MerkleProof { // ---- // Warning: (755-767): Assertion checker does not yet support this expression. +// Warning: (988-991): Assertion checker does not yet implement type abi // Warning: (988-1032): Assertion checker does not yet implement this type of function call. +// Warning: (1175-1178): Assertion checker does not yet implement type abi // Warning: (1175-1219): Assertion checker does not yet implement this type of function call. // Warning: (755-767): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol index 6db44a5ef..43bd10cf7 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol @@ -83,7 +83,7 @@ contract InternalCall { // Warning: (1144-1206): Function state mutability can be restricted to pure // Warning: (1212-1274): Function state mutability can be restricted to pure // Warning: (1280-1342): Function state mutability can be restricted to pure +// Warning: (771-774): Assertion checker does not yet implement type abi // Warning: (782-813): Type conversion is not yet fully supported and might yield false positives. // Warning: (771-814): Assertion checker does not yet implement this type of function call. -// Warning: (825-830): Assertion checker does not yet support the type of this variable. // Warning: (1403-1408): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol index f6c6f89c2..33cf71e81 100644 --- a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol @@ -9,5 +9,6 @@ contract C { // ---- // Warning: (133-143): Unused local variable. // Warning: (133-143): Assertion checker does not yet support the type of this variable. +// Warning: (146-147): Assertion checker does not yet implement type type(struct C.A storage pointer) // Warning: (146-163): Assertion checker does not yet implement type struct C.A memory // Warning: (146-163): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol index 8c3ef4aff..bed409b90 100644 --- a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol +++ b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol @@ -6,5 +6,7 @@ contract C { } // ---- // Warning: (31-64): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (162-165): Assertion checker does not yet implement type abi // Warning: (162-176): Assertion checker does not yet implement this type of function call. +// Warning: (178-181): Assertion checker does not yet implement type abi // Warning: (178-203): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol index 2ceb9e607..85387017b 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol @@ -16,3 +16,5 @@ contract C assert(y < 10000); } } +// ---- +// Warning: (228-229): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol index 324197004..5eda70213 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol @@ -17,4 +17,5 @@ contract C } } // ---- +// Warning: (228-229): Assertion checker does not yet implement type type(library L) // Warning: (245-261): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol new file mode 100644 index 000000000..e469f06ba --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +pragma experimental "ABIEncoderV2"; + +contract C { + struct S { uint x; uint[] b; } + function f() public pure returns (S memory, bytes memory) { + return abi.decode("abc", (S, bytes)); + } +} +// ---- +// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (151-159): Assertion checker does not yet support the type of this variable. +// Warning: (188-191): Assertion checker does not yet implement type abi +// Warning: (207-208): Assertion checker does not yet implement type type(struct C.S storage pointer) +// Warning: (188-217): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol new file mode 100644 index 000000000..cf86e8e78 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; +pragma experimental "ABIEncoderV2"; + +contract C { + function f() public pure { + (uint x1, bool b1) = abi.decode("abc", (uint, bool)); + (uint x2, bool b2) = abi.decode("abc", (uint, bool)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(x1 == x2); + } +} +// ---- +// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (125-132): Unused local variable. +// Warning: (183-190): Unused local variable. +// Warning: (136-139): Assertion checker does not yet implement type abi +// Warning: (136-167): Assertion checker does not yet implement this type of function call. +// Warning: (194-197): Assertion checker does not yet implement type abi +// Warning: (194-225): Assertion checker does not yet implement this type of function call. +// Warning: (303-319): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol new file mode 100644 index 000000000..c06577e0f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure { + (uint a1, bytes32 b1, C c1) = abi.decode("abc", (uint, bytes32, C)); + (uint a2, bytes32 b2, C c2) = abi.decode("abc", (uint, bytes32, C)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(a1 == a2); + assert(a1 != a2); + } + +} +// ---- +// Warning: (88-98): Unused local variable. +// Warning: (100-104): Unused local variable. +// Warning: (161-171): Unused local variable. +// Warning: (173-177): Unused local variable. +// Warning: (108-111): Assertion checker does not yet implement type abi +// Warning: (142-143): Assertion checker does not yet implement type type(contract C) +// Warning: (108-145): Assertion checker does not yet implement this type of function call. +// Warning: (181-184): Assertion checker does not yet implement type abi +// Warning: (215-216): Assertion checker does not yet implement type type(contract C) +// Warning: (181-218): Assertion checker does not yet implement this type of function call. +// Warning: (296-312): Assertion violation happens here +// Warning: (315-331): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol index 44c252868..33a7d972b 100644 --- a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol +++ b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol @@ -10,5 +10,6 @@ contract C } } // ---- +// Warning: (132-133): Assertion checker does not yet implement type type(enum C.D) // Warning: (132-136): Type conversion is not yet fully supported and might yield false positives. // Warning: (140-160): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_call.sol b/test/libsolidity/smtCheckerTests/types/address_call.sol index d6d165591..da87778e6 100644 --- a/test/libsolidity/smtCheckerTests/types/address_call.sol +++ b/test/libsolidity/smtCheckerTests/types/address_call.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (260-275): Assertion violation happens here +// Warning: (279-293): Assertion violation happens here +// Warning: (297-316): Assertion violation happens here +// Warning: (320-344): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol index 06925a66b..b9d3bcaea 100644 --- a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (268-283): Assertion violation happens here +// Warning: (287-301): Assertion violation happens here +// Warning: (305-324): Assertion violation happens here +// Warning: (328-352): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol index 529ce7ac0..32e4cbb9a 100644 --- a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (266-281): Assertion violation happens here +// Warning: (285-299): Assertion violation happens here +// Warning: (303-322): Assertion violation happens here +// Warning: (326-350): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/struct_1.sol b/test/libsolidity/smtCheckerTests/types/struct_1.sol index 38aa311a6..89c10c73c 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_1.sol @@ -17,8 +17,10 @@ contract C // Warning: (157-170): Unused local variable. // Warning: (157-170): Assertion checker does not yet support the type of this variable. // Warning: (139-146): Assertion checker does not yet implement type struct C.S storage ref +// Warning: (149-150): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (149-153): Assertion checker does not yet implement type struct C.S memory // Warning: (149-153): Assertion checker does not yet implement this expression. // Warning: (139-153): Assertion checker does not yet implement type struct C.S storage ref +// Warning: (173-174): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (173-177): Assertion checker does not yet implement type struct C.S memory // Warning: (173-177): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol index 99db33948..1a266aaa7 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol @@ -15,9 +15,11 @@ contract C { } // ---- // Warning: (112-120): Assertion checker does not yet support the type of this variable. +// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (137-141): Assertion checker does not yet implement type struct C.S memory // Warning: (137-141): Assertion checker does not yet implement this expression. // Warning: (193-203): Assertion checker does not yet support the type of this variable. +// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (137-141): Assertion checker does not yet implement type struct C.S memory // Warning: (137-141): Assertion checker does not yet implement this expression. // Warning: (227-228): Assertion checker does not yet implement type struct C.S memory From a8a50c4e0ea00ef978a8758916ff4fb8e77f3efa Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 12 Nov 2019 15:50:39 +0100 Subject: [PATCH 008/130] Upgrade evmone to v0.2.0 and adapt fuzzer build --- .circleci/docker/Dockerfile.clang.ubuntu1904 | 31 ++++---------------- .circleci/docker/Dockerfile.ubuntu1804 | 28 ++---------------- .circleci/docker/Dockerfile.ubuntu1904 | 27 ++--------------- test/tools/ossfuzz/CMakeLists.txt | 2 +- 4 files changed, 11 insertions(+), 77 deletions(-) diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904 index 4fb9b1132..2362d8768 100644 --- a/.circleci/docker/Dockerfile.clang.ubuntu1904 +++ b/.circleci/docker/Dockerfile.clang.ubuntu1904 @@ -89,40 +89,21 @@ RUN set -ex; \ ninja install/strip; \ rm -rf /usr/src/libprotobuf-mutator -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.2.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ + # isoltest links against the evmone shared library cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ ninja; \ ninja install/strip; \ + # abiv2_proto_ossfuzz links against the evmone standalone static library + cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \ + ninja; \ + ninja install/strip; \ rm -rf /usr/src/evmone FROM base diff --git a/.circleci/docker/Dockerfile.ubuntu1804 b/.circleci/docker/Dockerfile.ubuntu1804 index 0242661e7..d6b0c8142 100644 --- a/.circleci/docker/Dockerfile.ubuntu1804 +++ b/.circleci/docker/Dockerfile.ubuntu1804 @@ -74,34 +74,10 @@ RUN set -ex; \ ar r /usr/lib/libFuzzingEngine.a *.o; \ rm -rf /var/lib/libfuzzer -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE -ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e" +ARG EVMONE_HASH="81488656a53ae1bbf186d33fc69a4f5c59d3d7419b1ba1b4832a0d409b1a33bf" ARG EVMONE_MAJOR="0" -ARG EVMONE_MINOR="1" +ARG EVMONE_MINOR="2" ARG EVMONE_MICRO="0" RUN set -ex; \ EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu1904 index 504e13d1f..ecd09edf9 100644 --- a/.circleci/docker/Dockerfile.ubuntu1904 +++ b/.circleci/docker/Dockerfile.ubuntu1904 @@ -74,37 +74,14 @@ RUN set -ex; \ ar r /usr/lib/libFuzzingEngine.a *.o; \ rm -rf /var/lib/libfuzzer -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.2.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ + # isoltest links against the evmone shared library cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ ninja; \ ninja install/strip; \ diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 2481c63c3..2b22f41dd 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -71,7 +71,7 @@ if (OSSFUZZ) /usr/include/libprotobuf-mutator ) target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity - evmc evmone intx ethash evmc-instructions + evmc evmone-standalone.a protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a From 62ae015552380f098a25088f5b6ab7d4b4a4d12e Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 13 Nov 2019 13:07:41 +0100 Subject: [PATCH 009/130] docker: Add versioning and edit naming scheme --- .circleci/README.md | 16 +++++++++------- .circleci/config.yml | 14 +++++++------- test/tools/ossfuzz/CMakeLists.txt | 3 ++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 7f7b01455..23edcbb75 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -7,22 +7,24 @@ The docker images are build locally on the developer machine: ```!sh cd .circleci/docker/ -docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 . -docker push ethereum/solidity-buildpack-deps:ubuntu1904 +docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Dockerfile.ubuntu1904 . +docker push ethereum/solidity-buildpack-deps:ubuntu1904- ``` -which you can find on Dockerhub after the push at: +The current revision is `1`. - https://hub.docker.com/r/ethereum/solidity-buildpack-deps +Once the docker image has been built and pushed to Dockerhub, you can find it at: -where the image tag reflects the target OS to build Solidity and run its test on. + https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904- + +where the image tag reflects the target OS and revision to build Solidity and run its tests on. ### Testing docker images locally ```!sh cd solidity # Mounts your local solidity directory in docker container for testing -docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904 /bin/bash +docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904- /bin/bash cd /src/solidity -``` \ No newline at end of file +``` diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e9cebb06..7c2bda047 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -106,7 +106,7 @@ defaults: - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-1 steps: - checkout - attach_workspace: @@ -117,7 +117,7 @@ defaults: - test_ubuntu1904: &test_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 steps: - checkout - attach_workspace: @@ -287,7 +287,7 @@ jobs: b_ubu_clang: &build_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-1 environment: CC: clang CXX: clang++ @@ -299,7 +299,7 @@ jobs: b_ubu: &build_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 steps: - checkout - run: *run_build @@ -313,7 +313,7 @@ jobs: b_ubu18: &build_ubuntu1804 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1804 + - image: ethereum/solidity-buildpack-deps:ubuntu1804-1 environment: CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 CMAKE_BUILD_TYPE: RelWithDebugInfo @@ -519,7 +519,7 @@ jobs: b_docs: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 steps: - checkout - run: *setup_prerelease_commit_hash @@ -544,7 +544,7 @@ jobs: t_ubu_cli: &t_ubu_cli docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 environment: TERM: xterm steps: diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 2b22f41dd..83543b9f0 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -71,7 +71,8 @@ if (OSSFUZZ) /usr/include/libprotobuf-mutator ) target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity - evmc evmone-standalone.a + evmc + evmone-standalone protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a From 92745c7bfced7d8dc02627fcd6ba71c61e180d80 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 12:13:29 +0000 Subject: [PATCH 010/130] Update EVMC to 7 --- test/evmc/evmc.h | 291 ++++++++++++++------------------------------ test/evmc/evmc.hpp | 235 +++++++++++++++++++++++------------ test/evmc/helpers.h | 56 ++++----- test/evmc/loader.c | 60 ++++----- test/evmc/loader.h | 26 ++-- 5 files changed, 308 insertions(+), 360 deletions(-) diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index c826bfeae..fe950d379 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 6 + EVMC_ABI_VERSION = 7 }; @@ -153,9 +153,15 @@ struct evmc_tx_context int64_t block_timestamp; /**< The block timestamp. */ int64_t block_gas_limit; /**< The block gas limit. */ evmc_uint256be block_difficulty; /**< The block difficulty. */ + evmc_uint256be chain_id; /**< The blockchain's ChainID. */ }; -struct evmc_context; +/** + * @struct evmc_host_context + * The opaque data type representing the Host execution context. + * @see evmc_execute_fn(). + */ +struct evmc_host_context; /** * Get transaction context callback function. @@ -166,7 +172,7 @@ struct evmc_context; * @param context The pointer to the Host execution context. * @return The transaction context. */ -typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context); +typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_host_context* context); /** * Get block hash callback function. @@ -180,7 +186,7 @@ typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* co * @return The block hash or null bytes * if the information about the block is not available. */ -typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number); +typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context, int64_t number); /** * The execution status code. @@ -420,7 +426,8 @@ struct evmc_result * @param address The address of the account the query is about. * @return true if exists, false otherwise. */ -typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address); +typedef bool (*evmc_account_exists_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get storage callback function. @@ -433,7 +440,7 @@ typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_ * @return The storage value at the given storage key or null bytes * if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key); @@ -492,7 +499,7 @@ enum evmc_storage_status * @param value The value to be stored. * @return The effect on the storage item. */ -typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* context, +typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key, const evmc_bytes32* value); @@ -506,7 +513,7 @@ typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* con * @param address The address of the account. * @return The balance of the given account or 0 if the account does not exist. */ -typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, +typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_host_context* context, const evmc_address* address); /** @@ -519,7 +526,8 @@ typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, * @param address The address of the account. * @return The size of the code in the account or 0 if the account does not exist. */ -typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address); +typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get code size callback function. @@ -532,28 +540,27 @@ typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc * @param address The address of the account. * @return The hash of the code in the account or null bytes if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_host_context* context, const evmc_address* address); /** * Copy code callback function. * - * This callback function is used by an EVM to request a copy of the code - * of the given account to the memory buffer provided by the EVM. - * The Client MUST copy the requested code, starting with the given offset, - * to the provided memory buffer up to the size of the buffer or the size of - * the code, whichever is smaller. + * This callback function is used by an EVM to request a copy of the code + * of the given account to the memory buffer provided by the EVM. + * The Client MUST copy the requested code, starting with the given offset, + * to the provided memory buffer up to the size of the buffer or the size of + * the code, whichever is smaller. * - * @param context The pointer to the Client execution context. - * @see ::evmc_context. - * @param address The address of the account. - * @param code_offset The offset of the code to copy. - * @param buffer_data The pointer to the memory buffer allocated by the EVM - * to store a copy of the requested code. - * @param buffer_size The size of the memory buffer. - * @return The number of bytes copied to the buffer by the Client. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the account. + * @param code_offset The offset of the code to copy. + * @param buffer_data The pointer to the memory buffer allocated by the EVM + * to store a copy of the requested code. + * @param buffer_size The size of the memory buffer. + * @return The number of bytes copied to the buffer by the Client. */ -typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, +typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context, const evmc_address* address, size_t code_offset, uint8_t* buffer_data, @@ -562,34 +569,31 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, /** * Selfdestruct callback function. * - * This callback function is used by an EVM to SELFDESTRUCT given contract. - * The execution of the contract will not be stopped, that is up to the EVM. + * This callback function is used by an EVM to SELFDESTRUCT given contract. + * The execution of the contract will not be stopped, that is up to the EVM. * - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract to be selfdestructed. - * @param beneficiary The address where the remaining ETH is going to be - * transferred. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract to be selfdestructed. + * @param beneficiary The address where the remaining ETH is going to be transferred. */ -typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context, +typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_address* beneficiary); /** * Log callback function. * - * This callback function is used by an EVM to inform about a LOG that happened - * during an EVM bytecode execution. - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract that generated the log. - * @param data The pointer to unindexed data attached to the log. - * @param data_size The length of the data. - * @param topics The pointer to the array of topics attached to the log. - * @param topics_count The number of the topics. Valid values are between - * 0 and 4 inclusively. + * This callback function is used by an EVM to inform about a LOG that happened + * during an EVM bytecode execution. + * + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract that generated the log. + * @param data The pointer to unindexed data attached to the log. + * @param data_size The length of the data. + * @param topics The pointer to the array of topics attached to the log. + * @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively. */ -typedef void (*evmc_emit_log_fn)(struct evmc_context* context, +typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context, const evmc_address* address, const uint8_t* data, size_t data_size, @@ -599,11 +603,11 @@ typedef void (*evmc_emit_log_fn)(struct evmc_context* context, /** * Pointer to the callback function supporting EVM calls. * - * @param context The pointer to the Host execution context. - * @param msg The call parameters. + * @param context The pointer to the Host execution context. + * @param msg The call parameters. * @return The result of the call. */ -typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context, +typedef struct evmc_result (*evmc_call_fn)(struct evmc_host_context* context, const struct evmc_message* msg); /** @@ -654,31 +658,15 @@ struct evmc_host_interface }; -/** - * Execution context managed by the Host. - * - * The Host MUST pass the pointer to the execution context to ::evmc_execute_fn. - * The VM MUST pass the same pointer back to the Host in every callback function. - * The context MUST contain at least the function table defining - * the context callback interface. - * Optionally, the Host MAY include in the context additional data. - */ -struct evmc_context -{ - /** The Host interface. */ - const struct evmc_host_interface* host; -}; - - /* Forward declaration. */ -struct evmc_instance; +struct evmc_vm; /** - * Destroys the EVM instance. + * Destroys the VM instance. * - * @param evm The EVM instance to be destroyed. + * @param vm The VM instance to be destroyed. */ -typedef void (*evmc_destroy_fn)(struct evmc_instance* evm); +typedef void (*evmc_destroy_fn)(struct evmc_vm* vm); /** * Possible outcomes of evmc_set_option. @@ -691,19 +679,19 @@ enum evmc_set_option_result }; /** - * Configures the EVM instance. + * Configures the VM instance. * - * Allows modifying options of the EVM instance. - * Options: - * - code cache behavior: on, off, read-only, ... - * - optimizations, + * Allows modifying options of the VM instance. + * Options: + * - code cache behavior: on, off, read-only, ... + * - optimizations, * - * @param evm The EVM instance to be configured. - * @param name The option name. NULL-terminated string. Cannot be NULL. - * @param value The new option value. NULL-terminated string. Cannot be NULL. - * @return The outcome of the operation. + * @param vm The VM instance to be configured. + * @param name The option name. NULL-terminated string. Cannot be NULL. + * @param value The new option value. NULL-terminated string. Cannot be NULL. + * @return The outcome of the operation. */ -typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm, +typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_vm* vm, char const* name, char const* value); @@ -762,6 +750,7 @@ enum evmc_revision * The Petersburg revision. * * Other names: Constantinople2, ConstantinopleFix. + * * https://eips.ethereum.org/EIPS/eip-1716 */ EVMC_PETERSBURG = 6, @@ -773,23 +762,15 @@ enum evmc_revision */ EVMC_ISTANBUL = 7, + /** + * The Berlin revision. + * + * The spec draft: https://eips.ethereum.org/EIPS/eip-2070. + */ + EVMC_BERLIN = 8, + /** The maximum EVM revision supported. */ - EVMC_MAX_REVISION = EVMC_ISTANBUL, - - - /** - * Reserved for the post-Constantinople upgrade. - * - * @deprecated Replaced with ::EVMC_PETERSBURG. - */ - EVMC_CONSTANTINOPLE2 EVMC_DEPRECATED = EVMC_PETERSBURG, - - /** - * The latests EVM revision supported. - * - * @deprecated Replaced with ::EVMC_MAX_REVISION. - */ - EVMC_LATEST_REVISION EVMC_DEPRECATED = EVMC_MAX_REVISION + EVMC_MAX_REVISION = EVMC_BERLIN }; @@ -798,19 +779,22 @@ enum evmc_revision * * This function MAY be invoked multiple times for a single VM instance. * - * @param instance The VM instance. This argument MUST NOT be NULL. - * @param context The pointer to the Host execution context to be passed - * to the Host interface methods (::evmc_host_interface). - * This argument MUST NOT be NULL unless - * the @p instance has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param vm The VM instance. This argument MUST NOT be NULL. + * @param host The Host interface. This argument MUST NOT be NULL unless + * the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param context The opaque pointer to the Host execution context. + * This argument MAY be NULL. The VM MUST pass the same + * pointer to the methods of the @p host interface. + * The VM MUST NOT dereference the pointer. * @param rev The requested EVM specification revision. * @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL. * @param code The reference to the code to be executed. This argument MAY be NULL. * @param code_size The length of the code. If @p code is NULL this argument MUST be 0. * @return The execution result. */ -typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance, - struct evmc_context* context, +typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, @@ -855,98 +839,20 @@ typedef uint32_t evmc_capabilities_flagset; * Return the supported capabilities of the VM instance. * * This function MAY be invoked multiple times for a single VM instance, - * and its value MAY be influenced by calls to evmc_instance::set_option. + * and its value MAY be influenced by calls to evmc_vm::set_option. * - * @param instance The EVM instance. - * @return The supported capabilities of the VM. @see evmc_capabilities. + * @param vm The VM instance. + * @return The supported capabilities of the VM. @see evmc_capabilities. */ -typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance); - -/** - * The opaque type representing a Client-side tracer object. - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - */ -struct evmc_tracer_context; - -/** - * The callback to trace instructions execution in an EVM. - * - * This function informs the Client what instruction has been executed in the EVM implementation - * and what are the results of executing this particular instruction. - * The message level information (like call depth, destination address, etc.) are not provided here. - * This piece of information can be acquired by inspecting messages being sent to the EVM in - * ::evmc_execute_fn and the results of the messages execution. - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - * - * @param context The pointer to the Client-side tracing context. This allows to - * implement the tracer in OOP manner. - * @param code_offset The current instruction position in the code. - * @param status_code The status code of the instruction execution. - * @param gas_left The amount of the gas left after the instruction execution. - * @param stack_num_items The current EVM stack height after the instruction execution. - * @param pushed_stack_item The top EVM stack item pushed as the result of the instruction - * execution. This value is null when the instruction does not push - * anything to the stack. - * @param memory_size The size of the EVM memory after the instruction execution. - * @param changed_memory_offset The offset in number of bytes of the beginning of the memory area - * modified as the result of the instruction execution. - * The Client MAY use this information together with - * @p changed_memory_size and @p changed_memory to incrementally - * update the copy of the full VM's memory. - * @param changed_memory_size The size of the memory area modified as the result of - * the instruction execution. - * @param changed_memory The pointer to the memory area modified as the result of - * the instruction execution. - * The Client MAY access the pointed memory area - * (limited by the @p changed_memory_size) only during the current - * execution of the evmc_trace_callback(). - * The pointer MUST NOT be stored by the Client. - * The Client MUST NOT assume that - * `changed_memory - changed_memory_offset` is a valid base pointer - * of the VM memory. - */ -typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context, - size_t code_offset, - enum evmc_status_code status_code, - int64_t gas_left, - size_t stack_num_items, - const evmc_uint256be* pushed_stack_item, - size_t memory_size, - size_t changed_memory_offset, - size_t changed_memory_size, - const uint8_t* changed_memory); - -/** - * Sets the EVM instruction tracer. - * - * When the tracer is set in the EVM instance, the EVM SHOULD call back the tracer with information - * about instructions execution in the EVM. - * @see ::evmc_trace_callback. - * - * This will overwrite the previous settings (the callback and the context). - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - * - * @param instance The EVM instance. - * @param callback The tracer callback function. This argument MAY be NULL to disable previously - * set tracer. - * @param context The Client-side tracer context. This argument MAY be NULL in case the tracer - * does not require any context. This argument MUST be NULL if the callback - * argument is NULL. - */ -typedef void (*evmc_set_tracer_fn)(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context); +typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_vm* vm); /** - * The EVM instance. + * The VM instance. * * Defines the base struct of the VM implementation. */ -struct evmc_instance +struct evmc_vm { /** * EVMC ABI version implemented by the VM instance. @@ -973,14 +879,14 @@ struct evmc_instance const char* version; /** - * Pointer to function destroying the EVM instance. + * Pointer to function destroying the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ evmc_destroy_fn destroy; /** - * Pointer to function executing a code by the EVM instance. + * Pointer to function executing a code by the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ @@ -998,17 +904,6 @@ struct evmc_instance */ evmc_get_capabilities_fn get_capabilities; - /** - * Optional pointer to function setting the EVM instruction tracer. - * - * If the EVM does not support this feature the pointer can be NULL. - * - * @deprecated - * Since EVMC 6.3, the tracing API has been deprecated as there have been some - * design flaws discovered. New API is expected to be introduced in future. - */ - evmc_set_tracer_fn set_tracer; - /** * Optional pointer to function modifying VM's options. * @@ -1033,9 +928,9 @@ struct evmc_instance * For example, the shared library with the "beta-interpreter" implementation may be named * `libbeta-interpreter.so`. * - * @return EVM instance or NULL indicating instance creation failure. + * @return The VM instance or NULL indicating instance creation failure. */ -struct evmc_instance* evmc_create_example_vm(void); +struct evmc_vm* evmc_create_example_vm(void); #endif #if __cplusplus diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index ff486a232..1b52ae8d0 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -334,49 +334,46 @@ public: } }; -/// @copybrief evmc_instance +class Host; + +/// @copybrief evmc_vm /// -/// This is a RAII wrapper for evmc_instance and objects of this type +/// This is a RAII wrapper for evmc_vm and objects of this type /// automatically destroys the VM instance. -class vm +class VM { public: - vm() noexcept = default; + VM() noexcept = default; - /// Converting constructor from evmc_instance. - explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {} + /// Converting constructor from evmc_vm. + explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} /// Destructor responsible for automatically destroying the VM instance. - ~vm() noexcept + ~VM() noexcept { if (m_instance) m_instance->destroy(m_instance); } - vm(const vm&) = delete; - vm& operator=(const vm&) = delete; + VM(const VM&) = delete; + VM& operator=(const VM&) = delete; /// Move constructor. - vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } + VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } /// Move assignment operator. - vm& operator=(vm&& other) noexcept + VM& operator=(VM&& other) noexcept { - this->~vm(); + this->~VM(); m_instance = other.m_instance; other.m_instance = nullptr; return *this; } /// The constructor that captures a VM instance and configures the instance - /// with provided list of options. - vm(evmc_instance* instance, - std::initializer_list> options) noexcept - : m_instance{instance} - { - for (auto option : options) - set_option(option.first, option.second); - } + /// with the provided list of options. + inline VM(evmc_vm* vm, + std::initializer_list> options) noexcept; /// Checks if contains a valid pointer to the VM instance. explicit operator bool() const noexcept { return m_instance != nullptr; } @@ -384,13 +381,13 @@ public: /// Checks whenever the VM instance is ABI compatible with the current EVMC API. bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } - /// @copydoc evmc_instance::name + /// @copydoc evmc_vm::name char const* name() const noexcept { return m_instance->name; } - /// @copydoc evmc_instance::version + /// @copydoc evmc_vm::version char const* version() const noexcept { return m_instance->version; } - /// @copydoc evmc::instance::get_capabilities + /// @copydoc evmc::vm::get_capabilities evmc_capabilities_flagset get_capabilities() const noexcept { return m_instance->get_capabilities(m_instance); @@ -403,19 +400,53 @@ public: } /// @copydoc evmc_execute() - result execute(evmc_context& ctx, + result execute(const evmc_host_interface& host, + evmc_host_context* ctx, evmc_revision rev, const evmc_message& msg, const uint8_t* code, size_t code_size) noexcept { - return result{m_instance->execute(m_instance, &ctx, rev, &msg, code, code_size)}; + return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)}; + } + + /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. + inline result execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept; + + /// Executes code without the Host context. + /// + /// The same as + /// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision, + /// const evmc_message&, const uint8_t*, size_t), + /// but without providing the Host context and interface. + /// This method is for experimental precompiles support where execution is + /// guaranteed not to require any Host access. + result execute(evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{ + m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)}; } private: - evmc_instance* m_instance = nullptr; + evmc_vm* m_instance = nullptr; }; +inline VM::VM(evmc_vm* vm, + std::initializer_list> options) noexcept + : m_instance{vm} +{ + for (const auto& option : options) + set_option(option.first, option.second); +} + + /// The EVMC Host interface class HostInterface { @@ -471,46 +502,52 @@ public: /// Wrapper around EVMC host context / host interface. /// -/// To be used by VM implementations as better alternative to using ::evmc_context directly. +/// To be used by VM implementations as better alternative to using ::evmc_host_context directly. class HostContext : public HostInterface { - evmc_context* context = nullptr; + const evmc_host_interface* host = nullptr; + evmc_host_context* context = nullptr; evmc_tx_context tx_context = {}; public: - /// Implicit converting constructor from evmc_context. - HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT + /// Default constructor for null Host context. + HostContext() = default; + + /// Constructor from the EVMC Host primitives. + HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept + : host{interface}, context{ctx} + {} bool account_exists(const address& address) noexcept final { - return context->host->account_exists(context, &address); + return host->account_exists(context, &address); } bytes32 get_storage(const address& address, const bytes32& key) noexcept final { - return context->host->get_storage(context, &address, &key); + return host->get_storage(context, &address, &key); } evmc_storage_status set_storage(const address& address, const bytes32& key, const bytes32& value) noexcept final { - return context->host->set_storage(context, &address, &key, &value); + return host->set_storage(context, &address, &key, &value); } uint256be get_balance(const address& address) noexcept final { - return context->host->get_balance(context, &address); + return host->get_balance(context, &address); } size_t get_code_size(const address& address) noexcept final { - return context->host->get_code_size(context, &address); + return host->get_code_size(context, &address); } bytes32 get_code_hash(const address& address) noexcept final { - return context->host->get_code_hash(context, &address); + return host->get_code_hash(context, &address); } size_t copy_code(const address& address, @@ -518,17 +555,17 @@ public: uint8_t* buffer_data, size_t buffer_size) noexcept final { - return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); + return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } void selfdestruct(const address& addr, const address& beneficiary) noexcept final { - context->host->selfdestruct(context, &addr, &beneficiary); + host->selfdestruct(context, &addr, &beneficiary); } result call(const evmc_message& message) noexcept final { - return result{context->host->call(context, &message)}; + return result{host->call(context, &message)}; } /// @copydoc HostInterface::get_tx_context() @@ -540,13 +577,13 @@ public: evmc_tx_context get_tx_context() noexcept final { if (tx_context.block_timestamp == 0) - tx_context = context->host->get_tx_context(context); + tx_context = host->get_tx_context(context); return tx_context; } bytes32 get_block_hash(int64_t number) noexcept final { - return context->host->get_block_hash(context, number); + return host->get_block_hash(context, number); } void emit_log(const address& addr, @@ -555,7 +592,7 @@ public: const bytes32 topics[], size_t topics_count) noexcept final { - context->host->emit_log(context, &addr, data, data_size, topics, topics_count); + host->emit_log(context, &addr, data, data_size, topics, topics_count); } }; @@ -563,89 +600,135 @@ public: /// /// When implementing EVMC Host, you can directly inherit from the evmc::Host class. /// This way your implementation will be simpler by avoiding manual handling -/// of the ::evmc_context and the ::evmc_context::host. -class Host : public HostInterface, public evmc_context +/// of the ::evmc_host_context and the ::evmc_host_interface. +class Host : public HostInterface { public: - inline Host() noexcept; + /// Provides access to the global host interface. + /// @returns Reference to the host interface object. + static const evmc_host_interface& get_interface() noexcept; + + /// Converts the Host object to the opaque host context pointer. + /// @returns Pointer to evmc_host_context. + evmc_host_context* to_context() noexcept { return reinterpret_cast(this); } + + /// Converts the opaque host context pointer back to the original Host object. + /// @tparam DerivedClass The class derived from the Host class. + /// @param context The opaque host context pointer. + /// @returns The pointer to DerivedClass. + template + static DerivedClass* from_context(evmc_host_context* context) noexcept + { + // Get pointer of the Host base class. + auto* h = reinterpret_cast(context); + + // Additional downcast, only possible if DerivedClass inherits from Host. + return static_cast(h); + } }; + +inline result VM::execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept +{ + return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size); +} + + namespace internal { -inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept +inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->account_exists(*addr); + return Host::from_context(h)->account_exists(*addr); } -inline evmc_bytes32 get_storage(evmc_context* h, + +inline evmc_bytes32 get_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key) noexcept { - return static_cast(h)->get_storage(*addr, *key); + return Host::from_context(h)->get_storage(*addr, *key); } -inline evmc_storage_status set_storage(evmc_context* h, + +inline evmc_storage_status set_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key, const evmc_bytes32* value) noexcept { - return static_cast(h)->set_storage(*addr, *key, *value); + return Host::from_context(h)->set_storage(*addr, *key, *value); } -inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_balance(*addr); + return Host::from_context(h)->get_balance(*addr); } -inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept + +inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_size(*addr); + return Host::from_context(h)->get_code_size(*addr); } -inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_hash(*addr); + return Host::from_context(h)->get_code_hash(*addr); } -inline size_t copy_code(evmc_context* h, + +inline size_t copy_code(evmc_host_context* h, const evmc_address* addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) noexcept { - return static_cast(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); + return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); } -inline void selfdestruct(evmc_context* h, + +inline void selfdestruct(evmc_host_context* h, const evmc_address* addr, const evmc_address* beneficiary) noexcept { - static_cast(h)->selfdestruct(*addr, *beneficiary); + Host::from_context(h)->selfdestruct(*addr, *beneficiary); } -inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept + +inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept { - return static_cast(h)->call(*msg).release_raw(); + return Host::from_context(h)->call(*msg).release_raw(); } -inline evmc_tx_context get_tx_context(evmc_context* h) noexcept + +inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept { - return static_cast(h)->get_tx_context(); + return Host::from_context(h)->get_tx_context(); } -inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept + +inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept { - return static_cast(h)->get_block_hash(block_number); + return Host::from_context(h)->get_block_hash(block_number); } -inline void emit_log(evmc_context* h, + +inline void emit_log(evmc_host_context* h, const evmc_address* addr, const uint8_t* data, size_t data_size, const evmc_bytes32 topics[], size_t num_topics) noexcept { - static_cast(h)->emit_log(*addr, data, data_size, static_cast(topics), + Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics), num_topics); } - -constexpr evmc_host_interface interface{ - account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash, - copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log, -}; } // namespace internal -inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {} - +inline const evmc_host_interface& Host::get_interface() noexcept +{ + static constexpr evmc_host_interface interface{ + ::evmc::internal::account_exists, ::evmc::internal::get_storage, + ::evmc::internal::set_storage, ::evmc::internal::get_balance, + ::evmc::internal::get_code_size, ::evmc::internal::get_code_hash, + ::evmc::internal::copy_code, ::evmc::internal::selfdestruct, + ::evmc::internal::call, ::evmc::internal::get_tx_context, + ::evmc::internal::get_block_hash, ::evmc::internal::emit_log}; + return interface; +} } // namespace evmc diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h index 2c4dbceae..36febfd72 100644 --- a/test/evmc/helpers.h +++ b/test/evmc/helpers.h @@ -22,36 +22,35 @@ #include /** - * Returns true if the VM instance has a compatible ABI version. + * Returns true if the VM has a compatible ABI version. */ -static inline int evmc_is_abi_compatible(struct evmc_instance* instance) +static inline bool evmc_is_abi_compatible(struct evmc_vm* vm) { - return instance->abi_version == EVMC_ABI_VERSION; + return vm->abi_version == EVMC_ABI_VERSION; } /** - * Returns the name of the VM instance. + * Returns the name of the VM. */ -static inline const char* evmc_vm_name(struct evmc_instance* instance) +static inline const char* evmc_vm_name(struct evmc_vm* vm) { - return instance->name; + return vm->name; } /** - * Returns the version of the VM instance. + * Returns the version of the VM. */ -static inline const char* evmc_vm_version(struct evmc_instance* instance) +static inline const char* evmc_vm_version(struct evmc_vm* vm) { - return instance->version; + return vm->version; } /** - * Checks if the VM instance has the given capability. + * Checks if the VM has the given capability. * * @see evmc_get_capabilities_fn */ -static inline bool evmc_vm_has_capability(struct evmc_instance* vm, - enum evmc_capabilities capability) +static inline bool evmc_vm_has_capability(struct evmc_vm* vm, enum evmc_capabilities capability) { return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0; } @@ -61,52 +60,39 @@ static inline bool evmc_vm_has_capability(struct evmc_instance* vm, * * @see evmc_destroy_fn */ -static inline void evmc_destroy(struct evmc_instance* instance) +static inline void evmc_destroy(struct evmc_vm* vm) { - instance->destroy(instance); + vm->destroy(vm); } /** - * Sets the option for the VM instance, if the feature is supported by the VM. + * Sets the option for the VM, if the feature is supported by the VM. * * @see evmc_set_option_fn */ -static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance, +static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm, char const* name, char const* value) { - if (instance->set_option) - return instance->set_option(instance, name, value); + if (vm->set_option) + return vm->set_option(vm, name, value); return EVMC_SET_OPTION_INVALID_NAME; } -/** - * Sets the tracer callback for the VM instance, if the feature is supported by the VM. - * - * @see evmc_set_tracer_fn - */ -EVMC_DEPRECATED -static inline void evmc_set_tracer(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context) -{ - if (instance->set_tracer) - instance->set_tracer(instance, callback, context); -} - /** * Executes code in the VM instance. * * @see evmc_execute_fn. */ -static inline struct evmc_result evmc_execute(struct evmc_instance* instance, - struct evmc_context* context, +static inline struct evmc_result evmc_execute(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, size_t code_size) { - return instance->execute(instance, context, rev, msg, code, code_size); + return vm->execute(vm, host, context, rev, msg, code, code_size); } /// The evmc_result release function using free() for releasing the memory. diff --git a/test/evmc/loader.c b/test/evmc/loader.c index 30bb39023..d82005438 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -152,8 +152,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro char* base_name = prefixed_name + prefix_length; strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos); - // Trim the file extension. - char* ext_pos = strrchr(prefixed_name, '.'); + // Trim all file extensions. + char* ext_pos = strchr(prefixed_name, '.'); if (ext_pos) *ext_pos = 0; @@ -163,15 +163,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro *dash_pos++ = '_'; // Search for the built function name. - while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL) - { - // Shorten the base name by skipping the `word_` segment. - const char* shorter_name_pos = strchr(base_name, '_'); - if (!shorter_name_pos) - break; - - memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1); - } + create_fn = DLL_GET_CREATE_FN(handle, prefixed_name); if (!create_fn) create_fn = DLL_GET_CREATE_FN(handle, "evmc_create"); @@ -196,8 +188,7 @@ const char* evmc_last_error_msg() return m; } -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code) +struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code) { // First load the DLL. This also resets the last_error_msg; evmc_create_fn create_fn = evmc_load(filename, error_code); @@ -207,21 +198,21 @@ struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; - struct evmc_instance* instance = create_fn(); - if (!instance) + struct evmc_vm* vm = create_fn(); + if (!vm) { - ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, - "creating EVMC instance of %s has failed", filename); + ec = set_error(EVMC_LOADER_VM_CREATION_FAILURE, "creating EVMC VM of %s has failed", + filename); goto exit; } - if (!evmc_is_abi_compatible(instance)) + if (!evmc_is_abi_compatible(vm)) { ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, "EVMC ABI version %d of %s mismatches the expected version %d", - instance->abi_version, filename, EVMC_ABI_VERSION); - evmc_destroy(instance); - instance = NULL; + vm->abi_version, filename, EVMC_ABI_VERSION); + evmc_destroy(vm); + vm = NULL; goto exit; } @@ -229,7 +220,7 @@ exit: if (error_code) *error_code = ec; - return instance; + return vm; } /// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr. @@ -255,11 +246,10 @@ static char* get_token(char** str_ptr, char delim) return str; } -struct evmc_instance* evmc_load_and_configure(const char* config, - enum evmc_loader_error_code* error_code) +struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_error_code* error_code) { enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; - struct evmc_instance* instance = NULL; + struct evmc_vm* vm = NULL; char config_copy_buffer[PATH_MAX_LENGTH]; if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0) @@ -273,14 +263,14 @@ struct evmc_instance* evmc_load_and_configure(const char* config, char* options = config_copy_buffer; const char* path = get_token(&options, ','); - instance = evmc_load_and_create(path, error_code); - if (!instance) + vm = evmc_load_and_create(path, error_code); + if (!vm) return NULL; - if (instance->set_option == NULL && strlen(options) != 0) + if (vm->set_option == NULL && strlen(options) != 0) { ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options", - instance->name, path); + vm->name, path); goto exit; } @@ -293,18 +283,18 @@ struct evmc_instance* evmc_load_and_configure(const char* config, // The option variable will have the value, can be empty. const char* name = get_token(&option, '='); - enum evmc_set_option_result r = instance->set_option(instance, name, option); + enum evmc_set_option_result r = vm->set_option(vm, name, option); switch (r) { case EVMC_SET_OPTION_SUCCESS: break; case EVMC_SET_OPTION_INVALID_NAME: ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s): unknown option '%s'", - instance->name, path, name); + vm->name, path, name); goto exit; case EVMC_SET_OPTION_INVALID_VALUE: ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE, - "%s (%s): unsupported value '%s' for option '%s'", instance->name, path, + "%s (%s): unsupported value '%s' for option '%s'", vm->name, path, option, name); goto exit; } @@ -315,9 +305,9 @@ exit: *error_code = ec; if (ec == EVMC_LOADER_SUCCESS) - return instance; + return vm; - if (instance) - evmc_destroy(instance); + if (vm) + evmc_destroy(vm); return NULL; } diff --git a/test/evmc/loader.h b/test/evmc/loader.h index 0c50a81f4..523139e31 100644 --- a/test/evmc/loader.h +++ b/test/evmc/loader.h @@ -19,7 +19,7 @@ extern "C" { #endif /** The function pointer type for EVMC create functions. */ -typedef struct evmc_instance* (*evmc_create_fn)(void); +typedef struct evmc_vm* (*evmc_create_fn)(void); /** Error codes for the EVMC loader. */ enum evmc_loader_error_code @@ -37,7 +37,7 @@ enum evmc_loader_error_code EVMC_LOADER_INVALID_ARGUMENT = 3, /** The creation of a VM instance has failed. */ - EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, + EVMC_LOADER_VM_CREATION_FAILURE = 4, /** The ABI version of the VM instance has mismatched. */ EVMC_LOADER_ABI_VERSION_MISMATCH = 5, @@ -61,21 +61,16 @@ enum evmc_loader_error_code * After the DLL is successfully loaded the function tries to find the EVM create function in the * library. The `filename` is used to guess the EVM name and the name of the create function. * The create function name is constructed by the following rules. Consider example path: - * "/ethereum/libexample-interpreter.so". + * "/ethereum/libexample-interpreter.so.1.0". * - the filename is taken from the path: - * "libexample-interpreter.so", - * - the "lib" prefix and file extension are stripped from the name: + * "libexample-interpreter.so.1.0", + * - the "lib" prefix and all file extensions are stripped from the name: * "example-interpreter" * - all "-" are replaced with "_" to construct _base name_: * "example_interpreter", * - the function name "evmc_create_" + _base name_ is searched in the library: * "evmc_create_example_interpreter", - * - if function not found, the _base name_ is shorten by skipping the first word separated by "_": - * "interpreter", - * - then, the function of the shorter name "evmc_create_" + _base name_ is searched in the library: - * "evmc_create_interpreter", - * - the name shortening continues until a function is found or the name cannot be shorten more, - * - lastly, when no function found, the function name "evmc_create" is searched in the library. + * - if the function is not found, the function name "evmc_create" is searched in the library. * * If the create function is found in the library, the pointer to the function is returned. * Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. @@ -98,7 +93,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * * This is a macro for creating the VM instance with the function returned from evmc_load(). * The function signals the same errors as evmc_load() and additionally: - * - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL, + * - ::EVMC_LOADER_VM_CREATION_FAILURE when the create function returns NULL, * - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different * from the ABI version of this library (::EVMC_ABI_VERSION). * @@ -114,8 +109,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the created VM or NULL in case of error. */ -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code); +struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code); /** * Dynamically loads the EVMC module, then creates and configures the VM instance. @@ -151,8 +145,8 @@ struct evmc_instance* evmc_load_and_create(const char* filename, * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the created VM or NULL in case of error. */ -struct evmc_instance* evmc_load_and_configure(const char* config, - enum evmc_loader_error_code* error_code); +struct evmc_vm* evmc_load_and_configure(const char* config, + enum evmc_loader_error_code* error_code); /** * Returns the human-readable message describing the most recent error From 65f3661d34dc07524475a18f2c47228e678d1a7c Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Tue, 19 Nov 2019 11:11:06 +0100 Subject: [PATCH 011/130] Update .github/workflows/stale.yml Co-Authored-By: chriseth --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6904a16e5..048e2dcb4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,6 +12,6 @@ jobs: - uses: actions/stale@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: "A reminder that this pull request has had no activity for 30 days" + stale-pr-message: "A reminder that this pull request has had no activity for 14 days" stale-pr-label: "no-pr-activity" days-before-stale: 14 From 1432f0ad2864e64260463e595af4164bcaff3f69 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 12:17:38 +0000 Subject: [PATCH 012/130] Adapt EVMHost for EVMC7 --- test/EVMHost.cpp | 10 +++++----- test/EVMHost.h | 6 +++--- test/tools/ossfuzz/abiV2ProtoFuzzer.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index b7755f28f..d23e2c548 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -35,17 +35,17 @@ using namespace dev; using namespace dev::test; -evmc::vm* EVMHost::getVM(string const& _path) +evmc::VM* EVMHost::getVM(string const& _path) { - static unique_ptr theVM; + static unique_ptr theVM; if (!theVM && !_path.empty()) { evmc_loader_error_code errorCode = {}; - evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode); + evmc_vm* vm = evmc_load_and_configure(_path.c_str(), &errorCode); if (vm && errorCode == EVMC_LOADER_SUCCESS) { if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1)) - theVM = make_unique(vm); + theVM = make_unique(vm); else { evmc_destroy(vm); @@ -63,7 +63,7 @@ evmc::vm* EVMHost::getVM(string const& _path) return theVM.get(); } -EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm): +EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): m_vm(_vm) { if (!m_vm) diff --git a/test/EVMHost.h b/test/EVMHost.h index c01d27ad0..2ffcabcfa 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -40,9 +40,9 @@ public: /// Tries to dynamically load libevmone. @returns nullptr on failure. /// The path has to be provided for the first successful run and will be ignored /// afterwards. - static evmc::vm* getVM(std::string const& _path = {}); + static evmc::VM* getVM(std::string const& _path = {}); - explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm = getVM()); + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm = getVM()); struct Account { @@ -179,7 +179,7 @@ private: /// @note The return value is only valid as long as @a _data is alive! static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; - evmc::vm* m_vm = nullptr; + evmc::VM* m_vm = nullptr; evmc_revision m_evmVersion; }; diff --git a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp index 91bd4b8e2..9d7308299 100644 --- a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp +++ b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp @@ -24,7 +24,7 @@ #include -static evmc::vm evmone = evmc::vm{evmc_create_evmone()}; +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; using namespace dev::test::abiv2fuzzer; using namespace dev::test; From 6b198a261bc0f2a943c9ff9f1c94d1153ab228f9 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 12:17:58 +0000 Subject: [PATCH 013/130] Support Istanbul in EVMHost --- test/EVMHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index d23e2c548..05bca2014 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -83,7 +83,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): else if (_evmVersion == langutil::EVMVersion::constantinople()) m_evmVersion = EVMC_CONSTANTINOPLE; else if (_evmVersion == langutil::EVMVersion::istanbul()) - assertThrow(false, Exception, "Istanbul is not supported yet."); + m_evmVersion = EVMC_ISTANBUL; else if (_evmVersion == langutil::EVMVersion::berlin()) assertThrow(false, Exception, "Berlin is not supported yet."); else //if (_evmVersion == langutil::EVMVersion::petersburg()) From 9a124101baf945c344604fbe3cf3da40882d0b70 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 14 Nov 2019 12:00:04 +0100 Subject: [PATCH 014/130] Set chain_id in EVMHost --- test/EVMHost.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 05bca2014..d2852614e 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -227,6 +227,8 @@ evmc_tx_context EVMHost::get_tx_context() noexcept ctx.block_gas_limit = 20000000; ctx.tx_gas_price = convertToEVMC(u256("3000000000")); ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292")); + // Mainnet according to EIP-155 + ctx.chain_id = convertToEVMC(u256(1)); return ctx; } From 35708706422e80bcb496b31570b2572aa018b140 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Nov 2019 23:25:17 +0100 Subject: [PATCH 015/130] Upgrade evmone to v0.3.0 in the docker images --- .circleci/README.md | 2 +- .circleci/docker/Dockerfile.clang.ubuntu1904 | 4 ++-- .circleci/docker/Dockerfile.ubuntu1804 | 4 ++-- .circleci/docker/Dockerfile.ubuntu1904 | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 23edcbb75..c8bcd45f5 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -11,7 +11,7 @@ docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Docker docker push ethereum/solidity-buildpack-deps:ubuntu1904- ``` -The current revision is `1`. +The current revision is `2`. Once the docker image has been built and pushed to Dockerhub, you can find it at: diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904 index 2362d8768..3678afbed 100644 --- a/.circleci/docker/Dockerfile.clang.ubuntu1904 +++ b/.circleci/docker/Dockerfile.clang.ubuntu1904 @@ -92,7 +92,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.2.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ @@ -109,4 +109,4 @@ RUN set -ex; \ FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include \ No newline at end of file +COPY --from=libraries /usr/include /usr/include diff --git a/.circleci/docker/Dockerfile.ubuntu1804 b/.circleci/docker/Dockerfile.ubuntu1804 index d6b0c8142..fbf664a49 100644 --- a/.circleci/docker/Dockerfile.ubuntu1804 +++ b/.circleci/docker/Dockerfile.ubuntu1804 @@ -75,9 +75,9 @@ RUN set -ex; \ rm -rf /var/lib/libfuzzer # EVMONE -ARG EVMONE_HASH="81488656a53ae1bbf186d33fc69a4f5c59d3d7419b1ba1b4832a0d409b1a33bf" +ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b" ARG EVMONE_MAJOR="0" -ARG EVMONE_MINOR="2" +ARG EVMONE_MINOR="3" ARG EVMONE_MICRO="0" RUN set -ex; \ EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu1904 index ecd09edf9..2c211d956 100644 --- a/.circleci/docker/Dockerfile.ubuntu1904 +++ b/.circleci/docker/Dockerfile.ubuntu1904 @@ -77,7 +77,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.2.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ From 4eccbaa4da3535782a59545048d5f985775879a8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 12:08:04 +0100 Subject: [PATCH 016/130] Use new docker images (revision 2) --- .circleci/config.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c2bda047..cdd6edeab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -106,7 +106,7 @@ defaults: - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 steps: - checkout - attach_workspace: @@ -117,7 +117,7 @@ defaults: - test_ubuntu1904: &test_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - attach_workspace: @@ -287,7 +287,7 @@ jobs: b_ubu_clang: &build_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 environment: CC: clang CXX: clang++ @@ -299,7 +299,7 @@ jobs: b_ubu: &build_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - run: *run_build @@ -313,7 +313,7 @@ jobs: b_ubu18: &build_ubuntu1804 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1804-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1804-2 environment: CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 CMAKE_BUILD_TYPE: RelWithDebugInfo @@ -519,7 +519,7 @@ jobs: b_docs: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - run: *setup_prerelease_commit_hash @@ -544,7 +544,7 @@ jobs: t_ubu_cli: &t_ubu_cli docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-1 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 environment: TERM: xterm steps: From 4ecd46a2e8e425eada02e4bfd698a5482ee8498e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 19 Nov 2019 16:10:46 +0100 Subject: [PATCH 017/130] Clarify scoping with for loops. --- docs/yul.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/yul.rst b/docs/yul.rst index ae8eb1f9e..d0a09d3a5 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -182,11 +182,17 @@ introduce new identifiers into these scopes. Identifiers are visible in the block they are defined in (including all sub-nodes and sub-blocks). -As an exception, identifiers defined in the "init" part of the for-loop + +As an exception, identifiers defined directly in the "init" part of the for-loop (the first block) are visible in all other parts of the for-loop (but not outside of the loop). Identifiers declared in the other parts of the for loop respect the regular syntactical scoping rules. + +This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent +to ``{ I... for {} C { P... } { B... } }``. + + The parameters and return parameters of functions are visible in the function body and their names cannot overlap. From eec1998a89e7436b08049983241f780363dee787 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 19 Nov 2019 16:49:43 +0100 Subject: [PATCH 018/130] Update evmone version to v0.3.0 in docs and scripts --- .circleci/osx_install_dependencies.sh | 6 +++--- docs/contributing.rst | 2 +- test/Common.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 22e14af7e..fadff605e 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -52,8 +52,8 @@ then rm -rf z3-4.8.6-x64-osx-10.14.6 # evmone - wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz - tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local - rm -f evmone-0.1.0-darwin-x86_64.tar.gz + wget https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz + tar xzpf evmone-0.3.0-darwin-x86_64.tar.gz -C /usr/local + rm -f evmone-0.3.0-darwin-x86_64.tar.gz fi diff --git a/docs/contributing.rst b/docs/contributing.rst index a78d2e5d7..6220d7d7f 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -94,7 +94,7 @@ The test system will automatically try to discover the location of the ``evmone` starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests are skipped. To run all tests, download the library from -`Github `_ +`Github `_ and either place it in the project root path or inside the ``deps`` folder. If you do not have libz3 installed on your system, you should disable the SMT tests: diff --git a/test/Common.h b/test/Common.h index ebbe2d2ad..bc632ff59 100644 --- a/test/Common.h +++ b/test/Common.h @@ -32,13 +32,13 @@ namespace test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-windows-amd64.zip"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-linux-x86_64.tar.gz"; #endif From 71a310a2eaf48452a9c501c2ee7d6b5bd66badf8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 12:46:59 +0000 Subject: [PATCH 019/130] Run fuzzer for istanbul --- test/tools/fuzzer_common.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index edeb1e554..5cd64a6c7 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -34,7 +34,8 @@ static vector s_evmVersions = { "spuriousDragon", "byzantium", "constantinople", - "petersburg" + "petersburg", + "istanbul" }; void FuzzerUtil::runCompiler(string const& _input, bool _quiet) From 21312f52f2bafc8cab46a5c0940c4c82a6a8f802 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 12:56:28 +0000 Subject: [PATCH 020/130] Add new gas prices for SLOAD/BALANCE/EXTCODEHASH for Istanbul (EIP1884) --- libevmasm/GasMeter.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index f988138c0..8086ed196 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -53,7 +53,12 @@ namespace GasCosts } inline unsigned balanceGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 400 : 20; + if (_evmVersion >= langutil::EVMVersion::istanbul()) + return 700; + else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) + return 400; + else + return 20; } static unsigned const expGas = 10; inline unsigned expByteGas(langutil::EVMVersion _evmVersion) @@ -64,7 +69,12 @@ namespace GasCosts static unsigned const keccak256WordGas = 6; inline unsigned sloadGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 200 : 50; + if (_evmVersion >= langutil::EVMVersion::istanbul()) + return 800; + else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) + return 200; + else + return 50; } static unsigned const sstoreSetGas = 20000; static unsigned const sstoreResetGas = 5000; From 840aba8929098fa69c0873aba46f0c5ea856fbac Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 14:10:08 +0000 Subject: [PATCH 021/130] Add new gas price for deployed bytecode (EIP2028) --- libevmasm/ConstantOptimiser.cpp | 8 ++++---- libevmasm/GasMeter.cpp | 4 ++-- libevmasm/GasMeter.h | 7 +++++-- libsolidity/interface/CompilerStack.cpp | 2 +- libyul/backends/evm/EVMMetrics.cpp | 4 ++-- test/EVMHost.cpp | 21 +++++++++++---------- test/EVMHost.h | 5 ++++- test/libsolidity/GasCosts.cpp | 22 +++++++++++++--------- test/libsolidity/GasMeter.cpp | 3 ++- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 4825bc5cf..36cb2d656 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -96,7 +96,7 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const { assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated."); - return bigint(GasMeter::dataGas(_data, m_params.isCreation)); + return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion)); } size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) @@ -131,7 +131,7 @@ bigint LiteralMethod::gasNeeded() const return combineGas( simpleRunGas({Instruction::PUSH1}), // PUSHX plus data - (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), + (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), 0 ); } @@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const // Run gas: we ignore memory increase costs simpleRunGas(copyRoutine()) + GasCosts::copyGas, // Data gas for copy routines: Some bytes are zero, but we ignore them. - bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), + bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), // Data gas for data itself dataGas(toBigEndian(m_value)) ); @@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const return combineGas( simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), // Data gas for routine: Some bytes are zero, but we ignore them. - bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), + bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), 0 ); } diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 5ff3a905e..ca8d7fbd4 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -266,13 +266,13 @@ unsigned GasMeter::runGas(Instruction _instruction) return 0; } -u256 GasMeter::dataGas(bytes const& _data, bool _inCreation) +u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion) { bigint gas = 0; if (_inCreation) { for (auto b: _data) - gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; + gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas; } else gas = bigint(GasCosts::createDataGas) * _data.size(); diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 8086ed196..38ab4aa6f 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -102,7 +102,10 @@ namespace GasCosts static unsigned const txGas = 21000; static unsigned const txCreateGas = 53000; static unsigned const txDataZeroGas = 4; - static unsigned const txDataNonZeroGas = 68; + inline unsigned txDataNonZeroGas(langutil::EVMVersion _evmVersion) + { + return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68; + } static unsigned const copyGas = 3; } @@ -149,7 +152,7 @@ public: /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas /// otherwise code will be stored and have to pay "createDataGas" cost. - static u256 dataGas(bytes const& _data, bool _inCreation); + static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion); private: /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 57cae9374..423cdcceb 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1410,7 +1410,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const if (eth::AssemblyItems const* items = assemblyItems(_contractName)) { Gas executionGas = gasEstimator.functionalEstimation(*items); - Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)}; + Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false, m_evmVersion)}; Json::Value creation(Json::objectValue); creation["codeDepositCost"] = gasToJson(codeDepositGas); diff --git a/libyul/backends/evm/EVMMetrics.cpp b/libyul/backends/evm/EVMMetrics.cpp index 5211e5132..204014589 100644 --- a/libyul/backends/evm/EVMMetrics.cpp +++ b/libyul/backends/evm/EVMMetrics.cpp @@ -96,7 +96,7 @@ void GasMeterVisitor::operator()(Literal const& _lit) m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::PUSH1); m_dataGas += singleByteDataGas() + - size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation)); + size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation, m_dialect.evmVersion())); } void GasMeterVisitor::operator()(Identifier const&) @@ -108,7 +108,7 @@ void GasMeterVisitor::operator()(Identifier const&) size_t GasMeterVisitor::singleByteDataGas() const { if (m_isCreation) - return dev::eth::GasCosts::txDataNonZeroGas; + return dev::eth::GasCosts::txDataNonZeroGas(m_dialect.evmVersion()); else return dev::eth::GasCosts::createDataGas; } diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index d2852614e..64ef1c5e5 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -64,7 +64,8 @@ evmc::VM* EVMHost::getVM(string const& _path) } EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): - m_vm(_vm) + m_vm(_vm), + m_evmVersion(_evmVersion) { if (!m_vm) { @@ -73,21 +74,21 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): } if (_evmVersion == langutil::EVMVersion::homestead()) - m_evmVersion = EVMC_HOMESTEAD; + m_evmRevision = EVMC_HOMESTEAD; else if (_evmVersion == langutil::EVMVersion::tangerineWhistle()) - m_evmVersion = EVMC_TANGERINE_WHISTLE; + m_evmRevision = EVMC_TANGERINE_WHISTLE; else if (_evmVersion == langutil::EVMVersion::spuriousDragon()) - m_evmVersion = EVMC_SPURIOUS_DRAGON; + m_evmRevision = EVMC_SPURIOUS_DRAGON; else if (_evmVersion == langutil::EVMVersion::byzantium()) - m_evmVersion = EVMC_BYZANTIUM; + m_evmRevision = EVMC_BYZANTIUM; else if (_evmVersion == langutil::EVMVersion::constantinople()) - m_evmVersion = EVMC_CONSTANTINOPLE; + m_evmRevision = EVMC_CONSTANTINOPLE; else if (_evmVersion == langutil::EVMVersion::istanbul()) - m_evmVersion = EVMC_ISTANBUL; + m_evmRevision = EVMC_ISTANBUL; else if (_evmVersion == langutil::EVMVersion::berlin()) assertThrow(false, Exception, "Berlin is not supported yet."); else //if (_evmVersion == langutil::EVMVersion::petersburg()) - m_evmVersion = EVMC_PETERSBURG; + m_evmRevision = EVMC_PETERSBURG; } evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept @@ -146,7 +147,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept { message.gas -= message.kind == EVMC_CREATE ? eth::GasCosts::txCreateGas : eth::GasCosts::txGas; for (size_t i = 0; i < message.input_size; ++i) - message.gas -= message.input_data[i] == 0 ? eth::GasCosts::txDataZeroGas : eth::GasCosts::txDataNonZeroGas; + message.gas -= message.input_data[i] == 0 ? eth::GasCosts::txDataZeroGas : eth::GasCosts::txDataNonZeroGas(m_evmVersion); if (message.gas < 0) { evmc::result result({}); @@ -191,7 +192,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept evmc::address currentAddress = m_currentAddress; m_currentAddress = message.destination; - evmc::result result = m_vm->execute(*this, m_evmVersion, message, code.data(), code.size()); + evmc::result result = m_vm->execute(*this, m_evmRevision, message, code.data(), code.size()); m_currentAddress = currentAddress; if (message.kind == EVMC_CREATE) diff --git a/test/EVMHost.h b/test/EVMHost.h index 2ffcabcfa..a0adb67bc 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -180,7 +180,10 @@ private: static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; evmc::VM* m_vm = nullptr; - evmc_revision m_evmVersion; + // EVM version requested by the testing tool + langutil::EVMVersion m_evmVersion; + // EVM version requested from EVMC (matches the above) + evmc_revision m_evmRevision; }; diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 8aad4fab7..395c44575 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -37,10 +38,10 @@ namespace solidity namespace test { -#define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt) \ +#define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt, _evmVersion) \ do \ { \ - u256 bzzr1Cost = GasMeter::dataGas(dev::bzzr1Hash(m_compiler.metadata(m_compiler.lastContractName())).asBytes(), true); \ + u256 bzzr1Cost = GasMeter::dataGas(dev::bzzr1Hash(m_compiler.metadata(m_compiler.lastContractName())).asBytes(), true, _evmVersion); \ u256 gasOpt{_gasOpt}; \ u256 gasNoOpt{_gasNoOpt}; \ u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \ @@ -95,22 +96,25 @@ BOOST_AUTO_TEST_CASE(string_storage) m_compiler.overwriteReleaseFlag(true); compileAndRun(sourceCode); - if (Options::get().evmVersion() <= EVMVersion::byzantium()) - CHECK_DEPLOY_GAS(134071, 130763); + auto evmVersion = dev::test::Options::get().evmVersion(); + + if (evmVersion <= EVMVersion::byzantium()) + CHECK_DEPLOY_GAS(134071, 130763, evmVersion); // This is only correct on >=Constantinople. else if (Options::get().useABIEncoderV2) { if (Options::get().optimizeYul) - CHECK_DEPLOY_GAS(151455, 127653); + CHECK_DEPLOY_GAS(151455, 127653, evmVersion); else - CHECK_DEPLOY_GAS(151455, 135371); + CHECK_DEPLOY_GAS(151455, 135371, evmVersion); } else - CHECK_DEPLOY_GAS(126861, 119591); - if (Options::get().evmVersion() >= EVMVersion::byzantium()) + CHECK_DEPLOY_GAS(126861, 119591, evmVersion); + + if (evmVersion >= EVMVersion::byzantium()) { callContractFunction("f()"); - if (Options::get().evmVersion() == EVMVersion::byzantium()) + if (evmVersion == EVMVersion::byzantium()) CHECK_GAS(21551, 21526, 20); // This is only correct on >=Constantinople. else if (Options::get().useABIEncoderV2) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 4b13172b1..bccf27458 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -114,9 +114,10 @@ public: static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) { + auto evmVersion = dev::test::Options::get().evmVersion(); GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas; for (auto i: _data) - gas += i != 0 ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; + gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas; return gas; } From e912c7dca885a3aa68fad539f00ab207d782a5de Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 7 Nov 2019 15:35:13 +0000 Subject: [PATCH 022/130] Run istanbul tests on circleci --- .circleci/soltest_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 7b4564ee7..3a2acd1ac 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -29,7 +29,7 @@ set -e REPODIR="$(realpath $(dirname $0)/..)" for OPTIMIZE in 0 1; do - for EVM in homestead byzantium constantinople petersburg; do + for EVM in homestead byzantium constantinople petersburg istanbul; do EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh done done From 0033d6352205a71cda948ac17c4192a744a8b55a Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 11 Nov 2019 18:00:16 +0100 Subject: [PATCH 023/130] Run more tests on istanbul. --- scripts/tests.sh | 6 +++--- test/externalTests/common.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 01577af1c..266c3184e 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -81,7 +81,7 @@ EVM_VERSIONS="homestead byzantium" if [ -z "$CI" ] then - EVM_VERSIONS+=" constantinople petersburg" + EVM_VERSIONS+=" constantinople petersburg istanbul" fi # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer @@ -91,9 +91,9 @@ do for vm in $EVM_VERSIONS do FORCE_ABIV2_RUNS="no" - if [[ "$vm" == "constantinople" ]] + if [[ "$vm" == "istanbul" ]] then - FORCE_ABIV2_RUNS="no yes" # run both in constantinople + FORCE_ABIV2_RUNS="no yes" # run both in istanbul fi for abiv2 in $FORCE_ABIV2_RUNS do diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index c018789ac..a94b62dc4 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -207,7 +207,7 @@ function run_test for optimize in "${optimizer_settings[@]}" do clean - force_solc_settings "$CONFIG" "$optimize" "petersburg" + force_solc_settings "$CONFIG" "$optimize" "istanbul" # Force ABIEncoderV2 in the last step. Has to be the last because code is modified. if [ "$FORCE_ABIv2" = true ]; then [[ "$optimize" =~ yul ]] && force_abi_v2 From fc2af72a3de23a29158d027c1b84acc8feb6c581 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 14 Nov 2019 12:00:16 +0100 Subject: [PATCH 024/130] Add semantic test for CHAINID in inline assembly --- .../semanticTests/inlineAssembly/chainid.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/chainid.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/chainid.sol b/test/libsolidity/semanticTests/inlineAssembly/chainid.sol new file mode 100644 index 000000000..491e03a54 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/chainid.sol @@ -0,0 +1,12 @@ +contract C { + function f() public returns (uint id) { + assembly { + id := chainid() + } + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=istanbul +// ---- +// f() -> 1 From cbb08ae3dbeafba332d5f306b3bb3946ed365558 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 14 Nov 2019 12:13:05 +0100 Subject: [PATCH 025/130] Add semantic test for SELFBALANCE in inline assembly --- .../semanticTests/inlineAssembly/selfbalance.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/selfbalance.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/selfbalance.sol b/test/libsolidity/semanticTests/inlineAssembly/selfbalance.sol new file mode 100644 index 000000000..8a0a5caae --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/selfbalance.sol @@ -0,0 +1,12 @@ +contract C { + function f() public payable returns (uint ret) { + assembly { + ret := selfbalance() + } + } +} +// ==== +// EVMVersion: >=istanbul +// compileViaYul: also +// ---- +// f(), 254 ether -> 254 From da50285ae3874625eee1c7aaf10d4f77afea9004 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 13:34:56 +0100 Subject: [PATCH 026/130] Update test expectations in GasCostsTests --- test/libsolidity/GasCosts.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 395c44575..a527f7d0f 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -108,8 +108,10 @@ BOOST_AUTO_TEST_CASE(string_storage) else CHECK_DEPLOY_GAS(151455, 135371, evmVersion); } - else + else if (evmVersion < EVMVersion::istanbul()) CHECK_DEPLOY_GAS(126861, 119591, evmVersion); + else + CHECK_DEPLOY_GAS(114173, 107163, evmVersion); if (evmVersion >= EVMVersion::byzantium()) { @@ -124,8 +126,10 @@ BOOST_AUTO_TEST_CASE(string_storage) else CHECK_GAS(21713, 21635, 20); } - else + else if (evmVersion < EVMVersion::istanbul()) CHECK_GAS(21546, 21526, 20); + else + CHECK_GAS(21332, 21322, 20); } } From 17f6a5a060aa776718d549d90a3b709ff6243f7c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 14:22:21 +0100 Subject: [PATCH 027/130] Update test expectations in GasCosts for ABIEncoderV2 --- test/libsolidity/GasCosts.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index a527f7d0f..0a1efcce9 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -104,9 +104,19 @@ BOOST_AUTO_TEST_CASE(string_storage) else if (Options::get().useABIEncoderV2) { if (Options::get().optimizeYul) - CHECK_DEPLOY_GAS(151455, 127653, evmVersion); + { + if (evmVersion < EVMVersion::istanbul()) + CHECK_DEPLOY_GAS(151455, 127653, evmVersion); + else + CHECK_DEPLOY_GAS(123, 113821, evmVersion); + } else - CHECK_DEPLOY_GAS(151455, 135371, evmVersion); + { + if (evmVersion < EVMVersion::istanbul()) + CHECK_DEPLOY_GAS(151455, 135371, evmVersion); + else + CHECK_DEPLOY_GAS(134711, 120083, evmVersion); + } } else if (evmVersion < EVMVersion::istanbul()) CHECK_DEPLOY_GAS(126861, 119591, evmVersion); @@ -122,9 +132,19 @@ BOOST_AUTO_TEST_CASE(string_storage) else if (Options::get().useABIEncoderV2) { if (Options::get().optimizeYul) - CHECK_GAS(21713, 21567, 20); + { + if (evmVersion < EVMVersion::istanbul()) + CHECK_GAS(21713, 21567, 20); + else + CHECK_GAS(21713, 21351, 20); + } else - CHECK_GAS(21713, 21635, 20); + { + if (evmVersion < EVMVersion::istanbul()) + CHECK_GAS(21713, 21635, 20); + else + CHECK_GAS(21499, 21431, 20); + } } else if (evmVersion < EVMVersion::istanbul()) CHECK_GAS(21546, 21526, 20); From 17ed0f0898b74984f70c4c39210c23885e0e7a60 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 14:25:40 +0100 Subject: [PATCH 028/130] Set costs to 0 for unreachable cases in GasCosts tests --- test/libsolidity/GasCosts.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 0a1efcce9..c56187148 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -105,17 +105,18 @@ BOOST_AUTO_TEST_CASE(string_storage) { if (Options::get().optimizeYul) { + // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(151455, 127653, evmVersion); + CHECK_DEPLOY_GAS(0, 127653, evmVersion); else - CHECK_DEPLOY_GAS(123, 113821, evmVersion); + CHECK_DEPLOY_GAS(0, 113821, evmVersion); } else { if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(151455, 135371, evmVersion); + CHECK_DEPLOY_GAS(0, 135371, evmVersion); else - CHECK_DEPLOY_GAS(134711, 120083, evmVersion); + CHECK_DEPLOY_GAS(0, 120083, evmVersion); } } else if (evmVersion < EVMVersion::istanbul()) @@ -134,16 +135,16 @@ BOOST_AUTO_TEST_CASE(string_storage) if (Options::get().optimizeYul) { if (evmVersion < EVMVersion::istanbul()) - CHECK_GAS(21713, 21567, 20); + CHECK_GAS(0, 21567, 20); else - CHECK_GAS(21713, 21351, 20); + CHECK_GAS(0, 21351, 20); } else { if (evmVersion < EVMVersion::istanbul()) - CHECK_GAS(21713, 21635, 20); + CHECK_GAS(0, 21635, 20); else - CHECK_GAS(21499, 21431, 20); + CHECK_GAS(0, 21431, 20); } } else if (evmVersion < EVMVersion::istanbul()) From 23cad71c88b7992d385d2f9ad2c44557622bef87 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 19 Nov 2019 13:07:32 +0100 Subject: [PATCH 029/130] Disallow conversion from ``super`` --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 7 +++++++ libsolidity/codegen/CompilerUtils.cpp | 3 +++ .../not_allowed_conversion_from_super.sol | 15 +++++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol diff --git a/Changelog.md b/Changelog.md index d3294901f..6085c722b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error when using function pointers as arguments. * Type Checker: Disallow constructor of the same class to be used as modifier. * Type Checker: Treat magic variables as unknown identifiers in inline assembly. + * Code Generator: Fix internal error when trying to convert ``super`` to a different type diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f5e315829..ccdadc5df 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1433,6 +1433,9 @@ Type const* ContractType::encodingType() const BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { + if (m_super) + return false; + if (*this == _convertTo) return true; if (_convertTo.category() == Category::Contract) @@ -1450,8 +1453,12 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (m_super) + return false; + if (auto const* addressType = dynamic_cast(&_convertTo)) return isPayable() || (addressType->stateMutability() < StateMutability::Payable); + return isImplicitlyConvertibleTo(_convertTo); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a275a6e03..5333cfddb 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -711,6 +711,9 @@ void CompilerUtils::convertType( Type::Category stackTypeCategory = _typeOnStack.category(); Type::Category targetTypeCategory = _targetType.category(); + if (auto contrType = dynamic_cast(&_typeOnStack)) + solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\""); + bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum); bool chopSignBitsPending = _chopSignBits && targetTypeCategory == Type::Category::Integer; if (chopSignBitsPending) diff --git a/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol new file mode 100644 index 000000000..dc6f00f43 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol @@ -0,0 +1,15 @@ +contract S +{ + int o; + function foo() public returns (int) { return o = 3; } +} + +contract B is S +{ + function fii() public + { + o = S(super).foo(); + } +} +// ---- +// TypeError: (129-137): Explicit type conversion not allowed from "contract super B" to "contract S". From be849b3c4752c49d89a6eb549c4bb674c0c04aca Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 19 Nov 2019 16:42:49 +0100 Subject: [PATCH 030/130] Replace boost::variant by std::variant in libyul --- libsolidity/analysis/ViewPureChecker.cpp | 22 ++++---- .../codegen/ir/IRGeneratorForStatements.cpp | 4 +- libyul/AsmAnalysis.cpp | 24 ++++---- libyul/AsmAnalysis.h | 4 +- libyul/AsmData.h | 5 +- libyul/AsmDataForward.h | 6 +- libyul/AsmParser.cpp | 56 +++++++++---------- libyul/AsmParser.h | 4 +- libyul/AsmPrinter.cpp | 21 +++---- libyul/AsmPrinter.h | 4 +- libyul/AsmScope.cpp | 4 +- libyul/AsmScope.h | 7 +-- libyul/AsmScopeFiller.cpp | 10 ++-- libyul/AsmScopeFiller.h | 4 +- libyul/backends/evm/ConstantOptimiser.cpp | 12 ++-- libyul/backends/evm/EVMCodeTransform.cpp | 36 ++++++------ libyul/backends/evm/EVMCodeTransform.h | 4 +- libyul/backends/evm/EVMDialect.cpp | 4 +- libyul/backends/wasm/BinaryTransform.cpp | 14 ++--- libyul/backends/wasm/BinaryTransform.h | 2 +- libyul/backends/wasm/EVMToEWasmTranslator.cpp | 4 +- libyul/backends/wasm/EWasmAST.h | 4 +- libyul/backends/wasm/EWasmCodeTransform.cpp | 14 ++--- libyul/backends/wasm/EWasmCodeTransform.h | 2 +- libyul/backends/wasm/EWasmToText.cpp | 2 +- libyul/backends/wasm/EWasmToText.h | 2 +- libyul/backends/wasm/WordSizeTransform.cpp | 47 ++++++++-------- libyul/optimiser/ASTCopier.cpp | 4 +- libyul/optimiser/ASTCopier.h | 6 +- libyul/optimiser/ASTWalker.cpp | 8 +-- libyul/optimiser/ASTWalker.h | 6 +- libyul/optimiser/BlockFlattener.cpp | 4 +- .../CommonSubexpressionEliminator.cpp | 12 ++-- libyul/optimiser/ConditionalSimplifier.cpp | 12 ++-- libyul/optimiser/ConditionalUnsimplifier.cpp | 28 +++++----- libyul/optimiser/ControlFlowSimplifier.cpp | 6 +- libyul/optimiser/DataFlowAnalyzer.cpp | 13 +++-- libyul/optimiser/DeadCodeEliminator.cpp | 2 +- libyul/optimiser/Disambiguator.h | 2 - libyul/optimiser/ExpressionInliner.cpp | 6 +- libyul/optimiser/ExpressionInliner.h | 2 - libyul/optimiser/ExpressionJoiner.cpp | 12 ++-- libyul/optimiser/ExpressionSplitter.cpp | 2 +- libyul/optimiser/ForLoopConditionIntoBody.cpp | 4 +- .../optimiser/ForLoopConditionOutOfBody.cpp | 16 +++--- libyul/optimiser/ForLoopInitRewriter.cpp | 4 +- libyul/optimiser/FullInliner.cpp | 24 ++++---- libyul/optimiser/FullInliner.h | 2 - libyul/optimiser/FunctionGrouper.cpp | 8 +-- libyul/optimiser/FunctionHoister.cpp | 4 +- .../InlinableExpressionFunctionFinder.cpp | 6 +- libyul/optimiser/KnowledgeBase.cpp | 23 ++++---- libyul/optimiser/LoadResolver.cpp | 12 ++-- libyul/optimiser/MainFunction.cpp | 6 +- libyul/optimiser/Metrics.cpp | 24 ++++---- libyul/optimiser/NameDisplacer.cpp | 4 +- libyul/optimiser/OptimizerUtilities.cpp | 2 +- .../optimiser/RedundantAssignEliminator.cpp | 2 +- libyul/optimiser/Rematerialiser.cpp | 10 ++-- libyul/optimiser/SSAReverser.cpp | 12 ++-- libyul/optimiser/SSATransform.cpp | 32 +++++------ libyul/optimiser/Semantics.cpp | 16 +++--- libyul/optimiser/SimplificationRules.cpp | 20 +++---- libyul/optimiser/StackCompressor.cpp | 10 ++-- libyul/optimiser/StructuralSimplifier.cpp | 6 +- libyul/optimiser/Substitution.cpp | 4 +- libyul/optimiser/Suite.cpp | 8 +-- libyul/optimiser/SyntacticalEquality.cpp | 4 +- libyul/optimiser/UnusedPruner.cpp | 12 ++-- libyul/optimiser/VarDeclInitializer.cpp | 2 +- libyul/optimiser/VarNameCleaner.cpp | 4 +- test/libyul/Common.cpp | 4 +- test/libyul/YulOptimizerTest.cpp | 3 +- test/tools/yulInterpreter/Interpreter.cpp | 5 +- test/tools/yulopti.cpp | 3 +- 75 files changed, 371 insertions(+), 372 deletions(-) diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 58baec906..b18261113 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -21,7 +21,9 @@ #include #include #include + #include +#include using namespace std; using namespace dev; @@ -31,7 +33,7 @@ using namespace dev::solidity; namespace { -class AssemblyViewPureChecker: public boost::static_visitor +class AssemblyViewPureChecker { public: explicit AssemblyViewPureChecker( @@ -52,21 +54,21 @@ public: { checkInstruction(_instr.location, _instr.instruction); for (auto const& arg: _instr.arguments) - boost::apply_visitor(*this, arg); + std::visit(*this, arg); } void operator()(yul::ExpressionStatement const& _expr) { - boost::apply_visitor(*this, _expr.expression); + std::visit(*this, _expr.expression); } void operator()(yul::StackAssignment const&) {} void operator()(yul::Assignment const& _assignment) { - boost::apply_visitor(*this, *_assignment.value); + std::visit(*this, *_assignment.value); } void operator()(yul::VariableDeclaration const& _varDecl) { if (_varDecl.value) - boost::apply_visitor(*this, *_varDecl.value); + std::visit(*this, *_varDecl.value); } void operator()(yul::FunctionDefinition const& _funDef) { @@ -80,16 +82,16 @@ public: checkInstruction(_funCall.location, *fun->instruction); for (auto const& arg: _funCall.arguments) - boost::apply_visitor(*this, arg); + std::visit(*this, arg); } void operator()(yul::If const& _if) { - boost::apply_visitor(*this, *_if.condition); + std::visit(*this, *_if.condition); (*this)(_if.body); } void operator()(yul::Switch const& _switch) { - boost::apply_visitor(*this, *_switch.expression); + std::visit(*this, *_switch.expression); for (auto const& _case: _switch.cases) { if (_case.value) @@ -100,7 +102,7 @@ public: void operator()(yul::ForLoop const& _for) { (*this)(_for.pre); - boost::apply_visitor(*this, *_for.condition); + std::visit(*this, *_for.condition); (*this)(_for.body); (*this)(_for.post); } @@ -113,7 +115,7 @@ public: void operator()(yul::Block const& _block) { for (auto const& s: _block.statements) - boost::apply_visitor(*this, s); + std::visit(*this, s); } private: diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5dcb811cc..b2d47292f 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -833,9 +833,9 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) yul::Statement modified = bodyCopier(_inlineAsm.operations()); - solAssert(modified.type() == typeid(yul::Block), ""); + solAssert(holds_alternative(modified), ""); - m_code << yul::AsmPrinter()(boost::get(std::move(modified))) << "\n"; + m_code << yul::AsmPrinter()(std::get(std::move(modified))) << "\n"; return false; } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 9c62737d8..35e933cd1 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -214,7 +214,7 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) { int initialStackHeight = m_stackHeight; - bool success = boost::apply_visitor(*this, _statement.expression); + bool success = std::visit(*this, _statement.expression); if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose)) { Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; @@ -249,7 +249,7 @@ bool AsmAnalyzer::operator()(Assignment const& _assignment) int const expectedItems = _assignment.variableNames.size(); solAssert(expectedItems >= 1, ""); int const stackHeight = m_stackHeight; - bool success = boost::apply_visitor(*this, *_assignment.value); + bool success = std::visit(*this, *_assignment.value); if ((m_stackHeight - stackHeight) != expectedItems) { m_errorReporter.declarationError( @@ -276,7 +276,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) if (_varDecl.value) { int const stackHeight = m_stackHeight; - success = boost::apply_visitor(*this, *_varDecl.value); + success = std::visit(*this, *_varDecl.value); int numValues = m_stackHeight - stackHeight; if (numValues != numVariables) { @@ -298,7 +298,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) for (auto const& variable: _varDecl.variables) { expectValidType(variable.type.str(), variable.location); - m_activeVariables.insert(&boost::get(m_currentScope->identifiers.at(variable.name))); + m_activeVariables.insert(&std::get(m_currentScope->identifiers.at(variable.name))); } m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; @@ -313,7 +313,7 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) for (auto const& var: _funDef.parameters + _funDef.returnVariables) { expectValidType(var.type.str(), var.location); - m_activeVariables.insert(&boost::get(varScope.identifiers.at(var.name))); + m_activeVariables.insert(&std::get(varScope.identifiers.at(var.name))); } int const stackHeight = m_stackHeight; @@ -388,15 +388,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) success = false; else if (needsLiteralArguments) { - if (arg.type() != typeid(Literal)) + if (!holds_alternative(arg)) m_errorReporter.typeError( _funCall.functionName.location, "Function expects direct literals as arguments." ); - else if (!m_dataNames.count(boost::get(arg).value)) + else if (!m_dataNames.count(std::get(arg).value)) m_errorReporter.typeError( _funCall.functionName.location, - "Unknown data object \"" + boost::get(arg).value.str() + "\"." + "Unknown data object \"" + std::get(arg).value.str() + "\"." ); } } @@ -556,7 +556,7 @@ bool AsmAnalyzer::operator()(Block const& _block) int const initialStackHeight = m_stackHeight; for (auto const& s: _block.statements) - if (!boost::apply_visitor(*this, s)) + if (!std::visit(*this, s)) success = false; m_stackHeight -= scope(&_block).numberOfVariables(); @@ -585,7 +585,7 @@ bool AsmAnalyzer::expectExpression(Expression const& _expr) { bool success = true; int const initialHeight = m_stackHeight; - if (!boost::apply_visitor(*this, _expr)) + if (!std::visit(*this, _expr)) success = false; if (!expectDeposit(1, initialHeight, locationOf(_expr))) success = false; @@ -616,12 +616,12 @@ bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { // Check that it is a variable - if (var->type() != typeid(Scope::Variable)) + if (!holds_alternative(*var)) { m_errorReporter.typeError(_variable.location, "Assignment requires variable."); success = false; } - else if (!m_activeVariables.count(&boost::get(*var))) + else if (!m_activeVariables.count(&std::get(*var))) { m_errorReporter.declarationError( _variable.location, diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 14337d323..40d9a1d51 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -30,8 +30,6 @@ #include #include -#include - #include #include #include @@ -53,7 +51,7 @@ struct AsmAnalysisInfo; * references and performs other checks. * If all these checks pass, code generation should not throw errors. */ -class AsmAnalyzer: public boost::static_visitor +class AsmAnalyzer { public: explicit AsmAnalyzer( diff --git a/libyul/AsmData.h b/libyul/AsmData.h index cedff1106..33bf48090 100644 --- a/libyul/AsmData.h +++ b/libyul/AsmData.h @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -83,7 +82,7 @@ struct Break { langutil::SourceLocation location; }; /// Continue statement (valid within for loop) struct Continue { langutil::SourceLocation location; }; -struct LocationExtractor: boost::static_visitor +struct LocationExtractor { template langutil::SourceLocation operator()(T const& _node) const { @@ -94,7 +93,7 @@ struct LocationExtractor: boost::static_visitor /// Extracts the source location from an inline assembly node. template inline langutil::SourceLocation locationOf(T const& _node) { - return boost::apply_visitor(LocationExtractor(), _node); + return std::visit(LocationExtractor(), _node); } } diff --git a/libyul/AsmDataForward.h b/libyul/AsmDataForward.h index d01b1dffa..29c9db3f3 100644 --- a/libyul/AsmDataForward.h +++ b/libyul/AsmDataForward.h @@ -22,7 +22,7 @@ #pragma once -#include +#include namespace yul { @@ -48,7 +48,7 @@ struct Block; struct TypedName; -using Expression = boost::variant; -using Statement = boost::variant; +using Expression = std::variant; +using Statement = std::variant; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index ae3056f21..75e488290 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -184,7 +184,7 @@ Statement Parser::parseStatement() while (true) { - if (elementary.type() != typeid(Identifier)) + if (!holds_alternative(elementary)) { auto const token = currentToken() == Token::Comma ? "," : ":="; @@ -196,7 +196,7 @@ Statement Parser::parseStatement() ); } - auto const& identifier = boost::get(elementary); + auto const& identifier = std::get(elementary); if (m_dialect.builtin(identifier.name)) fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); @@ -212,7 +212,7 @@ Statement Parser::parseStatement() } Assignment assignment = - createWithLocation(boost::get(elementary).location); + createWithLocation(std::get(elementary).location); assignment.variableNames = std::move(variableNames); expectToken(Token::AssemblyAssign); @@ -224,10 +224,10 @@ Statement Parser::parseStatement() } case Token::Colon: { - if (elementary.type() != typeid(Identifier)) + if (!holds_alternative(elementary)) fatalParserError("Label name must precede \":\"."); - Identifier const& identifier = boost::get(elementary); + Identifier const& identifier = std::get(elementary); advance(); @@ -245,20 +245,20 @@ Statement Parser::parseStatement() break; } - if (elementary.type() == typeid(Identifier)) + if (holds_alternative(elementary)) { - Identifier& identifier = boost::get(elementary); + Identifier& identifier = std::get(elementary); return ExpressionStatement{identifier.location, { move(identifier) }}; } - else if (elementary.type() == typeid(Literal)) + else if (holds_alternative(elementary)) { - Expression expr = boost::get(elementary); + Expression expr = std::get(elementary); return ExpressionStatement{locationOf(expr), expr}; } else { - solAssert(elementary.type() == typeid(Instruction), "Invalid elementary operation."); - return boost::get(elementary); + solAssert(holds_alternative(elementary), "Invalid elementary operation."); + return std::get(elementary); } } @@ -272,9 +272,9 @@ Case Parser::parseCase() { advance(); ElementaryOperation literal = parseElementaryOperation(); - if (literal.type() != typeid(Literal)) + if (!holds_alternative(literal)) fatalParserError("Literal expected."); - _case.value = make_unique(boost::get(std::move(literal))); + _case.value = make_unique(std::get(std::move(literal))); } else solAssert(false, "Case or default case expected."); @@ -311,12 +311,12 @@ Expression Parser::parseExpression() RecursionGuard recursionGuard(*this); ElementaryOperation operation = parseElementaryOperation(); - if (operation.type() == typeid(FunctionCall)) + if (holds_alternative(operation)) return parseCall(std::move(operation)); - else if (operation.type() == typeid(Instruction)) + else if (holds_alternative(operation)) { solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); - Instruction const& instr = boost::get(operation); + Instruction const& instr = std::get(operation); // Disallow instructions returning multiple values (and DUP/SWAP) as expression. if ( instructionInfo(instr.instruction).ret != 1 || @@ -345,19 +345,19 @@ Expression Parser::parseExpression() } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); - else if (operation.type() == typeid(Instruction)) + else if (holds_alternative(operation)) { // Instructions not taking arguments are allowed as expressions. solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); - Instruction& instr = boost::get(operation); + Instruction& instr = std::get(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } - else if (operation.type() == typeid(Identifier)) - return boost::get(operation); + else if (holds_alternative(operation)) + return std::get(operation); else { - solAssert(operation.type() == typeid(Literal), ""); - return boost::get(operation); + solAssert(holds_alternative(operation), ""); + return std::get(operation); } } @@ -537,10 +537,10 @@ FunctionDefinition Parser::parseFunctionDefinition() Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) { RecursionGuard recursionGuard(*this); - if (_initialOp.type() == typeid(Instruction)) + if (holds_alternative(_initialOp)) { solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); - Instruction& instruction = boost::get(_initialOp); + Instruction& instruction = std::get(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; ret.location = std::move(instruction.location); @@ -591,16 +591,16 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) expectToken(Token::RParen); return ret; } - else if (_initialOp.type() == typeid(Identifier) || _initialOp.type() == typeid(FunctionCall)) + else if (holds_alternative(_initialOp) || holds_alternative(_initialOp)) { FunctionCall ret; - if (_initialOp.type() == typeid(Identifier)) + if (holds_alternative(_initialOp)) { - ret.functionName = std::move(boost::get(_initialOp)); + ret.functionName = std::move(std::get(_initialOp)); ret.location = ret.functionName.location; } else - ret = std::move(boost::get(_initialOp)); + ret = std::move(std::get(_initialOp)); expectToken(Token::LParen); if (currentToken() != Token::RParen) { diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index 6f88a211e..80b8edc26 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -30,9 +30,9 @@ #include #include +#include #include - namespace yul { @@ -56,7 +56,7 @@ public: static std::map const& instructions(); protected: - using ElementaryOperation = boost::variant; + using ElementaryOperation = std::variant; /// Creates an inline assembly node with the given source location. template T createWithLocation(langutil::SourceLocation const& _loc = {}) const diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index 71151a0cf..7814100aa 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -103,14 +103,15 @@ string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstructio boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( - _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), - ", ") + + _functionalInstruction.arguments | boost::adaptors::transformed([&](auto&& _node) { return std::visit(*this, _node); }), + ", " + ) + ")"; } string AsmPrinter::operator()(ExpressionStatement const& _statement) const { - return boost::apply_visitor(*this, _statement.expression); + return std::visit(*this, _statement.expression); } string AsmPrinter::operator()(Label const& _label) const @@ -133,7 +134,7 @@ string AsmPrinter::operator()(Assignment const& _assignment) const string variables = (*this)(_assignment.variableNames.front()); for (size_t i = 1; i < _assignment.variableNames.size(); ++i) variables += ", " + (*this)(_assignment.variableNames[i]); - return variables + " := " + boost::apply_visitor(*this, *_assignment.value); + return variables + " := " + std::visit(*this, *_assignment.value); } string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) const @@ -148,7 +149,7 @@ string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) c if (_variableDeclaration.value) { out += " := "; - out += boost::apply_visitor(*this, *_variableDeclaration.value); + out += std::visit(*this, *_variableDeclaration.value); } return out; } @@ -183,7 +184,7 @@ string AsmPrinter::operator()(FunctionCall const& _functionCall) const return (*this)(_functionCall.functionName) + "(" + boost::algorithm::join( - _functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), + _functionCall.arguments | boost::adaptors::transformed([&](auto&& _node) { return std::visit(*this, _node); }), ", " ) + ")"; } @@ -195,13 +196,13 @@ string AsmPrinter::operator()(If const& _if) const char delim = '\n'; if (body.find('\n') == string::npos) delim = ' '; - return "if " + boost::apply_visitor(*this, *_if.condition) + delim + (*this)(_if.body); + return "if " + std::visit(*this, *_if.condition) + delim + (*this)(_if.body); } string AsmPrinter::operator()(Switch const& _switch) const { solAssert(_switch.expression, "Invalid expression pointer."); - string out = "switch " + boost::apply_visitor(*this, *_switch.expression); + string out = "switch " + std::visit(*this, *_switch.expression); for (auto const& _case: _switch.cases) { if (!_case.value) @@ -217,7 +218,7 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const { solAssert(_forLoop.condition, "Invalid for loop condition."); string pre = (*this)(_forLoop.pre); - string condition = boost::apply_visitor(*this, *_forLoop.condition); + string condition = std::visit(*this, *_forLoop.condition); string post = (*this)(_forLoop.post); char delim = '\n'; if ( @@ -246,7 +247,7 @@ string AsmPrinter::operator()(Block const& _block) const if (_block.statements.empty()) return "{ }"; string body = boost::algorithm::join( - _block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)), + _block.statements | boost::adaptors::transformed([&](auto&& _node) { return std::visit(*this, _node); }), "\n" ); if (body.size() < 30 && body.find('\n') == string::npos) diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 82fed24f2..cf4fcecb2 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -26,12 +26,10 @@ #include -#include - namespace yul { -class AsmPrinter: public boost::static_visitor +class AsmPrinter { public: explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} diff --git a/libyul/AsmScope.cpp b/libyul/AsmScope.cpp index 252361d27..b10130e57 100644 --- a/libyul/AsmScope.cpp +++ b/libyul/AsmScope.cpp @@ -58,7 +58,7 @@ Scope::Identifier* Scope::lookup(YulString _name) auto id = s->identifiers.find(_name); if (id != s->identifiers.end()) { - if (crossedFunctionBoundary && id->second.type() == typeid(Scope::Variable)) + if (crossedFunctionBoundary && holds_alternative(id->second)) return nullptr; else return &id->second; @@ -84,7 +84,7 @@ size_t Scope::numberOfVariables() const { size_t count = 0; for (auto const& identifier: identifiers) - if (identifier.second.type() == typeid(Scope::Variable)) + if (holds_alternative(identifier.second)) count++; return count; } diff --git a/libyul/AsmScope.h b/libyul/AsmScope.h index 2c7af4ef1..e8cd6ef64 100644 --- a/libyul/AsmScope.h +++ b/libyul/AsmScope.h @@ -26,11 +26,10 @@ #include -#include - #include #include #include +#include namespace yul { @@ -48,7 +47,7 @@ struct Scope std::vector returns; }; - using Identifier = boost::variant; + using Identifier = std::variant; using Visitor = dev::GenericVisitor; using NonconstVisitor = dev::GenericVisitor; @@ -74,7 +73,7 @@ struct Scope { if (Identifier* id = lookup(_name)) { - boost::apply_visitor(_visitor, *id); + std::visit(_visitor, *id); return true; } else diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 1706b147d..3c940c92f 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -47,7 +47,7 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): bool ScopeFiller::operator()(ExpressionStatement const& _expr) { - return boost::apply_visitor(*this, _expr.expression); + return std::visit(*this, _expr.expression); } bool ScopeFiller::operator()(Label const& _item) @@ -116,7 +116,7 @@ bool ScopeFiller::operator()(ForLoop const& _forLoop) if (!(*this)(_forLoop.pre)) success = false; m_currentScope = &scope(&_forLoop.pre); - if (!boost::apply_visitor(*this, *_forLoop.condition)) + if (!std::visit(*this, *_forLoop.condition)) success = false; if (!(*this)(_forLoop.body)) success = false; @@ -137,11 +137,11 @@ bool ScopeFiller::operator()(Block const& _block) // First visit all functions to make them create // an entry in the scope according to their visibility. for (auto const& s: _block.statements) - if (s.type() == typeid(FunctionDefinition)) - if (!registerFunction(boost::get(s))) + if (holds_alternative(s)) + if (!registerFunction(std::get(s))) success = false; for (auto const& s: _block.statements) - if (!boost::apply_visitor(*this, s)) + if (!std::visit(*this, s)) success = false; m_currentScope = m_currentScope->superScope; diff --git a/libyul/AsmScopeFiller.h b/libyul/AsmScopeFiller.h index 220538bb2..f019889c5 100644 --- a/libyul/AsmScopeFiller.h +++ b/libyul/AsmScopeFiller.h @@ -22,8 +22,6 @@ #include -#include - #include #include @@ -44,7 +42,7 @@ struct AsmAnalysisInfo; * Fills scopes with identifiers and checks for name clashes. * Does not resolve references. */ -class ScopeFiller: public boost::static_visitor +class ScopeFiller { public: ScopeFiller(AsmAnalysisInfo& _info, langutil::ErrorReporter& _errorReporter); diff --git a/libyul/backends/evm/ConstantOptimiser.cpp b/libyul/backends/evm/ConstantOptimiser.cpp index f8b4854d8..74fe7667e 100644 --- a/libyul/backends/evm/ConstantOptimiser.cpp +++ b/libyul/backends/evm/ConstantOptimiser.cpp @@ -27,6 +27,8 @@ #include +#include + using namespace std; using namespace dev; using namespace yul; @@ -35,13 +37,13 @@ using Representation = ConstantOptimiser::Representation; namespace { -struct MiniEVMInterpreter: boost::static_visitor +struct MiniEVMInterpreter { explicit MiniEVMInterpreter(EVMDialect const& _dialect): m_dialect(_dialect) {} u256 eval(Expression const& _expr) { - return boost::apply_visitor(*this, _expr); + return std::visit(*this, _expr); } u256 eval(dev::eth::Instruction _instr, vector const& _arguments) @@ -92,9 +94,9 @@ struct MiniEVMInterpreter: boost::static_visitor void ConstantOptimiser::visit(Expression& _e) { - if (_e.type() == typeid(Literal)) + if (holds_alternative(_e)) { - Literal const& literal = boost::get(_e); + Literal const& literal = std::get(_e); if (literal.kind != LiteralKind::Number) return; @@ -115,7 +117,7 @@ Expression const* RepresentationFinder::tryFindRepresentation(dev::u256 const& _ return nullptr; Representation const& repr = findRepresentation(_value); - if (repr.expression->type() == typeid(Literal)) + if (holds_alternative(*repr.expression)) return nullptr; else return repr.expression.get(); diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index d3493b573..ee564ed6b 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -29,6 +29,8 @@ #include +#include + using namespace std; using namespace dev; using namespace yul; @@ -146,9 +148,9 @@ void CodeTransform::freeUnusedVariables() return; for (auto const& identifier: m_scope->identifiers) - if (identifier.second.type() == typeid(Scope::Variable)) + if (holds_alternative(identifier.second)) { - Scope::Variable const& var = boost::get(identifier.second); + Scope::Variable const& var = std::get(identifier.second); if (m_variablesScheduledForDeletion.count(&var)) deleteVariable(var); } @@ -179,7 +181,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) int height = m_assembly.stackHeight(); if (_varDecl.value) { - boost::apply_visitor(*this, *_varDecl.value); + std::visit(*this, *_varDecl.value); expectDeposit(numVariables, height); } else @@ -193,7 +195,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex) { YulString varName = _varDecl.variables[varIndex].name; - auto& var = boost::get(m_scope->identifiers.at(varName)); + auto& var = std::get(m_scope->identifiers.at(varName)); m_context->variableStackHeights[&var] = height + varIndex; if (!m_allowStackOpt) continue; @@ -242,7 +244,7 @@ void CodeTransform::stackError(StackTooDeepError _error, int _targetStackHeight) void CodeTransform::operator()(Assignment const& _assignment) { int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, *_assignment.value); + std::visit(*this, *_assignment.value); expectDeposit(_assignment.variableNames.size(), height); m_assembly.setSourceLocation(_assignment.location); @@ -261,7 +263,7 @@ void CodeTransform::operator()(StackAssignment const& _assignment) void CodeTransform::operator()(ExpressionStatement const& _statement) { m_assembly.setSourceLocation(_statement.location); - boost::apply_visitor(*this, _statement.expression); + std::visit(*this, _statement.expression); checkStackHeight(&_statement); } @@ -271,7 +273,7 @@ void CodeTransform::operator()(Label const& _label) m_assembly.setSourceLocation(_label.location); solAssert(m_scope, ""); solAssert(m_scope->identifiers.count(_label.name), ""); - Scope::Label& label = boost::get(m_scope->identifiers.at(_label.name)); + Scope::Label& label = std::get(m_scope->identifiers.at(_label.name)); m_assembly.appendLabel(labelID(label)); checkStackHeight(&_label); } @@ -340,7 +342,7 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) solAssert(_instruction.arguments.size() == 1, ""); } m_assembly.setSourceLocation(_instruction.location); - auto label = labelFromIdentifier(boost::get(_instruction.arguments.at(0))); + auto label = labelFromIdentifier(std::get(_instruction.arguments.at(0))); if (isJumpI) m_assembly.appendJumpToIf(label); else @@ -476,7 +478,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) { solAssert(m_scope, ""); solAssert(m_scope->identifiers.count(_function.name), ""); - Scope::Function& function = boost::get(m_scope->identifiers.at(_function.name)); + Scope::Function& function = std::get(m_scope->identifiers.at(_function.name)); int const localStackAdjustment = m_evm15 ? 0 : 1; int height = localStackAdjustment; @@ -485,7 +487,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) solAssert(varScope, ""); for (auto const& v: _function.parameters | boost::adaptors::reversed) { - auto& var = boost::get(varScope->identifiers.at(v.name)); + auto& var = std::get(varScope->identifiers.at(v.name)); m_context->variableStackHeights[&var] = height++; } @@ -503,7 +505,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) for (auto const& v: _function.returnVariables) { - auto& var = boost::get(varScope->identifiers.at(v.name)); + auto& var = std::get(varScope->identifiers.at(v.name)); m_context->variableStackHeights[&var] = height++; // Preset stack slots for return variables to zero. m_assembly.appendConstant(u256(0)); @@ -717,7 +719,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(YulString _name, Scope: void CodeTransform::visitExpression(Expression const& _expression) { int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, _expression); + std::visit(*this, _expression); expectDeposit(1, height); } @@ -728,7 +730,7 @@ void CodeTransform::visitStatements(vector const& _statements) for (auto const& statement: _statements) { freeUnusedVariables(); - auto const* functionDefinition = boost::get(&statement); + auto const* functionDefinition = std::get_if(&statement); if (functionDefinition && !jumpTarget) { m_assembly.setSourceLocation(locationOf(statement)); @@ -741,7 +743,7 @@ void CodeTransform::visitStatements(vector const& _statements) jumpTarget = std::nullopt; } - boost::apply_visitor(*this, statement); + std::visit(*this, statement); } // we may have a leftover jumpTarget if (jumpTarget) @@ -759,9 +761,9 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight // pop variables solAssert(m_info.scopes.at(&_block).get() == m_scope, ""); for (auto const& id: m_scope->identifiers) - if (id.second.type() == typeid(Scope::Variable)) + if (holds_alternative(id.second)) { - Scope::Variable const& var = boost::get(id.second); + Scope::Variable const& var = std::get(id.second); if (m_allowStackOpt) { solAssert(!m_context->variableStackHeights.count(&var), ""); @@ -789,7 +791,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) solAssert(m_scope, ""); if (auto var = m_scope->lookup(_variableName.name)) { - Scope::Variable const& _var = boost::get(*var); + Scope::Variable const& _var = std::get(*var); if (int heightDiff = variableHeightDiff(_var, _variableName.name, true)) m_assembly.appendInstruction(dev::eth::swapInstruction(heightDiff - 1)); m_assembly.appendInstruction(dev::eth::Instruction::POP); diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index e44953807..fde041686 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -27,8 +27,6 @@ #include #include -#include - #include #include @@ -107,7 +105,7 @@ private: Scope* m_scope = nullptr; }; -class CodeTransform: public boost::static_visitor<> +class CodeTransform { public: /// Create the code transformer. diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index a9cbd2cbc..0da3e8d68 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -113,7 +113,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_context.currentObject, "No object available."); yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); - YulString dataName = boost::get(arg).value; + YulString dataName = std::get(arg).value; if (_context.currentObject->name == dataName) _assembly.appendAssemblySize(); else @@ -134,7 +134,7 @@ map createBuiltins(langutil::EVMVersion _evmVe yulAssert(_context.currentObject, "No object available."); yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); - YulString dataName = boost::get(arg).value; + YulString dataName = std::get(arg).value; if (_context.currentObject->name == dataName) _assembly.appendConstant(0); else diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 18f02ec02..563d4d569 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -298,12 +298,12 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call) // they are references to object names that should not end up in the code. if (_call.functionName == "dataoffset") { - string name = boost::get(_call.arguments.at(0)).value; + string name = std::get(_call.arguments.at(0)).value; return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first); } else if (_call.functionName == "datasize") { - string name = boost::get(_call.arguments.at(0)).value; + string name = std::get(_call.arguments.at(0)).value; return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second); } @@ -333,7 +333,7 @@ bytes BinaryTransform::operator()(FunctionCall const& _call) bytes BinaryTransform::operator()(LocalAssignment const& _assignment) { return - boost::apply_visitor(*this, *_assignment.value) + + std::visit(*this, *_assignment.value) + toBytes(Opcode::LocalSet) + lebEncode(m_locals.at(_assignment.variableName)); } @@ -341,7 +341,7 @@ bytes BinaryTransform::operator()(LocalAssignment const& _assignment) bytes BinaryTransform::operator()(GlobalAssignment const& _assignment) { return - boost::apply_visitor(*this, *_assignment.value) + + std::visit(*this, *_assignment.value) + toBytes(Opcode::GlobalSet) + lebEncode(m_globals.at(_assignment.variableName)); } @@ -349,7 +349,7 @@ bytes BinaryTransform::operator()(GlobalAssignment const& _assignment) bytes BinaryTransform::operator()(If const& _if) { bytes result = - boost::apply_visitor(*this, *_if.condition) + + std::visit(*this, *_if.condition) + toBytes(Opcode::If) + toBytes(ValueType::Void); @@ -559,7 +559,7 @@ bytes BinaryTransform::visit(vector const& _expressions) { bytes result; for (auto const& expr: _expressions) - result += boost::apply_visitor(*this, expr); + result += std::visit(*this, expr); return result; } @@ -567,7 +567,7 @@ bytes BinaryTransform::visitReversed(vector const& _expressions) { bytes result; for (auto const& expr: _expressions | boost::adaptors::reversed) - result += boost::apply_visitor(*this, expr); + result += std::visit(*this, expr); return result; } diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h index 35c611b21..fb1057489 100644 --- a/libyul/backends/wasm/BinaryTransform.h +++ b/libyul/backends/wasm/BinaryTransform.h @@ -35,7 +35,7 @@ namespace wasm /** * Web assembly to binary transform. */ -class BinaryTransform: public boost::static_visitor +class BinaryTransform { public: static dev::bytes run(Module const& _module); diff --git a/libyul/backends/wasm/EVMToEWasmTranslator.cpp b/libyul/backends/wasm/EVMToEWasmTranslator.cpp index f4de4bc84..6dd6375de 100644 --- a/libyul/backends/wasm/EVMToEWasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEWasmTranslator.cpp @@ -694,7 +694,7 @@ Object EVMToEWasmTranslator::run(Object const& _object) if (!m_polyfill) parsePolyfill(); - Block ast = boost::get(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code)); + Block ast = std::get(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code)); set reservedIdentifiers; NameDispenser nameDispenser{m_dialect, ast, reservedIdentifiers}; OptimiserStepContext context{m_dialect, nameDispenser, reservedIdentifiers}; @@ -752,6 +752,6 @@ void EVMToEWasmTranslator::parsePolyfill() m_polyfillFunctions.clear(); for (auto const& statement: m_polyfill->statements) - m_polyfillFunctions.insert(boost::get(statement).name); + m_polyfillFunctions.insert(std::get(statement).name); } diff --git a/libyul/backends/wasm/EWasmAST.h b/libyul/backends/wasm/EWasmAST.h index 677668ade..8e5bc9ea4 100644 --- a/libyul/backends/wasm/EWasmAST.h +++ b/libyul/backends/wasm/EWasmAST.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include #include #include @@ -44,7 +44,7 @@ struct If; struct Loop; struct Break; struct BreakIf; -using Expression = boost::variant< +using Expression = std::variant< Literal, StringLiteral, LocalVariable, GlobalVariable, FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, Block, If, Loop, Break, BreakIf diff --git a/libyul/backends/wasm/EWasmCodeTransform.cpp b/libyul/backends/wasm/EWasmCodeTransform.cpp index 277df0df2..3503feef2 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.cpp +++ b/libyul/backends/wasm/EWasmCodeTransform.cpp @@ -45,11 +45,11 @@ wasm::Module EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& for (auto const& statement: _ast.statements) { yulAssert( - statement.type() == typeid(yul::FunctionDefinition), + holds_alternative(statement), "Expected only function definitions at the highest level." ); - if (statement.type() == typeid(yul::FunctionDefinition)) - module.functions.emplace_back(transform.translateFunction(boost::get(statement))); + if (holds_alternative(statement)) + module.functions.emplace_back(transform.translateFunction(std::get(statement))); } for (auto& imp: transform.m_functionsToImport) @@ -157,7 +157,7 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call) { vector literals; for (auto const& arg: _call.arguments) - literals.emplace_back(wasm::StringLiteral{boost::get(arg).value.str()}); + literals.emplace_back(wasm::StringLiteral{std::get(arg).value.str()}); return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)}; } else @@ -303,12 +303,12 @@ wasm::Expression EWasmCodeTransform::operator()(Block const& _block) unique_ptr EWasmCodeTransform::visit(yul::Expression const& _expression) { - return make_unique(boost::apply_visitor(*this, _expression)); + return make_unique(std::visit(*this, _expression)); } wasm::Expression EWasmCodeTransform::visitReturnByValue(yul::Expression const& _expression) { - return boost::apply_visitor(*this, _expression); + return std::visit(*this, _expression); } vector EWasmCodeTransform::visit(vector const& _expressions) @@ -321,7 +321,7 @@ vector EWasmCodeTransform::visit(vector const wasm::Expression EWasmCodeTransform::visit(yul::Statement const& _statement) { - return boost::apply_visitor(*this, _statement); + return std::visit(*this, _statement); } vector EWasmCodeTransform::visit(vector const& _statements) diff --git a/libyul/backends/wasm/EWasmCodeTransform.h b/libyul/backends/wasm/EWasmCodeTransform.h index cb9927d20..7b4a248c9 100644 --- a/libyul/backends/wasm/EWasmCodeTransform.h +++ b/libyul/backends/wasm/EWasmCodeTransform.h @@ -32,7 +32,7 @@ namespace yul { struct AsmAnalysisInfo; -class EWasmCodeTransform: public boost::static_visitor +class EWasmCodeTransform { public: static wasm::Module run(Dialect const& _dialect, yul::Block const& _ast); diff --git a/libyul/backends/wasm/EWasmToText.cpp b/libyul/backends/wasm/EWasmToText.cpp index 3971be1f6..cc8e4711d 100644 --- a/libyul/backends/wasm/EWasmToText.cpp +++ b/libyul/backends/wasm/EWasmToText.cpp @@ -171,7 +171,7 @@ string EWasmToText::transform(wasm::FunctionDefinition const& _function) string EWasmToText::visit(wasm::Expression const& _expression) { - return boost::apply_visitor(*this, _expression); + return std::visit(*this, _expression); } string EWasmToText::joinTransformed(vector const& _expressions, char _separator) diff --git a/libyul/backends/wasm/EWasmToText.h b/libyul/backends/wasm/EWasmToText.h index 6163fd80b..51b27c9c9 100644 --- a/libyul/backends/wasm/EWasmToText.h +++ b/libyul/backends/wasm/EWasmToText.h @@ -28,7 +28,7 @@ namespace yul { struct AsmAnalysisInfo; -class EWasmToText: public boost::static_visitor +class EWasmToText { public: std::string run(wasm::Module const& _module); diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 81c80da99..0394252b5 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -84,13 +85,13 @@ void WordSizeTransform::operator()(Block& _block) _block.statements, [&](Statement& _s) -> std::optional> { - if (_s.type() == typeid(VariableDeclaration)) + if (holds_alternative(_s)) { - VariableDeclaration& varDecl = boost::get(_s); + VariableDeclaration& varDecl = std::get(_s); // Special handling for datasize and dataoffset - they will only need one variable. - if (varDecl.value && varDecl.value->type() == typeid(FunctionCall)) - if (BuiltinFunction const* f = m_inputDialect.builtin(boost::get(*varDecl.value).functionName.name)) + if (varDecl.value && holds_alternative(*varDecl.value)) + if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*varDecl.value).functionName.name)) if (f->literalArguments) { yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, ""); @@ -113,8 +114,8 @@ void WordSizeTransform::operator()(Block& _block) if ( !varDecl.value || - varDecl.value->type() == typeid(FunctionalInstruction) || - varDecl.value->type() == typeid(FunctionCall) + holds_alternative(*varDecl.value) || + holds_alternative(*varDecl.value) ) { if (varDecl.value) visit(*varDecl.value); @@ -122,8 +123,8 @@ void WordSizeTransform::operator()(Block& _block) return std::nullopt; } else if ( - varDecl.value->type() == typeid(Identifier) || - varDecl.value->type() == typeid(Literal) + holds_alternative(*varDecl.value) || + holds_alternative(*varDecl.value) ) { yulAssert(varDecl.variables.size() == 1, ""); @@ -143,14 +144,14 @@ void WordSizeTransform::operator()(Block& _block) else yulAssert(false, ""); } - else if (_s.type() == typeid(Assignment)) + else if (holds_alternative(_s)) { - Assignment& assignment = boost::get(_s); + Assignment& assignment = std::get(_s); yulAssert(assignment.value, ""); // Special handling for datasize and dataoffset - they will only need one variable. - if (assignment.value->type() == typeid(FunctionCall)) - if (BuiltinFunction const* f = m_inputDialect.builtin(boost::get(*assignment.value).functionName.name)) + if (holds_alternative(*assignment.value)) + if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*assignment.value).functionName.name)) if (f->literalArguments) { yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, ""); @@ -172,8 +173,8 @@ void WordSizeTransform::operator()(Block& _block) } if ( - assignment.value->type() == typeid(FunctionalInstruction) || - assignment.value->type() == typeid(FunctionCall) + holds_alternative(*assignment.value) || + holds_alternative(*assignment.value) ) { if (assignment.value) visit(*assignment.value); @@ -181,8 +182,8 @@ void WordSizeTransform::operator()(Block& _block) return std::nullopt; } else if ( - assignment.value->type() == typeid(Identifier) || - assignment.value->type() == typeid(Literal) + holds_alternative(*assignment.value) || + holds_alternative(*assignment.value) ) { yulAssert(assignment.variableNames.size() == 1, ""); @@ -202,8 +203,8 @@ void WordSizeTransform::operator()(Block& _block) else yulAssert(false, ""); } - else if (_s.type() == typeid(Switch)) - return handleSwitch(boost::get(_s)); + else if (holds_alternative(_s)) + return handleSwitch(std::get(_s)); else visit(_s); return std::nullopt; @@ -342,7 +343,7 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) } vector splitExpressions; for (auto const& expr: expandValue(*_switch.expression)) - splitExpressions.emplace_back(boost::get(*expr).name); + splitExpressions.emplace_back(std::get(*expr).name); ret += handleSwitchInternal( _switch.location, @@ -372,15 +373,15 @@ array WordSizeTransform::generateU64IdentifierNames(YulString cons array, 4> WordSizeTransform::expandValue(Expression const& _e) { array, 4> ret; - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - Identifier const& id = boost::get(_e); + Identifier const& id = std::get(_e); for (int i = 0; i < 4; i++) ret[i] = make_unique(Identifier{id.location, m_variableMapping.at(id.name)[i]}); } - else if (_e.type() == typeid(Literal)) + else if (holds_alternative(_e)) { - Literal const& lit = boost::get(_e); + Literal const& lit = std::get(_e); u256 val = valueOfLiteral(lit); for (int i = 3; i >= 0; i--) { diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index efe3ff854..53c14ad67 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -155,12 +155,12 @@ Statement ASTCopier::operator ()(Block const& _block) Expression ASTCopier::translate(Expression const& _expression) { - return _expression.apply_visitor(static_cast(*this)); + return std::visit(static_cast(*this), _expression); } Statement ASTCopier::translate(Statement const& _statement) { - return _statement.apply_visitor(static_cast(*this)); + return std::visit(static_cast(*this), _statement); } Block ASTCopier::translate(Block const& _block) diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index dab3e296a..a2840b7ff 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -24,8 +24,6 @@ #include -#include - #include #include #include @@ -34,7 +32,7 @@ namespace yul { -class ExpressionCopier: public boost::static_visitor +class ExpressionCopier { public: virtual ~ExpressionCopier() = default; @@ -44,7 +42,7 @@ public: virtual Expression operator()(FunctionCall const&) = 0; }; -class StatementCopier: public boost::static_visitor +class StatementCopier { public: virtual ~StatementCopier() = default; diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index 523cc8a92..88d5a02b7 100644 --- a/libyul/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -94,12 +94,12 @@ void ASTWalker::operator()(Block const& _block) void ASTWalker::visit(Statement const& _st) { - boost::apply_visitor(*this, _st); + std::visit(*this, _st); } void ASTWalker::visit(Expression const& _e) { - boost::apply_visitor(*this, _e); + std::visit(*this, _e); } void ASTModifier::operator()(FunctionalInstruction& _instr) @@ -176,10 +176,10 @@ void ASTModifier::operator()(Block& _block) void ASTModifier::visit(Statement& _st) { - boost::apply_visitor(*this, _st); + std::visit(*this, _st); } void ASTModifier::visit(Expression& _e) { - boost::apply_visitor(*this, _e); + std::visit(*this, _e); } diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index 995f5fe21..47aab43f7 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -25,8 +25,6 @@ #include #include -#include - #include #include #include @@ -38,7 +36,7 @@ namespace yul /** * Generic AST walker. */ -class ASTWalker: public boost::static_visitor<> +class ASTWalker { public: virtual ~ASTWalker() = default; @@ -75,7 +73,7 @@ protected: /** * Generic AST modifier (i.e. non-const version of ASTWalker). */ -class ASTModifier: public boost::static_visitor<> +class ASTModifier { public: virtual ~ASTModifier() = default; diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp index 48816c695..f93d6e082 100644 --- a/libyul/optimiser/BlockFlattener.cpp +++ b/libyul/optimiser/BlockFlattener.cpp @@ -32,8 +32,8 @@ void BlockFlattener::operator()(Block& _block) _block.statements, [](Statement& _s) -> std::optional> { - if (_s.type() == typeid(Block)) - return std::move(boost::get(_s).statements); + if (holds_alternative(_s)) + return std::move(std::get(_s).statements); else return {}; } diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 65e0bae02..5baea5eb2 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -56,8 +56,8 @@ void CommonSubexpressionEliminator::visit(Expression& _e) bool descend = true; // If this is a function call to a function that requires literal arguments, // do not try to simplify there. - if (_e.type() == typeid(FunctionCall)) - if (BuiltinFunction const* builtin = m_dialect.builtin(boost::get(_e).functionName.name)) + if (holds_alternative(_e)) + if (BuiltinFunction const* builtin = m_dialect.builtin(std::get(_e).functionName.name)) if (builtin->literalArguments) // We should not modify function arguments that have to be literals // Note that replacing the function call entirely is fine, @@ -72,16 +72,16 @@ void CommonSubexpressionEliminator::visit(Expression& _e) if (descend) DataFlowAnalyzer::visit(_e); - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - Identifier& identifier = boost::get(_e); + Identifier& identifier = std::get(_e); YulString name = identifier.name; if (m_value.count(name)) { assertThrow(m_value.at(name), OptimizerException, ""); - if (m_value.at(name)->type() == typeid(Identifier)) + if (holds_alternative(*m_value.at(name))) { - YulString value = boost::get(*m_value.at(name)).name; + YulString value = std::get(*m_value.at(name)).name; assertThrow(inScope(value), OptimizerException, ""); _e = Identifier{locationOf(_e), value}; } diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp index d720ae88d..8d811233e 100644 --- a/libyul/optimiser/ConditionalSimplifier.cpp +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -29,12 +29,12 @@ using namespace yul; void ConditionalSimplifier::operator()(Switch& _switch) { visit(*_switch.expression); - if (_switch.expression->type() != typeid(Identifier)) + if (!holds_alternative(*_switch.expression)) { ASTModifier::operator()(_switch); return; } - YulString expr = boost::get(*_switch.expression).name; + YulString expr = std::get(*_switch.expression).name; for (auto& _case: _switch.cases) { if (_case.value) @@ -59,17 +59,17 @@ void ConditionalSimplifier::operator()(Block& _block) [&](Statement& _s) -> std::optional> { visit(_s); - if (_s.type() == typeid(If)) + if (holds_alternative(_s)) { - If& _if = boost::get(_s); + If& _if = std::get(_s); if ( - _if.condition->type() == typeid(Identifier) && + holds_alternative(*_if.condition) && !_if.body.statements.empty() && TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != TerminationFinder::ControlFlow::FlowOut ) { - YulString condition = boost::get(*_if.condition).name; + YulString condition = std::get(*_if.condition).name; langutil::SourceLocation location = _if.location; return make_vector( std::move(_s), diff --git a/libyul/optimiser/ConditionalUnsimplifier.cpp b/libyul/optimiser/ConditionalUnsimplifier.cpp index 224634017..a405205a3 100644 --- a/libyul/optimiser/ConditionalUnsimplifier.cpp +++ b/libyul/optimiser/ConditionalUnsimplifier.cpp @@ -29,12 +29,12 @@ using namespace yul; void ConditionalUnsimplifier::operator()(Switch& _switch) { visit(*_switch.expression); - if (_switch.expression->type() != typeid(Identifier)) + if (!holds_alternative(*_switch.expression)) { ASTModifier::operator()(_switch); return; } - YulString expr = boost::get(*_switch.expression).name; + YulString expr = std::get(*_switch.expression).name; for (auto& _case: _switch.cases) { if (_case.value) @@ -42,15 +42,15 @@ void ConditionalUnsimplifier::operator()(Switch& _switch) (*this)(*_case.value); if ( !_case.body.statements.empty() && - _case.body.statements.front().type() == typeid(Assignment) + holds_alternative(_case.body.statements.front()) ) { - Assignment const& assignment = boost::get(_case.body.statements.front()); + Assignment const& assignment = std::get(_case.body.statements.front()); if ( assignment.variableNames.size() == 1 && assignment.variableNames.front().name == expr && - assignment.value->type() == typeid(Literal) && - valueOfLiteral(boost::get(*assignment.value)) == valueOfLiteral(*_case.value) + holds_alternative(*assignment.value) && + valueOfLiteral(std::get(*assignment.value)) == valueOfLiteral(*_case.value) ) _case.body.statements.erase(_case.body.statements.begin()); } @@ -66,27 +66,27 @@ void ConditionalUnsimplifier::operator()(Block& _block) _block.statements, [&](Statement& _stmt1, Statement& _stmt2) -> std::optional> { - if (_stmt1.type() == typeid(If)) + if (holds_alternative(_stmt1)) { - If& _if = boost::get(_stmt1); + If& _if = std::get(_stmt1); if ( - _if.condition->type() == typeid(Identifier) && + holds_alternative(*_if.condition) && !_if.body.statements.empty() ) { - YulString condition = boost::get(*_if.condition).name; + YulString condition = std::get(*_if.condition).name; if ( - _stmt2.type() == typeid(Assignment) && + holds_alternative(_stmt2) && TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != TerminationFinder::ControlFlow::FlowOut ) { - Assignment const& assignment = boost::get(_stmt2); + Assignment const& assignment = std::get(_stmt2); if ( assignment.variableNames.size() == 1 && assignment.variableNames.front().name == condition && - assignment.value->type() == typeid(Literal) && - valueOfLiteral(boost::get(*assignment.value)) == 0 + holds_alternative(*assignment.value) && + valueOfLiteral(std::get(*assignment.value)) == 0 ) return {make_vector(std::move(_stmt1))}; } diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 99fa4b137..65da3e8fb 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -138,9 +138,9 @@ void ControlFlowSimplifier::operator()(Block& _block) void ControlFlowSimplifier::visit(Statement& _st) { - if (_st.type() == typeid(ForLoop)) + if (holds_alternative(_st)) { - ForLoop& forLoop = boost::get(_st); + ForLoop& forLoop = std::get(_st); yulAssert(forLoop.pre.statements.empty(), ""); size_t outerBreak = m_numBreakStatements; @@ -211,7 +211,7 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) _statements, [&](Statement& _stmt) -> OptionalStatements { - OptionalStatements result = boost::apply_visitor(visitor, _stmt); + OptionalStatements result = std::visit(visitor, _stmt); if (result) simplify(*result); else diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 6cf65ff25..9696e5e3f 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -32,6 +32,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -364,19 +365,19 @@ std::optional> DataFlowAnalyzer::isSimpleStore( _store == dev::eth::Instruction::SSTORE, "" ); - if (_statement.expression.type() == typeid(FunctionCall)) + if (holds_alternative(_statement.expression)) { - FunctionCall const& funCall = boost::get(_statement.expression); + FunctionCall const& funCall = std::get(_statement.expression); if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) if (auto const* builtin = dialect->builtin(funCall.functionName.name)) if (builtin->instruction == _store) if ( - funCall.arguments.at(0).type() == typeid(Identifier) && - funCall.arguments.at(1).type() == typeid(Identifier) + holds_alternative(funCall.arguments.at(0)) && + holds_alternative(funCall.arguments.at(1)) ) { - YulString key = boost::get(funCall.arguments.at(0)).name; - YulString value = boost::get(funCall.arguments.at(1)).name; + YulString key = std::get(funCall.arguments.at(0)).name; + YulString value = std::get(funCall.arguments.at(1)).name; return make_pair(key, value); } } diff --git a/libyul/optimiser/DeadCodeEliminator.cpp b/libyul/optimiser/DeadCodeEliminator.cpp index 95e1cf284..a2daf9452 100644 --- a/libyul/optimiser/DeadCodeEliminator.cpp +++ b/libyul/optimiser/DeadCodeEliminator.cpp @@ -56,7 +56,7 @@ void DeadCodeEliminator::operator()(Block& _block) remove_if( _block.statements.begin() + index + 1, _block.statements.end(), - [] (Statement const& _s) { return _s.type() != typeid(yul::FunctionDefinition); } + [] (Statement const& _s) { return !holds_alternative(_s); } ), _block.statements.end() ); diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index cd3ff49a2..14b7b7cc3 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -25,8 +25,6 @@ #include #include -#include - #include #include diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 5b1064cc8..0288b92f2 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -49,9 +49,9 @@ void ExpressionInliner::operator()(FunctionDefinition& _fun) void ExpressionInliner::visit(Expression& _expression) { ASTModifier::visit(_expression); - if (_expression.type() == typeid(FunctionCall)) + if (holds_alternative(_expression)) { - FunctionCall& funCall = boost::get(_expression); + FunctionCall& funCall = std::get(_expression); if (!m_inlinableFunctions.count(funCall.functionName.name)) return; FunctionDefinition const& fun = *m_inlinableFunctions.at(funCall.functionName.name); @@ -74,6 +74,6 @@ void ExpressionInliner::visit(Expression& _expression) substitutions[paraName] = &arg; } - _expression = Substitution(substitutions).translate(*boost::get(fun.body.statements.front()).value); + _expression = Substitution(substitutions).translate(*std::get(fun.body.statements.front()).value); } } diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index 9404f3e73..b08df86c0 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -22,9 +22,7 @@ #include #include -#include #include - #include namespace yul diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index 911d44de0..c991b5b4a 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -66,12 +66,12 @@ void ExpressionJoiner::operator()(Block& _block) void ExpressionJoiner::visit(Expression& _e) { - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - Identifier const& identifier = boost::get(_e); + Identifier const& identifier = std::get(_e); if (isLatestStatementVarDeclJoinable(identifier)) { - VariableDeclaration& varDecl = boost::get(*latestStatement()); + VariableDeclaration& varDecl = std::get(*latestStatement()); _e = std::move(*varDecl.value); // Delete the variable declaration (also get the moved-from structure back into a sane state) @@ -101,7 +101,7 @@ void ExpressionJoiner::handleArguments(vector& _arguments) for (Expression const& arg: _arguments | boost::adaptors::reversed) { --i; - if (arg.type() != typeid(Identifier) && arg.type() != typeid(Literal)) + if (!holds_alternative(arg) && !holds_alternative(arg)) break; } // i points to the last element that is neither an identifier nor a literal, @@ -138,9 +138,9 @@ Statement* ExpressionJoiner::latestStatement() bool ExpressionJoiner::isLatestStatementVarDeclJoinable(Identifier const& _identifier) { Statement const* statement = latestStatement(); - if (!statement || statement->type() != typeid(VariableDeclaration)) + if (!statement || !holds_alternative(*statement)) return false; - VariableDeclaration const& varDecl = boost::get(*statement); + VariableDeclaration const& varDecl = std::get(*statement); if (varDecl.variables.size() != 1 || !varDecl.value) return false; assertThrow(varDecl.variables.size() == 1, OptimizerException, ""); diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 86fb6206f..c0759126e 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -101,7 +101,7 @@ void ExpressionSplitter::operator()(Block& _block) void ExpressionSplitter::outlineExpression(Expression& _expr) { - if (_expr.type() == typeid(Identifier)) + if (holds_alternative(_expr)) return; visit(_expr); diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index a3c213a05..d232ecbe9 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -33,8 +33,8 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) { if ( m_dialect.booleanNegationFunction() && - _forLoop.condition->type() != typeid(Literal) && - _forLoop.condition->type() != typeid(Identifier) + !holds_alternative(*_forLoop.condition) && + !holds_alternative(*_forLoop.condition) ) { langutil::SourceLocation loc = locationOf(*_forLoop.condition); diff --git a/libyul/optimiser/ForLoopConditionOutOfBody.cpp b/libyul/optimiser/ForLoopConditionOutOfBody.cpp index e3d0f13ff..e7f93b8b6 100644 --- a/libyul/optimiser/ForLoopConditionOutOfBody.cpp +++ b/libyul/optimiser/ForLoopConditionOutOfBody.cpp @@ -36,17 +36,17 @@ void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) if ( !m_dialect.booleanNegationFunction() || - _forLoop.condition->type() != typeid(Literal) || - valueOfLiteral(boost::get(*_forLoop.condition)) == u256(0) || + !holds_alternative(*_forLoop.condition) || + valueOfLiteral(std::get(*_forLoop.condition)) == u256(0) || _forLoop.body.statements.empty() || - _forLoop.body.statements.front().type() != typeid(If) + !holds_alternative(_forLoop.body.statements.front()) ) return; - If& firstStatement = boost::get(_forLoop.body.statements.front()); + If& firstStatement = std::get(_forLoop.body.statements.front()); if ( firstStatement.body.statements.empty() || - firstStatement.body.statements.front().type() != typeid(Break) + !holds_alternative(firstStatement.body.statements.front()) ) return; if (!SideEffectsCollector(m_dialect, *firstStatement.condition).movable()) @@ -56,10 +56,10 @@ void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) langutil::SourceLocation location = locationOf(*firstStatement.condition); if ( - firstStatement.condition->type() == typeid(FunctionCall) && - boost::get(*firstStatement.condition).functionName.name == iszero + holds_alternative(*firstStatement.condition) && + std::get(*firstStatement.condition).functionName.name == iszero ) - _forLoop.condition = make_unique(std::move(boost::get(*firstStatement.condition).arguments.front())); + _forLoop.condition = make_unique(std::move(std::get(*firstStatement.condition).arguments.front())); else _forLoop.condition = make_unique(FunctionCall{ location, diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp index a11215e50..ff2786d6d 100644 --- a/libyul/optimiser/ForLoopInitRewriter.cpp +++ b/libyul/optimiser/ForLoopInitRewriter.cpp @@ -29,9 +29,9 @@ void ForLoopInitRewriter::operator()(Block& _block) _block.statements, [&](Statement& _stmt) -> std::optional> { - if (_stmt.type() == typeid(ForLoop)) + if (holds_alternative(_stmt)) { - auto& forLoop = boost::get(_stmt); + auto& forLoop = std::get(_stmt); (*this)(forLoop.pre); (*this)(forLoop.body); (*this)(forLoop.post); diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 8b0af2691..54c8555d2 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -50,7 +50,7 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): SSAValueTracker tracker; tracker(m_ast); for (auto const& ssaValue: tracker.values()) - if (ssaValue.second && ssaValue.second->type() == typeid(Literal)) + if (ssaValue.second && holds_alternative(*ssaValue.second)) m_constants.emplace(ssaValue.first); // Store size of global statements. @@ -58,9 +58,9 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): map references = ReferencesCounter::countReferences(m_ast); for (auto& statement: m_ast.statements) { - if (statement.type() != typeid(FunctionDefinition)) + if (!holds_alternative(statement)) continue; - FunctionDefinition& fun = boost::get(statement); + FunctionDefinition& fun = std::get(statement); m_functions[fun.name] = &fun; // Always inline functions that are only called once. if (references[fun.name] == 1) @@ -72,8 +72,8 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): void FullInliner::run() { for (auto& statement: m_ast.statements) - if (statement.type() == typeid(Block)) - handleBlock({}, boost::get(statement)); + if (holds_alternative(statement)) + handleBlock({}, std::get(statement)); // TODO it might be good to determine a visiting order: // first handle functions that are called from many places. @@ -112,9 +112,9 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) // Constant arguments might provide a means for further optimization, so they cause a bonus. bool constantArg = false; for (auto const& argument: _funCall.arguments) - if (argument.type() == typeid(Literal) || ( - argument.type() == typeid(Identifier) && - m_constants.count(boost::get(argument).name) + if (holds_alternative(argument) || ( + holds_alternative(argument) && + m_constants.count(std::get(argument).name) )) { constantArg = true; @@ -157,7 +157,7 @@ void InlineModifier::operator()(Block& _block) std::optional> InlineModifier::tryInlineStatement(Statement& _statement) { // Only inline for expression statements, assignments and variable declarations. - Expression* e = boost::apply_visitor(GenericFallbackReturnsVisitor( + Expression* e = std::visit(GenericFallbackReturnsVisitor( [](ExpressionStatement& _s) { return &_s.expression; }, [](Assignment& _s) { return _s.value.get(); }, [](VariableDeclaration& _s) { return _s.value.get(); } @@ -165,7 +165,7 @@ std::optional> InlineModifier::tryInlineStatement(Statement& _ if (e) { // Only inline direct function calls. - FunctionCall* funCall = boost::apply_visitor(GenericFallbackReturnsVisitor( + FunctionCall* funCall = std::visit(GenericFallbackReturnsVisitor( [](FunctionCall& _e) { return &_e; } ), *e); if (funCall && m_driver.shallInline(*funCall, m_currentFunction)) @@ -203,9 +203,9 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC newVariable(var, nullptr); Statement newBody = BodyCopier(m_nameDispenser, variableReplacements)(function->body); - newStatements += std::move(boost::get(newBody).statements); + newStatements += std::move(std::get(newBody).statements); - boost::apply_visitor(GenericFallbackVisitor{ + std::visit(GenericFallbackVisitor{ [&](Assignment& _assignment) { for (size_t i = 0; i < _assignment.variableNames.size(); ++i) diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 746cc46db..21fdb3552 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -29,8 +29,6 @@ #include -#include - #include #include diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index a8d7982b4..b6a79265a 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -40,10 +40,10 @@ void FunctionGrouper::operator()(Block& _block) for (auto&& statement: _block.statements) { - if (statement.type() == typeid(FunctionDefinition)) + if (holds_alternative(statement)) reordered.emplace_back(std::move(statement)); else - boost::get(reordered.front()).statements.emplace_back(std::move(statement)); + std::get(reordered.front()).statements.emplace_back(std::move(statement)); } _block.statements = std::move(reordered); } @@ -52,10 +52,10 @@ bool FunctionGrouper::alreadyGrouped(Block const& _block) { if (_block.statements.empty()) return false; - if (_block.statements.front().type() != typeid(Block)) + if (!holds_alternative(_block.statements.front())) return false; for (size_t i = 1; i < _block.statements.size(); ++i) - if (_block.statements.at(i).type() != typeid(FunctionDefinition)) + if (!holds_alternative(_block.statements.at(i))) return false; return true; } diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index 6ed930fcc..d2981691d 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -36,8 +36,8 @@ void FunctionHoister::operator()(Block& _block) m_isTopLevel = false; for (auto&& statement: _block.statements) { - boost::apply_visitor(*this, statement); - if (statement.type() == typeid(FunctionDefinition)) + std::visit(*this, statement); + if (holds_alternative(statement)) { m_functions.emplace_back(std::move(statement)); statement = Block{_block.location, {}}; diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index f72e5357c..33309009e 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -45,9 +45,9 @@ void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _fu { YulString retVariable = _function.returnVariables.front().name; Statement const& bodyStatement = _function.body.statements.front(); - if (bodyStatement.type() == typeid(Assignment)) + if (holds_alternative(bodyStatement)) { - Assignment const& assignment = boost::get(bodyStatement); + Assignment const& assignment = std::get(bodyStatement); if (assignment.variableNames.size() == 1 && assignment.variableNames.front().name == retVariable) { // TODO: use code size metric here @@ -57,7 +57,7 @@ void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _fu // function body. assertThrow(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, OptimizerException, ""); m_disallowedIdentifiers = set{retVariable, _function.name}; - boost::apply_visitor(*this, *assignment.value); + std::visit(*this, *assignment.value); if (!m_foundDisallowedIdentifier) m_inlinableFunctions[_function.name] = &_function; m_disallowedIdentifiers.clear(); diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 2c13ac05b..8d488688e 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -27,6 +27,9 @@ #include +#include + +using namespace std; using namespace yul; using namespace dev; @@ -37,12 +40,12 @@ bool KnowledgeBase::knownToBeDifferent(YulString _a, YulString _b) // If that fails, try `eq(_a, _b)`. Expression expr1 = simplify(FunctionCall{{}, {{}, "sub"_yulstring}, make_vector(Identifier{{}, _a}, Identifier{{}, _b})}); - if (expr1.type() == typeid(Literal)) - return valueOfLiteral(boost::get(expr1)) != 0; + if (holds_alternative(expr1)) + return valueOfLiteral(std::get(expr1)) != 0; Expression expr2 = simplify(FunctionCall{{}, {{}, "eq"_yulstring}, make_vector(Identifier{{}, _a}, Identifier{{}, _b})}); - if (expr2.type() == typeid(Literal)) - return valueOfLiteral(boost::get(expr2)) == 0; + if (holds_alternative(expr2)) + return valueOfLiteral(std::get(expr2)) == 0; return false; } @@ -53,9 +56,9 @@ bool KnowledgeBase::knownToBeDifferentByAtLeast32(YulString _a, YulString _b) // current values to turn `sub(_a, _b)` into a constant whose absolute value is at least 32. Expression expr1 = simplify(FunctionCall{{}, {{}, "sub"_yulstring}, make_vector(Identifier{{}, _a}, Identifier{{}, _b})}); - if (expr1.type() == typeid(Literal)) + if (holds_alternative(expr1)) { - u256 val = valueOfLiteral(boost::get(expr1)); + u256 val = valueOfLiteral(std::get(expr1)); return val >= 32 && val <= u256(0) - 32; } @@ -74,11 +77,11 @@ Expression KnowledgeBase::simplify(Expression _expression) else --m_recursionCounter; - if (_expression.type() == typeid(FunctionCall)) - for (Expression& arg: boost::get(_expression).arguments) + if (holds_alternative(_expression)) + for (Expression& arg: std::get(_expression).arguments) arg = simplify(arg); - else if (_expression.type() == typeid(FunctionalInstruction)) - for (Expression& arg: boost::get(_expression).arguments) + else if (holds_alternative(_expression)) + for (Expression& arg: std::get(_expression).arguments) arg = simplify(arg); if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues)) diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index 184214254..df625b513 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -48,16 +48,16 @@ void LoadResolver::visit(Expression& _e) if (!dynamic_cast(&m_dialect)) return; - if (_e.type() == typeid(FunctionCall)) + if (holds_alternative(_e)) { - FunctionCall const& funCall = boost::get(_e); + FunctionCall const& funCall = std::get(_e); if (auto const* builtin = dynamic_cast(m_dialect).builtin(funCall.functionName.name)) if (builtin->instruction) tryResolve(_e, *builtin->instruction, funCall.arguments); } - else if (_e.type() == typeid(FunctionalInstruction)) + else if (holds_alternative(_e)) { - FunctionalInstruction const& instruction = boost::get(_e); + FunctionalInstruction const& instruction = std::get(_e); tryResolve(_e, instruction.instruction, instruction.arguments); } } @@ -68,10 +68,10 @@ void LoadResolver::tryResolve( vector const& _arguments ) { - if (_arguments.empty() || _arguments.at(0).type() != typeid(Identifier)) + if (_arguments.empty() || !holds_alternative(_arguments.at(0))) return; - YulString key = boost::get(_arguments.at(0)).name; + YulString key = std::get(_arguments.at(0)).name; if ( _instruction == dev::eth::Instruction::SLOAD && m_storage.values.count(key) diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index 7ece2c330..770e59259 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -35,13 +35,13 @@ using namespace yul; void MainFunction::operator()(Block& _block) { assertThrow(_block.statements.size() >= 1, OptimizerException, ""); - assertThrow(_block.statements[0].type() == typeid(Block), OptimizerException, ""); + assertThrow(holds_alternative(_block.statements[0]), OptimizerException, ""); for (size_t i = 1; i < _block.statements.size(); ++i) - assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); + assertThrow(holds_alternative(_block.statements.at(i)), OptimizerException, ""); /// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function assertThrow(NameCollector(_block).names().count("main"_yulstring) == 0, OptimizerException, ""); - Block& block = boost::get(_block.statements[0]); + Block& block = std::get(_block.statements[0]); FunctionDefinition main{ block.location, "main"_yulstring, diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index dfc8d98b5..bbd578ab0 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -65,23 +65,23 @@ size_t CodeSize::codeSizeIncludingFunctions(Block const& _block) void CodeSize::visit(Statement const& _statement) { - if (_statement.type() == typeid(FunctionDefinition) && m_ignoreFunctions) + if (holds_alternative(_statement) && m_ignoreFunctions) return; else if ( - _statement.type() == typeid(If) || - _statement.type() == typeid(Break) || - _statement.type() == typeid(Continue) + holds_alternative(_statement) || + holds_alternative(_statement) || + holds_alternative(_statement) ) m_size += 2; - else if (_statement.type() == typeid(ForLoop)) + else if (holds_alternative(_statement)) m_size += 3; - else if (_statement.type() == typeid(Switch)) - m_size += 1 + 2 * boost::get(_statement).cases.size(); + else if (holds_alternative(_statement)) + m_size += 1 + 2 * std::get(_statement).cases.size(); else if (!( - _statement.type() == typeid(Block) || - _statement.type() == typeid(ExpressionStatement) || - _statement.type() == typeid(Assignment) || - _statement.type() == typeid(VariableDeclaration) + holds_alternative(_statement) || + holds_alternative(_statement) || + holds_alternative(_statement) || + holds_alternative(_statement) )) ++m_size; @@ -90,7 +90,7 @@ void CodeSize::visit(Statement const& _statement) void CodeSize::visit(Expression const& _expression) { - if (_expression.type() != typeid(Identifier)) + if (!holds_alternative(_expression)) ++m_size; ASTWalker::visit(_expression); } diff --git a/libyul/optimiser/NameDisplacer.cpp b/libyul/optimiser/NameDisplacer.cpp index cc6273fb2..2c8db2b12 100644 --- a/libyul/optimiser/NameDisplacer.cpp +++ b/libyul/optimiser/NameDisplacer.cpp @@ -64,8 +64,8 @@ void NameDisplacer::operator()(Block& _block) // First replace all the names of function definitions // because of scoping. for (auto& st: _block.statements) - if (st.type() == typeid(FunctionDefinition)) - checkAndReplaceNew(boost::get(st).name); + if (holds_alternative(st)) + checkAndReplaceNew(std::get(st).name); ASTModifier::operator()(_block); } diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index f9571a4c2..47c43a285 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -33,7 +33,7 @@ using namespace yul; void yul::removeEmptyBlocks(Block& _block) { auto isEmptyBlock = [](Statement const& _st) -> bool { - return _st.type() == typeid(Block) && boost::get(_st).statements.empty(); + return holds_alternative(_st) && std::get(_st).statements.empty(); }; boost::range::remove_erase_if(_block.statements, isEmptyBlock); } diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index 16383d307..477814e22 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -296,7 +296,7 @@ void RedundantAssignEliminator::finalize( void AssignmentRemover::operator()(Block& _block) { boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool { - return _statement.type() == typeid(Assignment) && m_toRemove.count(&boost::get(_statement)); + return holds_alternative(_statement) && m_toRemove.count(&std::get(_statement)); }); ASTModifier::operator()(_block); diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index c9088a431..1df07c52f 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -68,9 +68,9 @@ Rematerialiser::Rematerialiser( void Rematerialiser::visit(Expression& _e) { - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - Identifier& identifier = boost::get(_e); + Identifier& identifier = std::get(_e); YulString name = identifier.name; if (m_value.count(name)) { @@ -96,15 +96,15 @@ void Rematerialiser::visit(Expression& _e) void LiteralRematerialiser::visit(Expression& _e) { - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - Identifier& identifier = boost::get(_e); + Identifier& identifier = std::get(_e); YulString name = identifier.name; if (m_value.count(name)) { Expression const* value = m_value.at(name); assertThrow(value, OptimizerException, ""); - if (value->type() == typeid(Literal)) + if (holds_alternative(*value)) _e = *value; } } diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index 2a421f9d9..bca44097b 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -19,6 +19,8 @@ #include #include +#include + using namespace std; using namespace dev; using namespace yul; @@ -37,7 +39,7 @@ void SSAReverser::operator()(Block& _block) _block.statements, [&](Statement& _stmt1, Statement& _stmt2) -> std::optional> { - auto* varDecl = boost::get(&_stmt1); + auto* varDecl = std::get_if(&_stmt1); if (!varDecl || varDecl->variables.size() != 1 || !varDecl->value) return {}; @@ -48,9 +50,9 @@ void SSAReverser::operator()(Block& _block) // with // a := E // let a_1 := a - if (auto* assignment = boost::get(&_stmt2)) + if (auto* assignment = std::get_if(&_stmt2)) { - auto* identifier = boost::get(assignment->value.get()); + auto* identifier = std::get_if(assignment->value.get()); if ( assignment->variableNames.size() == 1 && identifier && @@ -81,9 +83,9 @@ void SSAReverser::operator()(Block& _block) // with // let a := E // let a_1 := a - else if (auto* varDecl2 = boost::get(&_stmt2)) + else if (auto* varDecl2 = std::get_if(&_stmt2)) { - auto* identifier = boost::get(varDecl2->value.get()); + auto* identifier = std::get_if(varDecl2->value.get()); if ( varDecl2->variables.size() == 1 && identifier && diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 1f5d47e20..0b464f043 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -60,9 +60,9 @@ void IntroduceSSA::operator()(Block& _block) _block.statements, [&](Statement& _s) -> std::optional> { - if (_s.type() == typeid(VariableDeclaration)) + if (holds_alternative(_s)) { - VariableDeclaration& varDecl = boost::get(_s); + VariableDeclaration& varDecl = std::get(_s); if (varDecl.value) visit(*varDecl.value); @@ -90,12 +90,12 @@ void IntroduceSSA::operator()(Block& _block) make_unique(Identifier{loc, newName}) }); } - boost::get(statements.front()).variables = std::move(newVariables); + std::get(statements.front()).variables = std::move(newVariables); return { std::move(statements) }; } - else if (_s.type() == typeid(Assignment)) + else if (holds_alternative(_s)) { - Assignment& assignment = boost::get(_s); + Assignment& assignment = std::get(_s); visit(*assignment.value); for (auto const& var: assignment.variableNames) assertThrow(m_variablesToReplace.count(var.name), OptimizerException, ""); @@ -117,7 +117,7 @@ void IntroduceSSA::operator()(Block& _block) make_unique(Identifier{loc, newName}) }); } - boost::get(statements.front()).variables = std::move(newVariables); + std::get(statements.front()).variables = std::move(newVariables); return { std::move(statements) }; } else @@ -228,9 +228,9 @@ void IntroduceControlFlowSSA::operator()(Block& _block) } m_variablesToReassign.clear(); - if (_s.type() == typeid(VariableDeclaration)) + if (holds_alternative(_s)) { - VariableDeclaration& varDecl = boost::get(_s); + VariableDeclaration& varDecl = std::get(_s); for (auto const& var: varDecl.variables) if (m_variablesToReplace.count(var.name)) { @@ -238,9 +238,9 @@ void IntroduceControlFlowSSA::operator()(Block& _block) m_variablesInScope.insert(var.name); } } - else if (_s.type() == typeid(Assignment)) + else if (holds_alternative(_s)) { - Assignment& assignment = boost::get(_s); + Assignment& assignment = std::get(_s); for (auto const& var: assignment.variableNames) if (m_variablesToReplace.count(var.name)) assignedVariables.insert(var.name); @@ -304,14 +304,14 @@ void PropagateValues::operator()(VariableDeclaration& _varDecl) if (m_variablesToReplace.count(variable)) { // `let a := a_1` - regular declaration of non-SSA variable - yulAssert(_varDecl.value->type() == typeid(Identifier), ""); - m_currentVariableValues[variable] = boost::get(*_varDecl.value).name; + yulAssert(holds_alternative(*_varDecl.value), ""); + m_currentVariableValues[variable] = std::get(*_varDecl.value).name; m_clearAtEndOfBlock.insert(variable); } - else if (_varDecl.value && _varDecl.value->type() == typeid(Identifier)) + else if (_varDecl.value && holds_alternative(*_varDecl.value)) { // `let a_1 := a` - assignment to SSA variable after a branch. - YulString value = boost::get(*_varDecl.value).name; + YulString value = std::get(*_varDecl.value).name; if (m_variablesToReplace.count(value)) { // This is safe because `a_1` is not a "variable to replace" and thus @@ -333,8 +333,8 @@ void PropagateValues::operator()(Assignment& _assignment) if (!m_variablesToReplace.count(name)) return; - yulAssert(_assignment.value && _assignment.value->type() == typeid(Identifier), ""); - m_currentVariableValues[name] = boost::get(*_assignment.value).name; + yulAssert(_assignment.value && holds_alternative(*_assignment.value), ""); + m_currentVariableValues[name] = std::get(*_assignment.value).name; m_clearAtEndOfBlock.insert(name); } diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 001193533..62aaa0e0f 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -165,13 +165,13 @@ pair TerminationFinder::firstUncondition TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement) { if ( - _statement.type() == typeid(ExpressionStatement) && - isTerminatingBuiltin(boost::get(_statement)) + holds_alternative(_statement) && + isTerminatingBuiltin(std::get(_statement)) ) return ControlFlow::Terminate; - else if (_statement.type() == typeid(Break)) + else if (holds_alternative(_statement)) return ControlFlow::Break; - else if (_statement.type() == typeid(Continue)) + else if (holds_alternative(_statement)) return ControlFlow::Continue; else return ControlFlow::FlowOut; @@ -179,13 +179,13 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt) { - if (_exprStmnt.expression.type() == typeid(FunctionalInstruction)) + if (holds_alternative(_exprStmnt.expression)) return eth::SemanticInformation::terminatesControlFlow( - boost::get(_exprStmnt.expression).instruction + std::get(_exprStmnt.expression).instruction ); - else if (_exprStmnt.expression.type() == typeid(FunctionCall)) + else if (holds_alternative(_exprStmnt.expression)) if (auto const* dialect = dynamic_cast(&m_dialect)) - if (auto const* builtin = dialect->builtin(boost::get(_exprStmnt.expression).functionName.name)) + if (auto const* builtin = dialect->builtin(std::get(_exprStmnt.expression).functionName.name)) if (builtin->instruction) return eth::SemanticInformation::terminatesControlFlow(*builtin->instruction); return false; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 0575ac578..683534d26 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -67,13 +67,13 @@ bool SimplificationRules::isInitialized() const std::optional const*>> SimplificationRules::instructionAndArguments(Dialect const& _dialect, Expression const& _expr) { - if (_expr.type() == typeid(FunctionalInstruction)) - return make_pair(boost::get(_expr).instruction, &boost::get(_expr).arguments); - else if (_expr.type() == typeid(FunctionCall)) + if (holds_alternative(_expr)) + return make_pair(std::get(_expr).instruction, &std::get(_expr).arguments); + else if (holds_alternative(_expr)) if (auto const* dialect = dynamic_cast(&_dialect)) - if (auto const* builtin = dialect->builtin(boost::get(_expr).functionName.name)) + if (auto const* builtin = dialect->builtin(std::get(_expr).functionName.name)) if (builtin->instruction) - return make_pair(*builtin->instruction, &boost::get(_expr).arguments); + return make_pair(*builtin->instruction, &std::get(_expr).arguments); return {}; } @@ -136,9 +136,9 @@ bool Pattern::matches( // Resolve the variable if possible. // Do not do it for "Any" because we can check identity better for variables. - if (m_kind != PatternKind::Any && _expr.type() == typeid(Identifier)) + if (m_kind != PatternKind::Any && holds_alternative(_expr)) { - YulString varName = boost::get(_expr).name; + YulString varName = std::get(_expr).name; if (_ssaValues.count(varName)) if (Expression const* new_expr = _ssaValues.at(varName)) expr = new_expr; @@ -147,9 +147,9 @@ bool Pattern::matches( if (m_kind == PatternKind::Constant) { - if (expr->type() != typeid(Literal)) + if (!holds_alternative(*expr)) return false; - Literal const& literal = boost::get(*expr); + Literal const& literal = std::get(*expr); if (literal.kind != LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value.str())) @@ -233,7 +233,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const u256 Pattern::d() const { - return valueOfNumberLiteral(boost::get(matchGroupValue())); + return valueOfNumberLiteral(std::get(matchGroupValue())); } Expression const& Pattern::matchGroupValue() const diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 72f63b76d..611883b2e 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -85,9 +85,9 @@ public: // get called on left-hand-sides of assignments. void visit(Expression& _e) override { - if (_e.type() == typeid(Identifier)) + if (holds_alternative(_e)) { - YulString name = boost::get(_e).name; + YulString name = std::get(_e).name; if (m_expressionCodeCost.count(name)) { if (!m_value.count(name)) @@ -162,7 +162,7 @@ bool StackCompressor::run( { yulAssert( _object.code && - _object.code->statements.size() > 0 && _object.code->statements.at(0).type() == typeid(Block), + _object.code->statements.size() > 0 && holds_alternative(_object.code->statements.at(0)), "Need to run the function grouper before the stack compressor." ); bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code); @@ -177,7 +177,7 @@ bool StackCompressor::run( yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value."); eliminateVariables( _dialect, - boost::get(_object.code->statements.at(0)), + std::get(_object.code->statements.at(0)), stackSurplus.at({}), allowMSizeOptimzation ); @@ -185,7 +185,7 @@ bool StackCompressor::run( for (size_t i = 1; i < _object.code->statements.size(); ++i) { - FunctionDefinition& fun = boost::get(_object.code->statements[i]); + FunctionDefinition& fun = std::get(_object.code->statements[i]); if (!stackSurplus.count(fun.name)) continue; diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 706176456..424caf575 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -95,7 +95,7 @@ void StructuralSimplifier::simplify(std::vector& _statements) _statements, [&](Statement& _stmt) -> OptionalStatements { - OptionalStatements result = boost::apply_visitor(visitor, _stmt); + OptionalStatements result = std::visit(visitor, _stmt); if (result) simplify(*result); else @@ -123,8 +123,8 @@ bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression) std::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const { - if (_expression.type() == typeid(Literal)) - return valueOfLiteral(boost::get(_expression)); + if (holds_alternative(_expression)) + return valueOfLiteral(std::get(_expression)); else return std::optional(); } diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index bc9efe962..98d71d410 100644 --- a/libyul/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -28,9 +28,9 @@ using namespace yul; Expression Substitution::translate(Expression const& _expression) { - if (_expression.type() == typeid(Identifier)) + if (holds_alternative(_expression)) { - YulString name = boost::get(_expression).name; + YulString name = std::get(_expression).name; if (m_substitutions.count(name)) // No recursive substitution return ASTCopier().translate(*m_substitutions.at(name)); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 50bfc6603..e35146e93 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -80,7 +80,7 @@ void OptimiserSuite::run( set reservedIdentifiers = _externallyUsedIdentifiers; reservedIdentifiers += _dialect.fixedFunctionNames(); - *_object.code = boost::get(Disambiguator( + *_object.code = std::get(Disambiguator( _dialect, *_object.analysisInfo, reservedIdentifiers @@ -291,7 +291,7 @@ void OptimiserSuite::run( { // If the first statement is an empty block, remove it. // We should only have function definitions after that. - if (ast.statements.size() > 1 && boost::get(ast.statements.front()).statements.empty()) + if (ast.statements.size() > 1 && std::get(ast.statements.front()).statements.empty()) ast.statements.erase(ast.statements.begin()); } suite.runSequence({ @@ -361,7 +361,7 @@ void OptimiserSuite::runSequence(std::vector const& _steps, Block& _ast) { unique_ptr copy; if (m_debug == Debug::PrintChanges) - copy = make_unique(boost::get(ASTCopier{}(_ast))); + copy = make_unique(std::get(ASTCopier{}(_ast))); for (string const& step: _steps) { if (m_debug == Debug::PrintStep) @@ -376,7 +376,7 @@ void OptimiserSuite::runSequence(std::vector const& _steps, Block& _ast) { cout << "== Running " << step << " changed the AST." << endl; cout << AsmPrinter{}(_ast) << endl; - copy = make_unique(boost::get(ASTCopier{}(_ast))); + copy = make_unique(std::get(ASTCopier{}(_ast))); } } } diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 9e425b17e..85a0dfb6d 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -32,7 +32,7 @@ using namespace yul; bool SyntacticallyEqual::operator()(Expression const& _lhs, Expression const& _rhs) { - return boost::apply_visitor([this](auto&& _lhsExpr, auto&& _rhsExpr) -> bool { + return std::visit([this](auto&& _lhsExpr, auto&& _rhsExpr) -> bool { // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. return this->expressionEqual(_lhsExpr, _rhsExpr); }, _lhs, _rhs); @@ -40,7 +40,7 @@ bool SyntacticallyEqual::operator()(Expression const& _lhs, Expression const& _r bool SyntacticallyEqual::operator()(Statement const& _lhs, Statement const& _rhs) { - return boost::apply_visitor([this](auto&& _lhsStmt, auto&& _rhsStmt) -> bool { + return std::visit([this](auto&& _lhsStmt, auto&& _rhsStmt) -> bool { // ``this->`` is redundant, but required to work around a bug present in gcc 6.x. return this->statementEqual(_lhsStmt, _rhsStmt); }, _lhs, _rhs); diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index f77c1f95c..e0d5a3dbe 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -68,18 +68,18 @@ UnusedPruner::UnusedPruner( void UnusedPruner::operator()(Block& _block) { for (auto&& statement: _block.statements) - if (statement.type() == typeid(FunctionDefinition)) + if (holds_alternative(statement)) { - FunctionDefinition& funDef = boost::get(statement); + FunctionDefinition& funDef = std::get(statement); if (!used(funDef.name)) { subtractReferences(ReferencesCounter::countReferences(funDef.body)); statement = Block{std::move(funDef.location), {}}; } } - else if (statement.type() == typeid(VariableDeclaration)) + else if (holds_alternative(statement)) { - VariableDeclaration& varDecl = boost::get(statement); + VariableDeclaration& varDecl = std::get(statement); // Multi-variable declarations are special. We can only remove it // if all variables are unused and the right-hand-side is either // movable or it returns a single value. In the latter case, we @@ -108,9 +108,9 @@ void UnusedPruner::operator()(Block& _block) }}; } } - else if (statement.type() == typeid(ExpressionStatement)) + else if (holds_alternative(statement)) { - ExpressionStatement& exprStmt = boost::get(statement); + ExpressionStatement& exprStmt = std::get(statement); if ( SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects). sideEffectFree(m_allowMSizeOptimization) diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index 45b53ee61..d6f07bc1e 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -51,5 +51,5 @@ void VarDeclInitializer::operator()(Block& _block) } } }; - iterateReplacing(_block.statements, boost::apply_visitor(visitor)); + iterateReplacing(_block.statements, [&](auto&& _statement) { return std::visit(visitor, _statement); }); } diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index 4c3946295..edb60a82b 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -40,8 +40,8 @@ VarNameCleaner::VarNameCleaner( m_translatedNames{} { for (auto const& statement: _ast.statements) - if (statement.type() == typeid(FunctionDefinition)) - m_blacklist.insert(boost::get(statement).name); + if (holds_alternative(statement)) + m_blacklist.insert(std::get(statement).name); m_usedNames = m_blacklist; } diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index c8948e6c3..aa3492cb0 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -37,6 +37,8 @@ #include +#include + using namespace std; using namespace langutil; using namespace yul; @@ -75,7 +77,7 @@ pair, shared_ptr> yul::test::parse(strin yul::Block yul::test::disambiguate(string const& _source, bool _yul) { auto result = parse(_source, _yul); - return boost::get(Disambiguator(defaultDialect(_yul), *result.second, {})(*result.first)); + return std::get(Disambiguator(defaultDialect(_yul), *result.second, {})(*result.first)); } string yul::test::format(string const& _source, bool _yul) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index fb120ea03..07dc8a5c3 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -74,6 +74,7 @@ #include #include +#include using namespace dev; using namespace langutil; @@ -415,7 +416,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c void YulOptimizerTest::disambiguate() { - *m_ast = boost::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); + *m_ast = std::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); updateContext(); } diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index 390c5bf05..d1b361c4e 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -35,6 +35,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -161,9 +162,9 @@ void Interpreter::operator()(Block const& _block) openScope(); // Register functions. for (auto const& statement: _block.statements) - if (statement.type() == typeid(FunctionDefinition)) + if (holds_alternative(statement)) { - FunctionDefinition const& funDef = boost::get(statement); + FunctionDefinition const& funDef = std::get(statement); solAssert(!m_scopes.back().count(funDef.name), ""); m_scopes.back().emplace(funDef.name, &funDef); } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index aaaeab511..ce3f70762 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -72,6 +72,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -131,7 +132,7 @@ public: set reservedIdentifiers; if (!disambiguated) { - *m_ast = boost::get(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast)); + *m_ast = std::get(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); m_nameDispenser = make_shared(m_dialect, *m_ast, reservedIdentifiers); disambiguated = true; From 2241c9181ef382327ffe411f1adb298f05668fdc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 17:42:26 +0100 Subject: [PATCH 031/130] Add README into the test/evmc directory --- test/evmc/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/evmc/README.md diff --git a/test/evmc/README.md b/test/evmc/README.md new file mode 100644 index 000000000..37417222c --- /dev/null +++ b/test/evmc/README.md @@ -0,0 +1,3 @@ +# EVMC + +This is an import of [EVMC](https://github.com/ethereum/evmc) version [7.0.0](https://github.com/ethereum/evmc/releases/tag/v7.0.0). From 672dfa422059f6a30a30ea1d62bc9b0ec88bf777 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 19 Nov 2019 19:55:05 +0100 Subject: [PATCH 032/130] Update Visitor.h documentation --- libdevcore/Visitor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libdevcore/Visitor.h b/libdevcore/Visitor.h index 4030c928e..c7f117d38 100644 --- a/libdevcore/Visitor.h +++ b/libdevcore/Visitor.h @@ -27,7 +27,7 @@ namespace dev { /// Generic visitor used as follows: -/// boost::apply_visitor(GenericVisitor( +/// std::visit(GenericVisitor( /// [](Class1& _c) { _c.f(); }, /// [](Class2& _c) { _c.g(); } /// ), variant); @@ -59,7 +59,7 @@ struct GenericVisitor<>: public boost::static_visitor<> { }; /// Generic visitor with fallback: -/// boost::apply_visitor(GenericFallbackVisitor( +/// std::visit(GenericFallbackVisitor( /// [](Class1& _c) { _c.f(); }, /// [](Class2& _c) { _c.g(); } /// ), variant); @@ -92,7 +92,7 @@ struct GenericFallbackVisitor<>: public boost::static_visitor<> { }; /// Generic visitor with fallback that can return a value: -/// boost::apply_visitor(GenericFallbackReturnsVisitor( +/// std::visit(GenericFallbackReturnsVisitor( /// [](Class1& _c) { return _c.f(); }, /// [](Class2& _c) { return _c.g(); } /// ), variant); From d358af6515acf70b6d4065d1b00c3235f09fe823 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 20 Nov 2019 00:00:34 +0100 Subject: [PATCH 033/130] Fix Wasm transform. --- libyul/backends/wasm/EVMToEWasmTranslator.cpp | 2 ++ libyul/backends/wasm/WordSizeTransform.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libyul/backends/wasm/EVMToEWasmTranslator.cpp b/libyul/backends/wasm/EVMToEWasmTranslator.cpp index 6dd6375de..6d4688c9a 100644 --- a/libyul/backends/wasm/EVMToEWasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEWasmTranslator.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -702,6 +703,7 @@ Object EVMToEWasmTranslator::run(Object const& _object) FunctionHoister::run(context, ast); FunctionGrouper::run(context, ast); MainFunction{}(ast); + ForLoopConditionIntoBody::run(context, ast); ExpressionSplitter::run(context, ast); WordSizeTransform::run(m_dialect, ast, nameDispenser); diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 21d586439..3bdfa631d 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -55,7 +55,7 @@ namespace yul * takes four u64 parameters and is supposed to return the logical disjunction * of them as a u64 value. If this name is already used somewhere, it is renamed. * - * Prerequisite: Disambiguator, ExpressionSplitter + * Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter */ class WordSizeTransform: public ASTModifier { From 978fccd13388eaf11163b73efb8ef7cc517bbdd7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 19 Nov 2019 18:58:56 +0100 Subject: [PATCH 034/130] Remove obsolete file from test/evmc --- test/evmc/CMakeLists.txt | 1 - test/evmc/helpers.hpp | 116 --------------------------------------- 2 files changed, 117 deletions(-) delete mode 100644 test/evmc/helpers.hpp diff --git a/test/evmc/CMakeLists.txt b/test/evmc/CMakeLists.txt index c08306e18..bd64e8625 100644 --- a/test/evmc/CMakeLists.txt +++ b/test/evmc/CMakeLists.txt @@ -16,7 +16,6 @@ target_sources(evmc INTERFACE ${PROJECT_SOURCE_DIR}/test/evmc/evmc.h ${PROJECT_SOURCE_DIR}/test/evmc/evmc.hpp ${PROJECT_SOURCE_DIR}/test/evmc/helpers.h - ${PROJECT_SOURCE_DIR}/test/evmc/helpers.hpp ${PROJECT_SOURCE_DIR}/test/evmc/utils.h ) target_include_directories(evmc INTERFACE ${PROJECT_SOURCE_DIR}/test/) diff --git a/test/evmc/helpers.hpp b/test/evmc/helpers.hpp deleted file mode 100644 index f30eec87a..000000000 --- a/test/evmc/helpers.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* EVMC: Ethereum Client-VM Connector API. - * Copyright 2018-2019 The EVMC Authors. - * Licensed under the Apache License, Version 2.0. - */ - -/** - * @file - * A collection of helpers (overloaded operators) for using EVMC types effectively in C++. - * - * @addtogroup helpers - * @{ - */ -#pragma once - -#include - -#include -#include - -using evmc::is_zero; - -/// The comparator for std::map. -EVMC_DEPRECATED -inline bool operator<(const evmc_address& a, const evmc_address& b) -{ - return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; -} - -/// The comparator for std::map. -EVMC_DEPRECATED -inline bool operator<(const evmc_bytes32& a, const evmc_bytes32& b) -{ - return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0; -} - -/// The comparator for equality. -EVMC_DEPRECATED -inline bool operator==(const evmc_address& a, const evmc_address& b) -{ - return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; -} - -/// The comparator for equality. -EVMC_DEPRECATED -inline bool operator==(const evmc_bytes32& a, const evmc_bytes32& b) -{ - return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; -} - -/// Parameters for the fnv1a hash function, specialized by the hash result size (size_t). -/// -/// The values for the matching size are taken from -/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters. -/// -/// @tparam size The size of the hash result (size_t). -template -struct fnv1_params -{ -}; - -/// Parameters for the fnv1a hash function, specialized for the hash result of 4 bytes. -template <> -struct fnv1_params<4> -{ - static constexpr auto prime = 0x1000193; ///< The FNV prime. - static constexpr auto offset_basis = 0x811c9dc5; ///< The FNV offset basis. -}; - -/// Parameters for the fnv1a hash function, specialized for the hash result of 8 bytes. -template <> -struct fnv1_params<8> -{ - static constexpr auto prime = 0x100000001b3; ///< The FNV prime. - static constexpr auto offset_basis = 0xcbf29ce484222325; ///< The FNV offset basis. -}; - -/// FNV1a hash function. -inline size_t fnv1a(const uint8_t* ptr, size_t len) noexcept -{ - using params = fnv1_params; - - auto ret = size_t{params::offset_basis}; - for (size_t i = 0; i < len; i++) - { - ret ^= ptr[i]; - ret *= params::prime; - } - return ret; -} - -namespace std -{ -/// Hash operator template specialization for evmc_address needed for unordered containers. -template <> -struct EVMC_DEPRECATED hash -{ - /// Hash operator using FNV1a. - size_t operator()(const evmc_address& s) const noexcept - { - return fnv1a(s.bytes, sizeof(s.bytes)); - } -}; - -/// Hash operator template needed for std::unordered_set and others using hashes. -template <> -struct EVMC_DEPRECATED hash -{ - /// Hash operator using FNV1a. - size_t operator()(const evmc_bytes32& s) const noexcept - { - return fnv1a(s.bytes, sizeof(s.bytes)); - } -}; -} // namespace std - -/** @} */ From 4d3aa6e99f9c6daeb4337ef35ad3246c8ba3626c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Nov 2019 00:14:32 +0100 Subject: [PATCH 035/130] Update to EVMC7 This file was missed in 92745c7bfc --- test/evmc/evmc.hpp | 236 ++++++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 120 deletions(-) diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index 1b52ae8d0..90321429d 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -334,118 +334,6 @@ public: } }; -class Host; - -/// @copybrief evmc_vm -/// -/// This is a RAII wrapper for evmc_vm and objects of this type -/// automatically destroys the VM instance. -class VM -{ -public: - VM() noexcept = default; - - /// Converting constructor from evmc_vm. - explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} - - /// Destructor responsible for automatically destroying the VM instance. - ~VM() noexcept - { - if (m_instance) - m_instance->destroy(m_instance); - } - - VM(const VM&) = delete; - VM& operator=(const VM&) = delete; - - /// Move constructor. - VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } - - /// Move assignment operator. - VM& operator=(VM&& other) noexcept - { - this->~VM(); - m_instance = other.m_instance; - other.m_instance = nullptr; - return *this; - } - - /// The constructor that captures a VM instance and configures the instance - /// with the provided list of options. - inline VM(evmc_vm* vm, - std::initializer_list> options) noexcept; - - /// Checks if contains a valid pointer to the VM instance. - explicit operator bool() const noexcept { return m_instance != nullptr; } - - /// Checks whenever the VM instance is ABI compatible with the current EVMC API. - bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } - - /// @copydoc evmc_vm::name - char const* name() const noexcept { return m_instance->name; } - - /// @copydoc evmc_vm::version - char const* version() const noexcept { return m_instance->version; } - - /// @copydoc evmc::vm::get_capabilities - evmc_capabilities_flagset get_capabilities() const noexcept - { - return m_instance->get_capabilities(m_instance); - } - - /// @copydoc evmc_set_option() - evmc_set_option_result set_option(const char name[], const char value[]) noexcept - { - return evmc_set_option(m_instance, name, value); - } - - /// @copydoc evmc_execute() - result execute(const evmc_host_interface& host, - evmc_host_context* ctx, - evmc_revision rev, - const evmc_message& msg, - const uint8_t* code, - size_t code_size) noexcept - { - return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)}; - } - - /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. - inline result execute(Host& host, - evmc_revision rev, - const evmc_message& msg, - const uint8_t* code, - size_t code_size) noexcept; - - /// Executes code without the Host context. - /// - /// The same as - /// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision, - /// const evmc_message&, const uint8_t*, size_t), - /// but without providing the Host context and interface. - /// This method is for experimental precompiles support where execution is - /// guaranteed not to require any Host access. - result execute(evmc_revision rev, - const evmc_message& msg, - const uint8_t* code, - size_t code_size) noexcept - { - return result{ - m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)}; - } - -private: - evmc_vm* m_instance = nullptr; -}; - -inline VM::VM(evmc_vm* vm, - std::initializer_list> options) noexcept - : m_instance{vm} -{ - for (const auto& option : options) - set_option(option.first, option.second); -} - /// The EVMC Host interface class HostInterface @@ -514,8 +402,10 @@ public: HostContext() = default; /// Constructor from the EVMC Host primitives. - HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept - : host{interface}, context{ctx} + /// @param interface The reference to the Host interface. + /// @param ctx The pointer to the Host context object. This parameter MAY be null. + HostContext(const evmc_host_interface& interface, evmc_host_context* ctx) noexcept + : host{&interface}, context{ctx} {} bool account_exists(const address& address) noexcept final @@ -596,6 +486,7 @@ public: } }; + /// Abstract class to be used by Host implementations. /// /// When implementing EVMC Host, you can directly inherit from the evmc::Host class. @@ -628,13 +519,118 @@ public: }; -inline result VM::execute(Host& host, - evmc_revision rev, - const evmc_message& msg, - const uint8_t* code, - size_t code_size) noexcept +/// @copybrief evmc_vm +/// +/// This is a RAII wrapper for evmc_vm, and object of this type +/// automatically destroys the VM instance. +class VM { - return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size); +public: + VM() noexcept = default; + + /// Converting constructor from evmc_vm. + explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} + + /// Destructor responsible for automatically destroying the VM instance. + ~VM() noexcept + { + if (m_instance) + m_instance->destroy(m_instance); + } + + VM(const VM&) = delete; + VM& operator=(const VM&) = delete; + + /// Move constructor. + VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } + + /// Move assignment operator. + VM& operator=(VM&& other) noexcept + { + this->~VM(); + m_instance = other.m_instance; + other.m_instance = nullptr; + return *this; + } + + /// The constructor that captures a VM instance and configures the instance + /// with the provided list of options. + inline VM(evmc_vm* vm, + std::initializer_list> options) noexcept; + + /// Checks if contains a valid pointer to the VM instance. + explicit operator bool() const noexcept { return m_instance != nullptr; } + + /// Checks whenever the VM instance is ABI compatible with the current EVMC API. + bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } + + /// @copydoc evmc_vm::name + char const* name() const noexcept { return m_instance->name; } + + /// @copydoc evmc_vm::version + char const* version() const noexcept { return m_instance->version; } + + /// @copydoc evmc::vm::get_capabilities + evmc_capabilities_flagset get_capabilities() const noexcept + { + return m_instance->get_capabilities(m_instance); + } + + /// @copydoc evmc_set_option() + evmc_set_option_result set_option(const char name[], const char value[]) noexcept + { + return evmc_set_option(m_instance, name, value); + } + + /// @copydoc evmc_execute() + result execute(const evmc_host_interface& host, + evmc_host_context* ctx, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)}; + } + + /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. + result execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size); + } + + /// Executes code without the Host context. + /// + /// The same as + /// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision, + /// const evmc_message&, const uint8_t*, size_t), + /// but without providing the Host context and interface. + /// This method is for experimental precompiles support where execution is + /// guaranteed not to require any Host access. + result execute(evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{ + m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)}; + } + +private: + evmc_vm* m_instance = nullptr; +}; + +inline VM::VM(evmc_vm* vm, + std::initializer_list> options) noexcept + : m_instance{vm} +{ + // This constructor is implemented outside of the class definition to workaround a doxygen bug. + for (const auto& option : options) + set_option(option.first, option.second); } From 6b5ce6dcd9a9052d3cf2d5f7531a1ed993f45731 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 20 Nov 2019 10:57:21 +0100 Subject: [PATCH 036/130] Disable actions again. --- .github/workflows/stale.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 048e2dcb4..000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Mark stale pull requests - -on: - schedule: - - cron: "0 0 * * *" - -jobs: - stale: - runs-on: ubuntu-latest - - steps: - - uses: actions/stale@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: "A reminder that this pull request has had no activity for 14 days" - stale-pr-label: "no-pr-activity" - days-before-stale: 14 From a89965f50092b5911569ac6d9ceeeb8c9e4c1b79 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 20 Nov 2019 16:47:38 +0100 Subject: [PATCH 037/130] Documentation: yul function definition clarification --- docs/yul.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yul.rst b/docs/yul.rst index d0a09d3a5..304a4b7e5 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -168,7 +168,7 @@ The ``continue`` and ``break`` statements can only be used inside loop bodies and have to be in the same function as the loop (or both have to be at the top level). The condition part of the for-loop has to evaluate to exactly one value. -Functions cannot be defined inside for loop init blocks. +Functions cannot be defined anywhere inside for loop init blocks. Literals cannot be larger than the their type. The largest type defined is 256-bit wide. From 26ab2d4c0640794366679b20d743c8ae76318720 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Thu, 21 Nov 2019 00:00:27 +0100 Subject: [PATCH 038/130] Splits out compilation only phase for external tests. --- .circleci/config.yml | 64 ++++++++++++++++++++++++++++++++---- test/externalTests/common.sh | 9 +++-- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cdd6edeab..9904ec70a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -597,7 +597,22 @@ jobs: npm --version test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) - t_ems_external_gnosis: + t_ems_compile_ext_gnosis: + docker: + - image: circleci/node:10 + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: External GnosisSafe compilation + command: | + export COMPILE_ONLY=1 + test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js + + t_ems_test_ext_gnosis: docker: - image: circleci/node:10 environment: @@ -613,7 +628,22 @@ jobs: - run: *gitter_notify_failure - run: *gitter_notify_success - t_ems_external_zeppelin: + t_ems_compile_ext_zeppelin: + docker: + - image: circleci/node:10 + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: External Zeppelin compilation + command: | + export COMPILE_ONLY=1 + test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js + + t_ems_test_ext_zeppelin: docker: - image: circleci/node:10 environment: @@ -629,7 +659,26 @@ jobs: - run: *gitter_notify_failure - run: *gitter_notify_success - t_ems_external_colony: + t_ems_compile_ext_colony: + docker: + - image: circleci/node:10 + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Install test dependencies + command: | + sudo apt-get -qy install lsof + - run: + name: External ColonyNetworks compilation + command: | + export COMPILE_ONLY=1 + test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js + + t_ems_test_ext_colony: docker: - image: circleci/node:10 environment: @@ -693,6 +742,9 @@ workflows: # Emscripten build and selected tests - b_ems: *workflow_trigger_on_tags - t_ems_solcjs: *workflow_emscripten + - t_ems_compile_ext_colony: *workflow_emscripten + - t_ems_compile_ext_gnosis: *workflow_emscripten + - t_ems_compile_ext_zeppelin: *workflow_emscripten nightly: @@ -708,9 +760,9 @@ workflows: jobs: # Emscripten builds and external tests - b_ems: *workflow_trigger_on_tags - - t_ems_external_zeppelin: *workflow_emscripten - - t_ems_external_gnosis: *workflow_emscripten - - t_ems_external_colony: *workflow_emscripten + - t_ems_test_ext_zeppelin: *workflow_emscripten + - t_ems_test_ext_gnosis: *workflow_emscripten + - t_ems_test_ext_colony: *workflow_emscripten # OSSFUZZ builds and (regression) tests - b_ubu_ossfuzz: *workflow_trigger_on_tags diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index a94b62dc4..dd9aa77aa 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -216,8 +216,13 @@ function run_test printLog "Running compile function..." $compile_fn verify_compiler_version "$SOLCVERSION" - printLog "Running test function..." - $test_fn + + if [[ "$COMPILE_ONLY" == 1 ]]; then + printLog "Skipping test function..." + else + printLog "Running test function..." + $test_fn + fi done } From 38fba0f557d63778c5f06c0db39df0cf86dbe708 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 21 Nov 2019 19:51:28 +0100 Subject: [PATCH 039/130] Clarify how errors are reported in standard-json mode. --- docs/using-the-compiler.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index db09b2a09..2a02322cf 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -57,7 +57,7 @@ Either add ``--libraries "file.sol:Math:0x12345678901234567890123456789012345678 If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. -If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. +If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output. .. note:: The library placeholder used to be the fully qualified name of the library itself @@ -140,6 +140,8 @@ The fields are generally subject to change, some are optional (as noted), but we try to only make backwards compatible changes. The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output. +The standard error output is not used and the process will always terminate in a "success" state, even +if there were errors. Errors are always reported as part of the JSON output. The following subsections describe the format through an example. Comments are of course not permitted and used here only for explanatory purposes. From a26dd5a4282266c549aaba9f8222fb70ee78d43a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 21 Nov 2019 20:08:50 +0100 Subject: [PATCH 040/130] Remove trailing spaces in output. --- libevmasm/KnownState.cpp | 8 ++++---- libsolidity/codegen/CompilerContext.cpp | 2 +- solc/CommandLineInterface.cpp | 18 +++++++++--------- test/tools/yulopti.cpp | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 73db60560..3d7c476fd 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -54,17 +54,17 @@ ostream& KnownState::stream(ostream& _out) const _out << "=== State ===" << endl; _out << "Stack height: " << dec << m_stackHeight << endl; - _out << "Equivalence classes: " << endl; + _out << "Equivalence classes:" << endl; for (Id eqClass = 0; eqClass < m_expressionClasses->size(); ++eqClass) streamExpressionClass(_out, eqClass); - _out << "Stack: " << endl; + _out << "Stack:" << endl; for (auto const& it: m_stackElements) { _out << " " << dec << it.first << ": "; streamExpressionClass(_out, it.second); } - _out << "Storage: " << endl; + _out << "Storage:" << endl; for (auto const& it: m_storageContent) { _out << " "; @@ -72,7 +72,7 @@ ostream& KnownState::stream(ostream& _out) const _out << ": "; streamExpressionClass(_out, it.second); } - _out << "Memory: " << endl; + _out << "Memory:" << endl; for (auto const& it: m_memoryContent) { _out << " "; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index d6045ee20..3ec0124e3 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -438,7 +438,7 @@ void CompilerContext::appendInlineAssembly( parserResult = std::move(obj.code); #ifdef SOL_OUTPUT_ASM - cout << "After optimizer: " << endl; + cout << "After optimizer:" << endl; cout << yul::AsmPrinter()(*parserResult) << endl; #endif } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 34b7c67c5..f85b8c3f9 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -271,7 +271,7 @@ void CommandLineInterface::handleBinary(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { - sout() << "Binary: " << endl; + sout() << "Binary:" << endl; sout() << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } @@ -281,7 +281,7 @@ void CommandLineInterface::handleBinary(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { - sout() << "Binary of the runtime part: " << endl; + sout() << "Binary of the runtime part:" << endl; sout() << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; } } @@ -293,7 +293,7 @@ void CommandLineInterface::handleOpcode(string const& _contract) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", dev::eth::disassemble(m_compiler->object(_contract).bytecode)); else { - sout() << "Opcodes: " << endl; + sout() << "Opcodes:" << endl; sout() << std::uppercase << dev::eth::disassemble(m_compiler->object(_contract).bytecode); sout() << endl; } @@ -307,7 +307,7 @@ void CommandLineInterface::handleIR(string const& _contractName) createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName)); else { - sout() << "IR: " << endl; + sout() << "IR:" << endl; sout() << m_compiler->yulIR(_contractName) << endl; } } @@ -327,7 +327,7 @@ void CommandLineInterface::handleEWasm(string const& _contractName) } else { - sout() << "EWasm text: " << endl; + sout() << "EWasm text:" << endl; sout() << m_compiler->eWasm(_contractName) << endl; sout() << "EWasm binary (hex): " << m_compiler->eWasmObject(_contractName).toHex() << endl; } @@ -355,7 +355,7 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + ".signatures", out); else - sout() << "Function signatures: " << endl << out; + sout() << "Function signatures:" << endl << out; } void CommandLineInterface::handleMetadata(string const& _contract) @@ -367,7 +367,7 @@ void CommandLineInterface::handleMetadata(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else - sout() << "Metadata: " << endl << data << endl; + sout() << "Metadata:" << endl << data << endl; } void CommandLineInterface::handleABI(string const& _contract) @@ -379,7 +379,7 @@ void CommandLineInterface::handleABI(string const& _contract) if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); else - sout() << "Contract JSON ABI " << endl << data << endl; + sout() << "Contract JSON ABI" << endl << data << endl; } void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) @@ -563,7 +563,7 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) addrString = addrString.substr(2); if (addrString.empty()) { - serr() << "Empty address provided for library \"" << libName << "\": " << endl; + serr() << "Empty address provided for library \"" << libName << "\":" << endl; serr() << "Note that there should not be any whitespace after the colon." << endl; return false; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index ce3f70762..e77cbf02b 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -141,7 +141,7 @@ public: cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl; cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl; - cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/ " << endl; + cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/" << endl; cout << " (C)onditional simplifier?" << endl; cout.flush(); int option = readStandardInputChar(); From 9ed706290b0fc9e043ee6debb2390146cbafbf4b Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 19 Nov 2019 17:09:44 +0100 Subject: [PATCH 041/130] Use pipeline parameters to store/reference current docker image revision --- .circleci/README.md | 2 +- .circleci/config.yml | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index c8bcd45f5..c754b5df0 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -11,7 +11,7 @@ docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Docker docker push ethereum/solidity-buildpack-deps:ubuntu1904- ``` -The current revision is `2`. +The current revision is stored in a [circle ci pipeline parameter](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `docker-image-rev`. Please update the value assigned to this parameter at the time of a docker image update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ. Once the docker image has been built and pushed to Dockerhub, you can find it at: diff --git a/.circleci/config.yml b/.circleci/config.yml index cdd6edeab..e61ec16fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,11 @@ # - t: test # - ubu: ubuntu # - ems: Emscripten -version: 2 +version: 2.1 +parameters: + docker-image-rev: + type: string + default: "2" defaults: @@ -106,7 +110,7 @@ defaults: - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >> steps: - checkout - attach_workspace: @@ -117,7 +121,7 @@ defaults: - test_ubuntu1904: &test_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> steps: - checkout - attach_workspace: @@ -287,7 +291,7 @@ jobs: b_ubu_clang: &build_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.docker-image-rev >> environment: CC: clang CXX: clang++ @@ -299,7 +303,7 @@ jobs: b_ubu: &build_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> steps: - checkout - run: *run_build @@ -313,7 +317,7 @@ jobs: b_ubu18: &build_ubuntu1804 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1804-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >> environment: CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 CMAKE_BUILD_TYPE: RelWithDebugInfo @@ -519,7 +523,7 @@ jobs: b_docs: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> steps: - checkout - run: *setup_prerelease_commit_hash @@ -544,7 +548,7 @@ jobs: t_ubu_cli: &t_ubu_cli docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> environment: TERM: xterm steps: From ae428dc2a03733d51332b1c785f8e9a1e14f4829 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Nov 2019 21:34:39 +0000 Subject: [PATCH 042/130] Clarify some notes in LinkerObject and assert that linkReferences are always correct --- libevmasm/Assembly.cpp | 3 +++ libevmasm/LinkerObject.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index ae8d001d7..88bddd7f7 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -519,8 +519,11 @@ map Assembly::optimiseInternal( LinkerObject const& Assembly::assemble() const { + // Return the already assembled object, if present. if (!m_assembledObject.bytecode.empty()) return m_assembledObject; + // Otherwise ensure the object is actually clear. + assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references."); size_t subTagSize = 1; for (auto const& sub: m_subs) diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index 928908032..3630365e0 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -35,7 +35,9 @@ namespace eth */ struct LinkerObject { + /// The bytecode. bytes bytecode; + /// Map from offsets in bytecode to library identifiers. The addresses starting at those offsets /// need to be replaced by the actual addresses by the linker. std::map linkReferences; @@ -47,7 +49,7 @@ struct LinkerObject void link(std::map const& _libraryAddresses); /// @returns a hex representation of the bytecode of the given object, replacing unlinked - /// addresses by placeholders. + /// addresses by placeholders. This output is lowercase. std::string toHex() const; /// @returns a 36 character string that is used as a placeholder for the library From bf1c07cef02fcbe43292c6e5187be27201fdb5b2 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 25 Nov 2019 16:24:39 +0100 Subject: [PATCH 043/130] Removes ext. tests from nightly CircleCI runs, --- .circleci/config.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9904ec70a..1c97b0e43 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -758,12 +758,6 @@ workflows: - develop_060 jobs: - # Emscripten builds and external tests - - b_ems: *workflow_trigger_on_tags - - t_ems_test_ext_zeppelin: *workflow_emscripten - - t_ems_test_ext_gnosis: *workflow_emscripten - - t_ems_test_ext_colony: *workflow_emscripten - # OSSFUZZ builds and (regression) tests - b_ubu_ossfuzz: *workflow_trigger_on_tags - t_ubu_ossfuzz: *workflow_ubuntu1904_ossfuzz From ae16ad88844b60dedc6c2601dc733e4f2336e6fd Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 25 Nov 2019 17:23:16 +0100 Subject: [PATCH 044/130] Link to latest examples. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a8c04da2a..0af8dd905 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,10 @@ contract HelloWorld { To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an browser-based IDE. Here are some example contracts: -1. [Voting](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting) -2. [Blind Auction](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#blind-auction) -3. [Safe remote purchase](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#safe-remote-purchase) -4. [Micropayment Channel](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#micropayment-channel) +1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting) +2. [Blind Auction](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#blind-auction) +3. [Safe remote purchase](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#safe-remote-purchase) +4. [Micropayment Channel](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#micropayment-channel) ## Documentation From d199db698e433220ec399d954cc3c008e9ed350f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 25 Nov 2019 16:16:21 +0100 Subject: [PATCH 045/130] [DOCS] Add more explanation and withdraw pattern. --- docs/examples/safe-remote.rst | 48 ++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index c4e684ce1..9ba3c4c85 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -4,6 +4,25 @@ Safe Remote Purchase ******************** +Purchasing goods remotely currently requires multiple parties that need to trust each other. +The simplest configuration involves a seller and a buyer. The buyer would like to receive +an item from the seller and the seller would like to get money (or an equivalent) +in return. The problematic part is the shipment here: There is no way to determine for +sure that the item arrived at the buyer. + +There are multiple ways to solve this problem, but all fall short in one or the other way. +In the following example, both parties have to put twice the value of the item into the +contract as escrow. As soon as this happened, the money will stay locked inside +the contract until the buyer confirms that they received the item. After that, +the buyer is returned the value (half of their deposit) and the seller gets three +times the value (their deposit plus the value). The idea behind +this is that both parties have an incentive to resolve the situation or otherwise +their money is locked forever. + +This contract of course does not solve the problem, but gives an overview of how +you can use state machine-like constructs inside a contract. + + :: pragma solidity >=0.4.22 <0.7.0; @@ -12,7 +31,7 @@ Safe Remote Purchase uint public value; address payable public seller; address payable public buyer; - enum State { Created, Locked, Inactive } + enum State { Created, Locked, Release, Inactive } // The state variable has a default value of the first member, `State.created` State public state; @@ -57,6 +76,7 @@ Safe Remote Purchase event Aborted(); event PurchaseConfirmed(); event ItemReceived(); + event SellerRefunded(); /// Abort the purchase and reclaim the ether. /// Can only be called by the seller before @@ -68,6 +88,10 @@ Safe Remote Purchase { emit Aborted(); state = State.Inactive; + // We use transfer here directly. It is + // reentrancy-safe, because it is the + // last call in this function and we + // already changed the state. seller.transfer(address(this).balance); } @@ -97,12 +121,24 @@ Safe Remote Purchase // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. - state = State.Inactive; - - // NOTE: This actually allows both the buyer and the seller to - // block the refund - the withdraw pattern should be used. + state = State.Release; buyer.transfer(value); - seller.transfer(address(this).balance); + } + + /// This function refunds the seller, i.e. + /// pays back the locked funds of the seller. + function refundSeller() + public + onlySeller + inState(State.Release) + { + emit SellerRefunded(); + // It is important to change the state first because + // otherwise, the contracts called using `send` below + // can call in again here. + state = State.Inactive; + + seller.transfer(3 * value); } } \ No newline at end of file From a214dbb44bba5cb43d9ca714a69666c096f4953e Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 25 Nov 2019 18:16:35 +0100 Subject: [PATCH 046/130] Reimplement toHex manually. --- libdevcore/CommonData.cpp | 46 ++++++++++++++++++++++++++++++--------- libdevcore/CommonData.h | 28 +++++++++++------------- libdevcore/Exceptions.h | 1 + 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 1b0e97204..ebdc81a38 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -29,26 +29,52 @@ using namespace std; using namespace dev; +namespace +{ + +static char const* upperHexChars = "0123456789ABCDEF"; +static char const* lowerHexChars = "0123456789abcdef"; + +} + +string dev::toHex(uint8_t _data, HexCase _case) +{ + assertThrow(_case != HexCase::Mixed, BadHexCase, "Mixed case can only be used for byte arrays."); + + char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; + + return std::string{ + chars[(unsigned(_data) / 16) & 0xf], + chars[unsigned(_data) & 0xf] + }; +} + string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case) { - std::ostringstream ret; - if (_prefix == HexPrefix::Add) - ret << "0x"; + std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0); + size_t i = 0; + if (_prefix == HexPrefix::Add) + { + ret[i++] = '0'; + ret[i++] = 'x'; + } + + // Mixed case will be handled inside the loop. + char const* chars = _case == HexCase::Upper ? upperHexChars : lowerHexChars; int rix = _data.size() - 1; for (uint8_t c: _data) { // switch hex case every four hexchars - auto hexcase = std::nouppercase; - if (_case == HexCase::Upper) - hexcase = std::uppercase; - else if (_case == HexCase::Mixed) - hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase; + if (_case == HexCase::Mixed) + chars = (rix-- & 2) == 0 ? lowerHexChars : upperHexChars; - ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c); + ret[i++] = chars[(unsigned(c) / 16) & 0xf]; + ret[i++] = chars[unsigned(c) & 0xf]; } + assertThrow(i == ret.size(), Exception, ""); - return ret.str(); + return ret; } int dev::fromHex(char _i, WhenError _throw) diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 017a632dc..d6e8f349e 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -130,9 +130,12 @@ enum class HexCase Mixed = 2, }; -/// Convert a series of bytes to the corresponding string of hex duplets. -/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte. -/// @example toHex("A\x69") == "4169" +/// Convert a single byte to a string of hex characters (of length two), +/// optionally with uppercase hex letters. +std::string toHex(uint8_t _data, HexCase _case = HexCase::Lower); + +/// Convert a series of bytes to the corresponding string of hex duplets, +/// optionally with "0x" prefix and with uppercase hex letters. std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower); /// Converts a (printable) ASCII hex character into the corresponding integer value. @@ -207,10 +210,6 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0) toBigEndian(_val, ret); return ret; } -inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) -{ - return (_min || _val) ? bytes{ _val } : bytes{}; -} /// Convenience function for conversion of a u256 to hex inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) @@ -219,13 +218,18 @@ inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) return (prefix == HexPrefix::Add) ? "0x" + str : str; } +inline std::string toCompactHexWithPrefix(u256 const& _value) +{ + return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); +} + /// Returns decimal representation for small numbers and hex for large numbers. inline std::string formatNumber(bigint const& _value) { if (_value < 0) return "-" + formatNumber(-_value); if (_value > 0x1000000) - return toHex(toCompactBigEndian(_value), HexPrefix::Add); + return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); else return _value.str(); } @@ -233,17 +237,11 @@ inline std::string formatNumber(bigint const& _value) inline std::string formatNumber(u256 const& _value) { if (_value > 0x1000000) - return toHex(toCompactBigEndian(_value), HexPrefix::Add); + return toCompactHexWithPrefix(_value); else return _value.str(); } -inline std::string toCompactHexWithPrefix(u256 val) -{ - std::ostringstream ret; - ret << std::hex << val; - return "0x" + ret.str(); -} // Algorithms for string and string-like collections. diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index 943379d7a..a0ab7c6b5 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -46,6 +46,7 @@ private: DEV_SIMPLE_EXCEPTION(InvalidAddress); DEV_SIMPLE_EXCEPTION(BadHexCharacter); +DEV_SIMPLE_EXCEPTION(BadHexCase); DEV_SIMPLE_EXCEPTION(FileError); DEV_SIMPLE_EXCEPTION(DataTooLong); From fc10fc30730b94bbefb4fe9560ca335daa518562 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Nov 2019 19:32:52 +0000 Subject: [PATCH 047/130] Move InvalidDeposit/InvalidOpcode from Instructions.h to Exceptions.h --- libevmasm/Exceptions.h | 3 +++ libevmasm/Instruction.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmasm/Exceptions.h b/libevmasm/Exceptions.h index 06b0ac788..d6557d399 100644 --- a/libevmasm/Exceptions.h +++ b/libevmasm/Exceptions.h @@ -33,5 +33,8 @@ struct OptimizerException: virtual AssemblyException {}; struct StackTooDeepException: virtual OptimizerException {}; struct ItemNotAvailableException: virtual OptimizerException {}; +DEV_SIMPLE_EXCEPTION(InvalidDeposit); +DEV_SIMPLE_EXCEPTION(InvalidOpcode); + } } diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index f2f5879d9..c93fbf158 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -31,9 +31,6 @@ namespace dev namespace eth { -DEV_SIMPLE_EXCEPTION(InvalidDeposit); -DEV_SIMPLE_EXCEPTION(InvalidOpcode); - /// Virtual machine bytecode instruction. enum class Instruction: uint8_t { From 4a1e85436b0ee5ac2befa97f4babb285faa222c5 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sat, 5 Oct 2019 23:47:23 +0300 Subject: [PATCH 048/130] Allow splitting string literals into multiple parts --- Changelog.md | 1 + docs/types/value-types.rst | 4 ++-- liblangutil/Scanner.cpp | 2 +- liblangutil/Token.h | 1 + libsolidity/ast/ASTJsonConverter.cpp | 1 + libsolidity/ast/TypeProvider.cpp | 1 + libsolidity/parsing/Parser.cpp | 15 ++++++++++++++- libyul/ObjectParser.cpp | 5 ++++- test/libsolidity/SolidityScanner.cpp | 2 +- .../string/string_multipart_hex_valid_parts.sol | 8 ++++++++ .../string_multipart_newline_with_hex_prefix.sol | 9 +++++++++ ...tring_multipart_newline_without_hex_prefix.sol | 10 ++++++++++ .../string/string_multipart_only_hex.sol | 8 ++++++++ .../string/string_multipart_only_regular.sol | 8 ++++++++ .../string/string_multipart_single_line.sol | 7 +++++++ 15 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_hex_valid_parts.sol create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_newline_with_hex_prefix.sol create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_newline_without_hex_prefix.sol create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_only_hex.sol create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_only_regular.sol create mode 100644 test/libsolidity/syntaxTests/string/string_multipart_single_line.sol diff --git a/Changelog.md b/Changelog.md index 5a47d9814..e04029d94 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Allow to obtain the selector of public or external library functions via a member ``.selector``. + * Parser: Allow splitting string and hexadecimal string literals into multiple parts. Compiler Features: diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 053160701..426bdbe37 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -459,7 +459,7 @@ a non-rational number). String Literals and Types ------------------------- -String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. +String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``), and they can also be split into multiple consecutive parts (``"foo" "bar"`` is equivalent to ``"foobar"``) which can be helpful when dealing with long strings. They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. @@ -498,7 +498,7 @@ terminate the string literal. Newline only terminates the string literal if it i Hexadecimal Literals -------------------- -Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. +Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``), and they can also be split into multiple consecutive parts (``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. Hexadecimal literals behave like :ref:`string literals ` and have the same convertibility restrictions. diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 0d65334fb..3ddb23cb1 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -798,7 +798,7 @@ Token Scanner::scanHexString() literal.complete(); advance(); // consume quote - return Token::StringLiteral; + return Token::HexStringLiteral; } // Parse for regex [:digit:]+(_[:digit:]+)* diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 35d4f76f3..c5e829524 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -221,6 +221,7 @@ namespace langutil K(FalseLiteral, "false", 0) \ T(Number, nullptr, 0) \ T(StringLiteral, nullptr, 0) \ + T(HexStringLiteral, nullptr, 0) \ T(CommentLiteral, nullptr, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 0162fc1c1..4d686b6f1 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -801,6 +801,7 @@ string ASTJsonConverter::literalTokenKind(Token _token) case dev::solidity::Token::Number: return "number"; case dev::solidity::Token::StringLiteral: + case dev::solidity::Token::HexStringLiteral: return "string"; case dev::solidity::Token::TrueLiteral: case dev::solidity::Token::FalseLiteral: diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index 6e94c011b..1962df9d5 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -331,6 +331,7 @@ TypePointer TypeProvider::forLiteral(Literal const& _literal) case Token::Number: return rationalNumber(_literal); case Token::StringLiteral: + case Token::HexStringLiteral: return stringLiteral(_literal.value()); default: return nullptr; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index f1145ee83..ff00df90b 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1614,9 +1614,22 @@ ASTPointer Parser::parsePrimaryExpression() } break; case Token::StringLiteral: + case Token::HexStringLiteral: + { + string literal = m_scanner->currentLiteral(); + Token firstToken = m_scanner->currentToken(); + while (m_scanner->peekNextToken() == firstToken) + { + m_scanner->next(); + literal += m_scanner->currentLiteral(); + } nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(token, getLiteralAndAdvance()); + m_scanner->next(); + if (m_scanner->currentToken() == Token::Illegal) + fatalParserError(to_string(m_scanner->currentError())); + expression = nodeFactory.createNode(token, make_shared(literal)); break; + } case Token::Identifier: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(getLiteralAndAdvance()); diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 5f1eadef6..2e201046f 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -120,7 +120,10 @@ void ObjectParser::parseData(Object& _containingObject) YulString name = parseUniqueName(&_containingObject); - expectToken(Token::StringLiteral, false); + if (currentToken() == Token::HexStringLiteral) + expectToken(Token::HexStringLiteral, false); + else + expectToken(Token::StringLiteral, false); addNamedSubObject(_containingObject, name, make_shared(name, asBytes(currentLiteral()))); advance(); } diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 7e7b58469..5d97ee988 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -505,7 +505,7 @@ BOOST_AUTO_TEST_CASE(valid_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233FF\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::HexStringLiteral); BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); } diff --git a/test/libsolidity/syntaxTests/string/string_multipart_hex_valid_parts.sol b/test/libsolidity/syntaxTests/string/string_multipart_hex_valid_parts.sol new file mode 100644 index 000000000..684322143 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_hex_valid_parts.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = hex"aa" hex"b"; + return escapeCharacters; + } +} +// ---- +// ParserError: (108-112): Expected even number of hex-nibbles within double-quotes. diff --git a/test/libsolidity/syntaxTests/string/string_multipart_newline_with_hex_prefix.sol b/test/libsolidity/syntaxTests/string/string_multipart_newline_with_hex_prefix.sol new file mode 100644 index 000000000..1d4de1759 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_newline_with_hex_prefix.sol @@ -0,0 +1,9 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = hex"0000" + hex"deaf" + hex"feed"; + return escapeCharacters; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/string/string_multipart_newline_without_hex_prefix.sol b/test/libsolidity/syntaxTests/string/string_multipart_newline_without_hex_prefix.sol new file mode 100644 index 000000000..613b86da6 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_newline_without_hex_prefix.sol @@ -0,0 +1,10 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = hex"0000" + "deaf" + "feed"; + return escapeCharacters; + } +} +// ---- +// ParserError: (118-124): Expected ';' but got 'StringLiteral' \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/string/string_multipart_only_hex.sol b/test/libsolidity/syntaxTests/string/string_multipart_only_hex.sol new file mode 100644 index 000000000..d748104f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_only_hex.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = hex"aa" hex"bb" "cc"; + return escapeCharacters; + } +} +// ---- +// ParserError: (116-120): Expected ';' but got 'StringLiteral' diff --git a/test/libsolidity/syntaxTests/string/string_multipart_only_regular.sol b/test/libsolidity/syntaxTests/string/string_multipart_only_regular.sol new file mode 100644 index 000000000..b420601f5 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_only_regular.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "foo" "bar" hex"aa"; + return escapeCharacters; + } +} +// ---- +// ParserError: (112-119): Expected ';' but got 'HexStringLiteral' diff --git a/test/libsolidity/syntaxTests/string/string_multipart_single_line.sol b/test/libsolidity/syntaxTests/string/string_multipart_single_line.sol new file mode 100644 index 000000000..c07bde30c --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_multipart_single_line.sol @@ -0,0 +1,7 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "first" "second" "third"; + return escapeCharacters; + } +} +// ---- From dc351ae5fadbeea96aef0ca1e8ee0d572460e9d1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 20 Nov 2019 21:35:25 +0000 Subject: [PATCH 049/130] Simplify the BadHexChar exception Use comment and remove the invalidSymbol tag. Also use asserts. --- libdevcore/CommonData.cpp | 6 +++--- libdevcore/Exceptions.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index ebdc81a38..eef798fb0 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -86,7 +86,7 @@ int dev::fromHex(char _i, WhenError _throw) if (_i >= 'A' && _i <= 'F') return _i - 'A' + 10; if (_throw == WhenError::Throw) - BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); + assertThrow(false, BadHexCharacter, to_string(_i)); else return -1; } @@ -103,7 +103,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) if (h != -1) ret.push_back(h); else if (_throw == WhenError::Throw) - BOOST_THROW_EXCEPTION(BadHexCharacter()); + assertThrow(false, BadHexCharacter, ""); else return bytes(); } @@ -114,7 +114,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) if (h != -1 && l != -1) ret.push_back((uint8_t)(h * 16 + l)); else if (_throw == WhenError::Throw) - BOOST_THROW_EXCEPTION(BadHexCharacter()); + assertThrow(false, BadHexCharacter, ""); else return bytes(); } diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index a0ab7c6b5..df7a9ba01 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -51,7 +51,6 @@ DEV_SIMPLE_EXCEPTION(FileError); DEV_SIMPLE_EXCEPTION(DataTooLong); // error information to be added to exceptions -using errinfo_invalidSymbol = boost::error_info; using errinfo_comment = boost::error_info; } From e2627e2232023a1d3e5d39db90792abae196f4eb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 22 Nov 2019 16:59:06 +0000 Subject: [PATCH 050/130] Simplify fromHex by passing the throw flag around --- libdevcore/CommonData.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index eef798fb0..8e077261c 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -99,22 +99,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) if (_s.size() % 2) { - int h = fromHex(_s[s++], WhenError::DontThrow); + int h = fromHex(_s[s++], _throw); if (h != -1) ret.push_back(h); - else if (_throw == WhenError::Throw) - assertThrow(false, BadHexCharacter, ""); else return bytes(); } for (unsigned i = s; i < _s.size(); i += 2) { - int h = fromHex(_s[i], WhenError::DontThrow); - int l = fromHex(_s[i + 1], WhenError::DontThrow); + int h = fromHex(_s[i], _throw); + int l = fromHex(_s[i + 1], _throw); if (h != -1 && l != -1) ret.push_back((uint8_t)(h * 16 + l)); - else if (_throw == WhenError::Throw) - assertThrow(false, BadHexCharacter, ""); else return bytes(); } From 991fbd2956074b89ad2abdc093f31ca30991b893 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 22 Nov 2019 17:02:12 +0000 Subject: [PATCH 051/130] Replace uses of BOOST_THROW_EXCEPTION with assertThrow Where appropriate. --- libdevcore/IpfsHash.cpp | 7 ++----- libevmasm/Assembly.cpp | 4 ++-- libevmasm/AssemblyItem.cpp | 6 +++--- libevmasm/CommonSubexpressionEliminator.cpp | 18 +++++++----------- libyul/backends/evm/AsmCodeGen.cpp | 10 +++++----- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/libdevcore/IpfsHash.cpp b/libdevcore/IpfsHash.cpp index add459a2c..22ad7100c 100644 --- a/libdevcore/IpfsHash.cpp +++ b/libdevcore/IpfsHash.cpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -55,11 +56,7 @@ string base58Encode(bytes const& _data) bytes dev::ipfsHash(string _data) { - if (_data.length() >= 1024 * 256) - BOOST_THROW_EXCEPTION( - DataTooLong() << - errinfo_comment("Ipfs hash for large (chunked) files not yet implemented.") - ); + assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); bytes lengthAsVarint = varintEncoding(_data.size()); diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 88bddd7f7..079567193 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -323,7 +323,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data()))); break; default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); + assertThrow(false, InvalidOpcode, ""); } } @@ -641,7 +641,7 @@ LinkerObject const& Assembly::assemble() const ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); break; default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); + assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling."); } } diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 61355530e..53d4b79a0 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -81,7 +81,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const default: break; } - BOOST_THROW_EXCEPTION(InvalidOpcode()); + assertThrow(false, InvalidOpcode, ""); } int AssemblyItem::arguments() const @@ -212,7 +212,7 @@ string AssemblyItem::toAssemblyText() const assertThrow(false, AssemblyException, "Invalid assembly item."); break; default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); + assertThrow(false, InvalidOpcode, ""); } if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) { @@ -277,7 +277,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item) _out << " ???"; break; default: - BOOST_THROW_EXCEPTION(InvalidOpcode()); + assertThrow(false, InvalidOpcode, ""); } return _out; } diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 949d2c75a..c8e2f9210 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -158,11 +158,10 @@ AssemblyItems CSECodeGenerator::generateCode( for (auto id: {p.first, p.second}) if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) { - if (seqNr < _initialSequenceNumber) - // Invalid sequenced operation. - // @todo quick fix for now. Proper fix needs to choose representative with higher - // sequence number during dependency analysis. - BOOST_THROW_EXCEPTION(StackTooDeepException()); + // Invalid sequenced operation. + // @todo quick fix for now. Proper fix needs to choose representative with higher + // sequence number during dependency analysis. + assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, ""); sequencedExpressions.insert(make_pair(seqNr, id)); } @@ -222,12 +221,9 @@ void CSECodeGenerator::addDependencies(Id _c) return; // we already computed the dependencies for _c ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); assertThrow(expr.item, OptimizerException, ""); - if (expr.item->type() == UndefinedItem) - BOOST_THROW_EXCEPTION( - // If this exception happens, we need to find a different way to generate the - // compound expression. - ItemNotAvailableException() << errinfo_comment("Undefined item requested but not available.") - ); + // If this exception happens, we need to find a different way to generate the + // compound expression. + assertThrow(expr.item->type() != UndefinedItem, ItemNotAvailableException, "Undefined item requested but not available."); for (Id argument: expr.arguments) { addDependencies(argument); diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index 999d215a3..ca0aeb86a 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -207,11 +207,11 @@ void CodeGenerator::assemble( } catch (StackTooDeepError const& _e) { - BOOST_THROW_EXCEPTION( - InternalCompilerError() << errinfo_comment( - "Stack too deep when compiling inline assembly" + - (_e.comment() ? ": " + *_e.comment() : ".") - )); + solAssert( + false, + "Stack too deep when compiling inline assembly" + + (_e.comment() ? ": " + *_e.comment() : ".") + ); } solAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); } From ce83bfb0889ef9238849e8a06e8a49743ad17dd2 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 21 Nov 2019 18:56:25 +0100 Subject: [PATCH 052/130] [Yul] Format string literals back to quoted string literals --- Changelog.md | 1 + libdevcore/CommonData.cpp | 13 ++ libdevcore/CommonData.h | 5 +- libsolidity/codegen/ABIFunctions.cpp | 5 +- libsolidity/codegen/YulUtilFunctions.cpp | 4 +- .../yul_string_format_ascii/input.json | 17 ++ .../yul_string_format_ascii/output.json | 161 +++++++++++++++++ .../input.json | 17 ++ .../output.json | 114 ++++++++++++ .../input.json | 17 ++ .../output.json | 138 ++++++++++++++ .../yul_string_format_ascii_long/input.json | 17 ++ .../yul_string_format_ascii_long/output.json | 169 ++++++++++++++++++ .../yul_string_format_hex/input.json | 17 ++ .../yul_string_format_hex/output.json | 138 ++++++++++++++ .../semanticTests/viaYul/string_format.sol | 13 ++ 16 files changed, 842 insertions(+), 4 deletions(-) create mode 100644 test/cmdlineTests/yul_string_format_ascii/input.json create mode 100644 test/cmdlineTests/yul_string_format_ascii/output.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_bytes32/input.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_bytes32/output.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_long/input.json create mode 100644 test/cmdlineTests/yul_string_format_ascii_long/output.json create mode 100644 test/cmdlineTests/yul_string_format_hex/input.json create mode 100644 test/cmdlineTests/yul_string_format_hex/output.json create mode 100644 test/libsolidity/semanticTests/viaYul/string_format.sol diff --git a/Changelog.md b/Changelog.md index 5a47d9814..e33cf0d71 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: + * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. Bugfixes: diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index ebdc81a38..1412dea77 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -181,3 +182,15 @@ bool dev::isValidDecimal(string const& _string) return false; return true; } + +// Returns a quoted string if all characters are printable ASCII chars, +// or its hex representation otherwise. +std::string dev::formatAsStringOrNumber(std::string const& _value) +{ + if (_value.length() <= 32) + for (auto const& c: _value) + if (c <= 0x1f || c >= 0x7f || c == '"') + return "0x" + h256(_value, h256::AlignLeft).hex(); + + return "\"" + _value + "\""; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index d6e8f349e..02b037ce8 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -290,7 +290,6 @@ void iterateReplacing(std::vector& _vector, F const& _f) _vector = std::move(modifiedVector); } - namespace detail { template @@ -355,6 +354,10 @@ std::string getChecksummedAddress(std::string const& _addr); bool isValidHex(std::string const& _string); bool isValidDecimal(std::string const& _string); +/// @returns a quoted string if all characters are printable ASCII chars, +/// or its hex representation otherwise. +std::string formatAsStringOrNumber(std::string const& _value); + template bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare) { diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 03dc40c5b..224d55449 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -975,7 +976,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( for (size_t i = 0; i < words; ++i) { wordParams[i]["offset"] = to_string(i * 32); - wordParams[i]["wordValue"] = "0x" + h256(value.substr(32 * i, 32), h256::AlignLeft).hex(); + wordParams[i]["wordValue"] = formatAsStringOrNumber(value.substr(32 * i, 32)); } templ("word", wordParams); return templ.render(); @@ -990,7 +991,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( } )"); templ("functionName", functionName); - templ("wordValue", "0x" + h256(value, h256::AlignLeft).hex()); + templ("wordValue", formatAsStringOrNumber(value)); return templ.render(); } }); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 8910ad7e7..1699bf9d3 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -23,6 +23,8 @@ #include #include #include + +#include #include #include @@ -1756,7 +1758,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const for (size_t i = 0; i < words; ++i) { wordParams[i]["offset"] = to_string(32 + i * 32); - wordParams[i]["wordValue"] = "0x" + h256(data.substr(32 * i, 32), h256::AlignLeft).hex(); + wordParams[i]["wordValue"] = formatAsStringOrNumber(data.substr(32 * i, 32)); } templ("word", wordParams); return templ.render(); diff --git a/test/cmdlineTests/yul_string_format_ascii/input.json b/test/cmdlineTests/yul_string_format_ascii/input.json new file mode 100644 index 000000000..c23c65b5a --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcabc\"; } }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json new file mode 100644 index 000000000..40ed5700f --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -0,0 +1,161 @@ +{"contracts":{"A":{"C":{"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_10\" { + code { + mstore(64, 128) + + // Begin state variable initialization for contract \"C\" (0 variables) + // End state variable initialization for contract \"C\". + + + codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + return(0, datasize(\"C_10_deployed\")) + + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { + converted := allocateMemory(64) + mstore(converted, 6) + + mstore(add(converted, 32), \"abcabc\") + + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + return_flag := 0 + break + + break + } + } + + } + object \"C_10_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_9() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + revert(0, 0) + + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { + let length := array_length_t_string_memory_ptr(value) + pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) + copy_memory_to_memory(add(value, 0x20), pos, length) + end := add(pos, round_up_to_mul_of_32(length)) + } + + function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + mstore(add(headStart, 0), sub(tail, headStart)) + tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0, tail) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function array_length_t_string_memory_ptr(value) -> length { + + + length := mload(value) + + + + } + + function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { + mstore(pos, length) + updated_pos := add(pos, 0x20) + } + + function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { + converted := allocateMemory(64) + mstore(converted, 6) + + mstore(add(converted, 32), \"abcabc\") + + } + + function copy_memory_to_memory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + return_flag := 0 + break + + break + } + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + } +} + +"}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json new file mode 100644 index 000000000..247f665cb --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes32) { return \"abcabc\"; } }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json new file mode 100644 index 000000000..062cdfdc9 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -0,0 +1,114 @@ +{"contracts":{"A":{"C":{"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_10\" { + code { + mstore(64, 128) + + // Begin state variable initialization for contract \"C\" (0 variables) + // End state variable initialization for contract \"C\". + + + codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + return(0, datasize(\"C_10_deployed\")) + + + function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() -> converted { + converted := 0x6162636162630000000000000000000000000000000000000000000000000000 + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() + return_flag := 0 + break + + break + } + } + + } + object \"C_10_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_9() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + revert(0, 0) + + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_bytes32_to_t_bytes32_fromStack(value, pos) { + mstore(pos, cleanup_t_bytes32(value)) + } + + function abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + abi_encode_t_bytes32_to_t_bytes32_fromStack(value0, add(headStart, 0)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function cleanup_t_bytes32(value) -> cleaned { + cleaned := value + } + + function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() -> converted { + converted := 0x6162636162630000000000000000000000000000000000000000000000000000 + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() + return_flag := 0 + break + + break + } + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + } +} + +"}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json new file mode 100644 index 000000000..c7309f2af --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0x61626364; } }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json new file mode 100644 index 000000000..7c163ff12 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -0,0 +1,138 @@ +{"contracts":{"A":{"C":{"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_10\" { + code { + mstore(64, 128) + + // Begin state variable initialization for contract \"C\" (0 variables) + // End state variable initialization for contract \"C\". + + + codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + return(0, datasize(\"C_10_deployed\")) + + + function cleanup_t_rational_1633837924_by_1(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { + converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + let expr_6 := 0x61626364 + vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) + return_flag := 0 + break + + break + } + } + + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) + + } + + } + object \"C_10_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_9() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + revert(0, 0) + + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { + mstore(pos, cleanup_t_bytes4(value)) + } + + function abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + abi_encode_t_bytes4_to_t_bytes4_fromStack(value0, add(headStart, 0)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function cleanup_t_bytes4(value) -> cleaned { + cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) + } + + function cleanup_t_rational_1633837924_by_1(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { + converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + let expr_6 := 0x61626364 + vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) + return_flag := 0 + break + + break + } + } + + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) + + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + } +} + +"}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/yul_string_format_ascii_long/input.json b/test/cmdlineTests/yul_string_format_ascii_long/input.json new file mode 100644 index 000000000..cf3b2a854 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_long/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\"; } }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json new file mode 100644 index 000000000..8245d0dd9 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -0,0 +1,169 @@ +{"contracts":{"A":{"C":{"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_10\" { + code { + mstore(64, 128) + + // Begin state variable initialization for contract \"C\" (0 variables) + // End state variable initialization for contract \"C\". + + + codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + return(0, datasize(\"C_10_deployed\")) + + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { + converted := allocateMemory(128) + mstore(converted, 85) + + mstore(add(converted, 32), \"abcdabcdcafecafeabcdabcdcafecafe\") + + mstore(add(converted, 64), \"ffffzzzzoooo0123456789,.<,>.?:;'\") + + mstore(add(converted, 96), \"[{]}|`~!@#$%^&*()-_=+\") + + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + return_flag := 0 + break + + break + } + } + + } + object \"C_10_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_9() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + revert(0, 0) + + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { + let length := array_length_t_string_memory_ptr(value) + pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) + copy_memory_to_memory(add(value, 0x20), pos, length) + end := add(pos, round_up_to_mul_of_32(length)) + } + + function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + mstore(add(headStart, 0), sub(tail, headStart)) + tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0, tail) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function array_length_t_string_memory_ptr(value) -> length { + + + length := mload(value) + + + + } + + function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { + mstore(pos, length) + updated_pos := add(pos, 0x20) + } + + function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { + converted := allocateMemory(128) + mstore(converted, 85) + + mstore(add(converted, 32), \"abcdabcdcafecafeabcdabcdcafecafe\") + + mstore(add(converted, 64), \"ffffzzzzoooo0123456789,.<,>.?:;'\") + + mstore(add(converted, 96), \"[{]}|`~!@#$%^&*()-_=+\") + + } + + function copy_memory_to_memory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + return_flag := 0 + break + + break + } + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + } +} + +"}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/yul_string_format_hex/input.json b/test/cmdlineTests/yul_string_format_hex/input.json new file mode 100644 index 000000000..5ba723f56 --- /dev/null +++ b/test/cmdlineTests/yul_string_format_hex/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir"] } + } + } +} diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json new file mode 100644 index 000000000..38cbd08ea --- /dev/null +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -0,0 +1,138 @@ +{"contracts":{"A":{"C":{"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_10\" { + code { + mstore(64, 128) + + // Begin state variable initialization for contract \"C\" (0 variables) + // End state variable initialization for contract \"C\". + + + codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + return(0, datasize(\"C_10_deployed\")) + + + function cleanup_t_rational_2864434397_by_1(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { + converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + let expr_6 := 0xaabbccdd + vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) + return_flag := 0 + break + + break + } + } + + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) + + } + + } + object \"C_10_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_9() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + revert(0, 0) + + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { + mstore(pos, cleanup_t_bytes4(value)) + } + + function abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + abi_encode_t_bytes4_to_t_bytes4_fromStack(value0, add(headStart, 0)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + + function cleanup_t_bytes4(value) -> cleaned { + cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) + } + + function cleanup_t_rational_2864434397_by_1(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { + converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) + } + + function fun_f_9() -> vloc__4 { + for { let return_flag := 1 } return_flag {} { + let expr_6 := 0xaabbccdd + vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) + return_flag := 0 + break + + break + } + } + + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) + + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + } +} + +"}}},"sources":{"A":{"id":0}}} diff --git a/test/libsolidity/semanticTests/viaYul/string_format.sol b/test/libsolidity/semanticTests/viaYul/string_format.sol new file mode 100644 index 000000000..2d9d71d7c --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/string_format.sol @@ -0,0 +1,13 @@ +contract C { + function f1() external pure returns (string memory) { return "abcabc"; } + function f2() external pure returns (string memory) { return "abcabc`~12345677890- _=+!@#$%^&*()[{]}|;:',<.>?"; } + function g() external pure returns (bytes32) { return "abcabc"; } + function h() external pure returns (bytes4) { return 0xcafecafe; } +} +// ==== +// compileViaYul: only +// ---- +// f1() -> 0x20, 6, left(0x616263616263) +// f2() -> 32, 47, 44048183223289766195424279195050628400112610419087780792899004030957505095210, 18165586057823232067963737336409268114628061002662705707816940456850361417728 +// g() -> left(0x616263616263) +// h() -> left(0xcafecafe) From 6625f634fc38990237b3520c514268c65ef03032 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 Nov 2019 17:08:23 +0100 Subject: [PATCH 053/130] Wasm Interpreter --- test/tools/yulInterpreter/CMakeLists.txt | 2 + .../EWasmBuiltinInterpreter.cpp | 376 ++++++++++++++++++ .../yulInterpreter/EWasmBuiltinInterpreter.h | 108 +++++ test/tools/yulInterpreter/Interpreter.cpp | 11 + 4 files changed, 497 insertions(+) create mode 100644 test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp create mode 100644 test/tools/yulInterpreter/EWasmBuiltinInterpreter.h diff --git a/test/tools/yulInterpreter/CMakeLists.txt b/test/tools/yulInterpreter/CMakeLists.txt index 52fe0e3c6..02d53d697 100644 --- a/test/tools/yulInterpreter/CMakeLists.txt +++ b/test/tools/yulInterpreter/CMakeLists.txt @@ -1,6 +1,8 @@ set(sources EVMInstructionInterpreter.h EVMInstructionInterpreter.cpp + EWasmBuiltinInterpreter.h + EWasmBuiltinInterpreter.cpp Interpreter.h Interpreter.cpp ) diff --git a/test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp new file mode 100644 index 000000000..c52ee7e10 --- /dev/null +++ b/test/tools/yulInterpreter/EWasmBuiltinInterpreter.cpp @@ -0,0 +1,376 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Yul interpreter module that evaluates EWasm builtins. + */ + +#include + +#include + +#include +#include + +#include + +#include + +using namespace std; +using namespace dev; +using namespace yul; +using namespace yul::test; + +namespace +{ + +/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to +/// @a _target at offset @a _targetOffset. Behaves as if @a _source would +/// continue with an infinite sequence of zero bytes beyond its end. +void copyZeroExtended( + map& _target, bytes const& _source, + size_t _targetOffset, size_t _sourceOffset, size_t _size +) +{ + for (size_t i = 0; i < _size; ++i) + _target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0; +} + +} + +using u512 = boost::multiprecision::number>; + +u256 EWasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _arguments) +{ + vector arg; + for (u256 const& a: _arguments) + arg.emplace_back(uint64_t(a & uint64_t(-1))); + + if (_fun == "datasize"_yulstring) + return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; + else if (_fun == "dataoffset"_yulstring) + return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; + else if (_fun == "datacopy"_yulstring) + { + // This is identical to codecopy. + if (accessMemory(_arguments.at(0), _arguments.at(2))) + copyZeroExtended( + m_state.memory, + m_state.code, + size_t(_arguments.at(0)), + size_t(_arguments.at(1) & size_t(-1)), + size_t(_arguments.at(2)) + ); + return 0; + } + else if (_fun == "drop"_yulstring) + return {}; + else if (_fun == "unreachable"_yulstring) + throw ExplicitlyTerminated(); + else if (_fun == "i64.add"_yulstring) + return arg[0] + arg[1]; + else if (_fun == "i64.sub"_yulstring) + return arg[0] - arg[1]; + else if (_fun == "i64.mul"_yulstring) + return arg[0] * arg[1]; + else if (_fun == "i64.div_u"_yulstring) + { + if (arg[1] == 0) + throw ExplicitlyTerminated(); + else + return arg[0] / arg[1]; + } + else if (_fun == "i64.rem_u"_yulstring) + { + if (arg[1] == 0) + throw ExplicitlyTerminated(); + else + return arg[0] % arg[1]; + } + else if (_fun == "i64.and"_yulstring) + return arg[0] & arg[1]; + else if (_fun == "i64.or"_yulstring) + return arg[0] | arg[1]; + else if (_fun == "i64.xor"_yulstring) + return arg[0] ^ arg[1]; + else if (_fun == "i64.shl"_yulstring) + return arg[0] << arg[1]; + else if (_fun == "i64.shr_u"_yulstring) + return arg[0] >> arg[1]; + else if (_fun == "i64.eq"_yulstring) + return arg[0] == arg[1] ? 1 : 0; + else if (_fun == "i64.ne"_yulstring) + return arg[0] != arg[1] ? 1 : 0; + else if (_fun == "i64.eqz"_yulstring) + return arg[0] == 0 ? 1 : 0; + else if (_fun == "i64.lt_u"_yulstring) + return arg[0] < arg[1] ? 1 : 0; + else if (_fun == "i64.gt_u"_yulstring) + return arg[0] > arg[1] ? 1 : 0; + else if (_fun == "i64.le_u"_yulstring) + return arg[0] <= arg[1] ? 1 : 0; + else if (_fun == "i64.ge_u"_yulstring) + return arg[0] >= arg[1] ? 1 : 0; + else if (_fun == "i64.store"_yulstring) + { + accessMemory(arg[0], 8); + writeMemoryWord(arg[0], arg[1]); + return 0; + } + else if (_fun == "i64.load"_yulstring) + { + accessMemory(arg[0], 8); + return readMemoryWord(arg[0]); + } + else if (_fun == "eth.getAddress"_yulstring) + return writeAddress(arg[0], m_state.address); + else if (_fun == "eth.getExternalBalance"_yulstring) + // TODO this does not read the address, but is consistent with + // EVM interpreter implementation. + // If we take the address into account, this needs to use readAddress. + return writeU128(arg[0], m_state.balance); + else if (_fun == "eth.getBlockHash"_yulstring) + { + if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) + return 1; + else + return writeU256(arg[1], 0xaaaaaaaa + u256(arg[0] - m_state.blockNumber - 256)); + } + else if (_fun == "eth.call"_yulstring) + { + // TODO read args from memory + // TODO use readAddress to read address. + logTrace(eth::Instruction::CALL, {}); + return arg[0] & 1; + } + else if (_fun == "eth.callDataCopy"_yulstring) + { + if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.calldata.size()) + throw ExplicitlyTerminated(); + if (accessMemory(arg[0], arg[2])) + copyZeroExtended( + m_state.memory, m_state.calldata, + size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) + ); + return {}; + } + else if (_fun == "eth.getCallDataSize"_yulstring) + return m_state.calldata.size(); + else if (_fun == "eth.callCode"_yulstring) + { + // TODO read args from memory + // TODO use readAddress to read address. + logTrace(eth::Instruction::CALLCODE, {}); + return arg[0] & 1; + } + else if (_fun == "eth.callDelegate"_yulstring) + { + // TODO read args from memory + // TODO use readAddress to read address. + logTrace(eth::Instruction::DELEGATECALL, {}); + return arg[0] & 1; + } + else if (_fun == "eth.callStatic"_yulstring) + { + // TODO read args from memory + // TODO use readAddress to read address. + logTrace(eth::Instruction::STATICCALL, {}); + return arg[0] & 1; + } + else if (_fun == "eth.storageStore"_yulstring) + { + m_state.storage[h256(readU256(arg[0]))] = readU256((arg[1])); + return 0; + } + else if (_fun == "eth.storageLoad"_yulstring) + return writeU256(arg[1], m_state.storage[h256(readU256(arg[0]))]); + else if (_fun == "eth.getCaller"_yulstring) + // TODO should this only write 20 bytes? + return writeAddress(arg[0], m_state.caller); + else if (_fun == "eth.getCallValue"_yulstring) + return writeU128(arg[0], m_state.callvalue); + else if (_fun == "eth.codeCopy"_yulstring) + { + if (accessMemory(arg[0], arg[2])) + copyZeroExtended( + m_state.memory, m_state.code, + size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) + ); + return 0; + } + else if (_fun == "eth.getCodeSize"_yulstring) + return writeU256(arg[0], m_state.code.size()); + else if (_fun == "eth.getBlockCoinbase"_yulstring) + return writeAddress(arg[0], m_state.coinbase); + else if (_fun == "eth.create"_yulstring) + { + // TODO access memory + // TODO use writeAddress to store resulting address + logTrace(eth::Instruction::CREATE, {}); + return 0xcccccc + arg[1]; + } + else if (_fun == "eth.getBlockDifficulty"_yulstring) + return writeU256(arg[0], m_state.difficulty); + else if (_fun == "eth.externalCodeCopy"_yulstring) + { + // TODO use readAddress to read address. + if (accessMemory(arg[1], arg[3])) + // TODO this way extcodecopy and codecopy do the same thing. + copyZeroExtended( + m_state.memory, m_state.code, + size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) + ); + return 0; + } + else if (_fun == "eth.getExternalCodeSize"_yulstring) + return u256(keccak256(h256(readAddress(arg[0])))) & 0xffffff; + else if (_fun == "eth.getGasLeft"_yulstring) + return 0x99; + else if (_fun == "eth.getBlockGasLimit"_yulstring) + return uint64_t(m_state.gaslimit); + else if (_fun == "eth.getTxGasPrice"_yulstring) + return writeU128(arg[0], m_state.gasprice); + else if (_fun == "eth.log"_yulstring) + { + logTrace(eth::Instruction::LOG0, {}); + return 0; + } + else if (_fun == "eth.getBlockNumber"_yulstring) + return m_state.blockNumber; + else if (_fun == "eth.getTxOrigin"_yulstring) + return writeAddress(arg[0], m_state.origin); + else if (_fun == "eth.finish"_yulstring) + { + bytes data; + if (accessMemory(arg[0], arg[1])) + data = readMemory(arg[0], arg[1]); + logTrace(eth::Instruction::RETURN, {}, data); + throw ExplicitlyTerminated(); + } + else if (_fun == "eth.revert"_yulstring) + { + bytes data; + if (accessMemory(arg[0], arg[1])) + data = readMemory(arg[0], arg[1]); + logTrace(eth::Instruction::REVERT, {}, data); + throw ExplicitlyTerminated(); + } + else if (_fun == "eth.getReturnDataSize"_yulstring) + return m_state.returndata.size(); + else if (_fun == "eth.returnDataCopy"_yulstring) + { + if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.returndata.size()) + throw ExplicitlyTerminated(); + if (accessMemory(arg[0], arg[2])) + copyZeroExtended( + m_state.memory, m_state.calldata, + size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) + ); + return {}; + } + else if (_fun == "eth.selfDestruct"_yulstring) + { + // TODO use readAddress to read address. + logTrace(eth::Instruction::SELFDESTRUCT, {}); + throw ExplicitlyTerminated(); + } + else if (_fun == "eth.getBlockTimestamp"_yulstring) + return m_state.timestamp; + + yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + + return 0; +} + +bool EWasmBuiltinInterpreter::accessMemory(u256 const& _offset, u256 const& _size) +{ + if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) + { + u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f); + m_state.msize = max(m_state.msize, newSize); + return _size <= 0xffff; + } + else + m_state.msize = u256(-1); + + return false; +} + +bytes EWasmBuiltinInterpreter::readMemory(uint64_t _offset, uint64_t _size) +{ + yulAssert(_size <= 0xffff, "Too large read."); + bytes data(size_t(_size), uint8_t(0)); + for (size_t i = 0; i < data.size(); ++i) + data[i] = m_state.memory[_offset + i]; + return data; +} + +uint64_t EWasmBuiltinInterpreter::readMemoryWord(uint64_t _offset) +{ + uint64_t r = 0; + for (size_t i = 0; i < 8; i++) + r |= uint64_t(m_state.memory[_offset + i]) << (i * 8); + return r; +} + +void EWasmBuiltinInterpreter::writeMemoryWord(uint64_t _offset, uint64_t _value) +{ + for (size_t i = 0; i < 8; i++) + m_state.memory[_offset + i] = uint8_t((_value >> (i * 8)) & 0xff); +} + +u256 EWasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _croppedTo) +{ + accessMemory(_offset, _croppedTo); + for (size_t i = 0; i < _croppedTo; i++) + { + m_state.memory[_offset + _croppedTo - 1 - i] = uint8_t(_value & 0xff); + _value >>= 8; + } + + return {}; +} + +u256 EWasmBuiltinInterpreter::readU256(uint64_t _offset, size_t _croppedTo) +{ + accessMemory(_offset, _croppedTo); + u256 value; + for (size_t i = 0; i < _croppedTo; i++) + value = (value << 8) | m_state.memory[_offset + i]; + + return value; +} + +void EWasmBuiltinInterpreter::logTrace(dev::eth::Instruction _instruction, std::vector const& _arguments, bytes const& _data) +{ + logTrace(dev::eth::instructionInfo(_instruction).name, _arguments, _data); +} + +void EWasmBuiltinInterpreter::logTrace(std::string const& _pseudoInstruction, std::vector const& _arguments, bytes const& _data) +{ + string message = _pseudoInstruction + "("; + for (size_t i = 0; i < _arguments.size(); ++i) + message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); + message += ")"; + if (!_data.empty()) + message += " [" + toHex(_data) + "]"; + m_state.trace.emplace_back(std::move(message)); + if (m_state.maxTraceSize > 0 && m_state.trace.size() >= m_state.maxTraceSize) + { + m_state.trace.emplace_back("Trace size limit reached."); + throw TraceLimitReached(); + } +} diff --git a/test/tools/yulInterpreter/EWasmBuiltinInterpreter.h b/test/tools/yulInterpreter/EWasmBuiltinInterpreter.h new file mode 100644 index 000000000..e96cbbae2 --- /dev/null +++ b/test/tools/yulInterpreter/EWasmBuiltinInterpreter.h @@ -0,0 +1,108 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Yul interpreter module that evaluates EWasm builtins. + */ + +#pragma once + +#include + +#include + +#include + +namespace dev +{ +namespace eth +{ +enum class Instruction: uint8_t; +} +} + +namespace yul +{ +class YulString; +struct BuiltinFunctionForEVM; + +namespace test +{ + +struct InterpreterState; + +/** + * Interprets EWasm builtins based on the current state and logs instructions with + * side-effects. + * + * Since this is mainly meant to be used for differential fuzz testing, it is focused + * on a single contract only, does not do any gas counting and differs from the correct + * implementation in many ways: + * + * - If memory access to a "large" memory position is performed, a deterministic + * value is returned. Data that is stored in a "large" memory position is not + * retained. + * - The blockhash instruction returns a fixed value if the argument is in range. + * - Extcodesize returns a deterministic value depending on the address. + * - Extcodecopy copies a deterministic value depending on the address. + * - And many other things + * + * The main focus is that the generated execution trace is the same for equivalent executions + * and likely to be different for non-equivalent executions. + */ +class EWasmBuiltinInterpreter +{ +public: + explicit EWasmBuiltinInterpreter(InterpreterState& _state): + m_state(_state) + {} + /// Evaluate builtin function + dev::u256 evalBuiltin(YulString _fun, std::vector const& _arguments); + +private: + /// Checks if the memory access is not too large for the interpreter and adjusts + /// msize accordingly. + /// @returns false if the amount of bytes read is lager than 0xffff + bool accessMemory(dev::u256 const& _offset, dev::u256 const& _size = 32); + /// @returns the memory contents at the provided address. + /// Does not adjust msize, use @a accessMemory for that + dev::bytes readMemory(uint64_t _offset, uint64_t _size = 32); + /// @returns the memory contents at the provided address (little-endian). + /// Does not adjust msize, use @a accessMemory for that + uint64_t readMemoryWord(uint64_t _offset); + /// Writes a word to memory (little-endian) + /// Does not adjust msize, use @a accessMemory for that + void writeMemoryWord(uint64_t _offset, uint64_t _value); + + /// Helper for eth.* builtins. Writes to memory (big-endian) and always returns zero. + dev::u256 writeU256(uint64_t _offset, dev::u256 _value, size_t _croppedTo = 32); + dev::u256 writeU128(uint64_t _offset, dev::u256 _value) { return writeU256(_offset, std::move(_value), 16); } + dev::u256 writeAddress(uint64_t _offset, dev::u256 _value) { return writeU256(_offset, std::move(_value), 20); } + /// Helper for eth.* builtins. Reads from memory (big-endian) and returns the value; + dev::u256 readU256(uint64_t _offset, size_t _croppedTo = 32); + dev::u256 readU128(uint64_t _offset) { return readU256(_offset, 16); } + dev::u256 readAddress(uint64_t _offset) { return readU256(_offset, 20); } + + void logTrace(dev::eth::Instruction _instruction, std::vector const& _arguments = {}, dev::bytes const& _data = {}); + /// Appends a log to the trace representing an instruction or similar operation by string, + /// with arguments and auxiliary data (if nonempty). + void logTrace(std::string const& _pseudoInstruction, std::vector const& _arguments = {}, dev::bytes const& _data = {}); + + InterpreterState& m_state; +}; + +} +} diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index d1b361c4e..93eee5f80 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -21,11 +21,13 @@ #include #include +#include #include #include #include #include +#include #include @@ -229,12 +231,21 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) evaluateArgs(_funCall.arguments); if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) + { if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) { EVMInstructionInterpreter interpreter(m_state); setValue(interpreter.evalBuiltin(*fun, values())); return; } + } + else if (WasmDialect const* dialect = dynamic_cast(&m_dialect)) + if (dialect->builtin(_funCall.functionName.name)) + { + EWasmBuiltinInterpreter interpreter(m_state); + setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); + return; + } auto [functionScopes, fun] = findFunctionAndScope(_funCall.functionName.name); From d4d0df021d1ab3076d5f3a349c19341840d0fb1b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 19 Nov 2019 23:08:33 +0100 Subject: [PATCH 054/130] EWasm translation tests. --- test/CMakeLists.txt | 2 + test/InteractiveTests.h | 2 + test/libyul/EWasmTranslationTest.cpp | 156 ++++++++++++++++++ test/libyul/EWasmTranslationTest.h | 65 ++++++++ .../libyul/ewasmTranslationTests/datacopy.yul | 20 +++ .../ewasmTranslationTests/dataoffset.yul | 16 ++ .../libyul/ewasmTranslationTests/datasize.yul | 16 ++ .../ewasmTranslationTests/mstore_mload.yul | 14 ++ test/libyul/ewasmTranslationTests/shl.yul | 17 ++ .../ewasmTranslationTests/simple_mstore.yul | 8 + .../ewasmTranslationTests/simple_sstore.yul | 12 ++ test/libyul/ewasmTranslationTests/smoke.yul | 5 + test/tools/CMakeLists.txt | 1 + 13 files changed, 334 insertions(+) create mode 100644 test/libyul/EWasmTranslationTest.cpp create mode 100644 test/libyul/EWasmTranslationTest.h create mode 100644 test/libyul/ewasmTranslationTests/datacopy.yul create mode 100644 test/libyul/ewasmTranslationTests/dataoffset.yul create mode 100644 test/libyul/ewasmTranslationTests/datasize.yul create mode 100644 test/libyul/ewasmTranslationTests/mstore_mload.yul create mode 100644 test/libyul/ewasmTranslationTests/shl.yul create mode 100644 test/libyul/ewasmTranslationTests/simple_mstore.yul create mode 100644 test/libyul/ewasmTranslationTests/simple_sstore.yul create mode 100644 test/libyul/ewasmTranslationTests/smoke.yul diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 387d6e515..b0c3b47da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -129,6 +129,8 @@ set(libyul_sources libyul/Common.cpp libyul/Common.h libyul/CompilabilityChecker.cpp + libyul/EWasmTranslationTest.cpp + libyul/EWasmTranslationTest.h libyul/FunctionSideEffects.cpp libyul/FunctionSideEffects.h libyul/Inliner.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index b75be49e7..591d1d7ed 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct Testsuite Testsuite const g_interactiveTestsuites[] = { /* Title Path Subpath SMT NeedsVM Creator function */ + {"EWasm Translation", "libyul", "ewasmTranslationTests",false,false, &yul::test::EWasmTranslationTest::create}, {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, diff --git a/test/libyul/EWasmTranslationTest.cpp b/test/libyul/EWasmTranslationTest.cpp new file mode 100644 index 000000000..dc174ff7b --- /dev/null +++ b/test/libyul/EWasmTranslationTest.cpp @@ -0,0 +1,156 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace yul::test; +using namespace dev::solidity; +using namespace dev::solidity::test; +using namespace std; + + +EWasmTranslationTest::EWasmTranslationTest(string const& _filename) +{ + boost::filesystem::path path(_filename); + + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); + file.exceptions(ios::badbit); + + m_source = parseSourceAndSettings(file); + m_expectation = parseSimpleExpectations(file); +} + +TestCase::TestResult EWasmTranslationTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + if (!parse(_stream, _linePrefix, _formatted)) + return TestResult::FatalError; + + *m_object = EVMToEWasmTranslator( + EVMDialect::strictAssemblyForEVMObjects(dev::test::Options::get().evmVersion()) + ).run(*m_object); + + // Add call to "main()". + m_object->code->statements.emplace_back( + ExpressionStatement{{}, FunctionCall{{}, Identifier{{}, "main"_yulstring}, {}}} + ); + + m_obtainedResult = interpret(); + + if (m_expectation != m_obtainedResult) + { + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl; + // TODO could compute a simple diff with highlighted lines + printIndented(_stream, m_expectation, nextIndentLevel); + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl; + printIndented(_stream, m_obtainedResult, nextIndentLevel); + return TestResult::Failure; + } + return TestResult::Success; +} + +void EWasmTranslationTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const +{ + printIndented(_stream, m_source, _linePrefix); +} + +void EWasmTranslationTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const +{ + printIndented(_stream, m_obtainedResult, _linePrefix); +} + +void EWasmTranslationTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const +{ + stringstream output(_output); + string line; + while (getline(output, line)) + _stream << _linePrefix << line << endl; +} + +bool EWasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + AssemblyStack stack( + dev::test::Options::get().evmVersion(), + AssemblyStack::Language::StrictAssembly, + dev::solidity::OptimiserSettings::none() + ); + if (stack.parseAndAnalyze("", m_source)) + { + m_object = stack.parserResult(); + return true; + } + else + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + printErrors(_stream, stack.errors()); + return false; + } +} + +string EWasmTranslationTest::interpret() +{ + InterpreterState state; + state.maxTraceSize = 10000; + state.maxSteps = 10000; + WasmDialect dialect; + Interpreter interpreter(state, dialect); + try + { + interpreter(*m_object->code); + } + catch (InterpreterTerminatedGeneric const&) + { + } + + stringstream result; + state.dumpTraceAndState(result); + return result.str(); +} + +void EWasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors) +{ + SourceReferenceFormatter formatter(_stream); + + for (auto const& error: _errors) + formatter.printErrorInformation(*error); +} diff --git a/test/libyul/EWasmTranslationTest.h b/test/libyul/EWasmTranslationTest.h new file mode 100644 index 000000000..351f83607 --- /dev/null +++ b/test/libyul/EWasmTranslationTest.h @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include + +namespace langutil +{ +class Scanner; +class Error; +using ErrorList = std::vector>; +} + +namespace yul +{ +namespace test +{ + +class EWasmTranslationTest: public dev::solidity::test::EVMVersionRestrictedTestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::unique_ptr(new EWasmTranslationTest(_config.filename)); + } + + explicit EWasmTranslationTest(std::string const& _filename); + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + + void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; + +private: + void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const; + bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); + std::string interpret(); + + static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); + + std::string m_source; + std::string m_expectation; + + std::shared_ptr m_object; + std::string m_obtainedResult; +}; + +} +} diff --git a/test/libyul/ewasmTranslationTests/datacopy.yul b/test/libyul/ewasmTranslationTests/datacopy.yul new file mode 100644 index 000000000..8812e7c9b --- /dev/null +++ b/test/libyul/ewasmTranslationTests/datacopy.yul @@ -0,0 +1,20 @@ +object "main" +{ + code { + datacopy(0, and(dataoffset("main"), 15), and(datasize("main"), 15)) + datacopy(32, and(dataoffset("sub"), 15), and(datasize("sub"), 15)) + sstore(0, mload(0)) + sstore(1, mload(32)) + } + object "sub" { code { sstore(0, 1) } } +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 636f6465636f6465000000000000000000000000000000000000000000000000 +// 40: 6465636f00000000000000000000000000000000000000000000000000000000 +// 60: 636f6465636f6465000000000000000000000000000000000000000000000000 +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 6465636f00000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000001: 636f6465636f6465000000000000000000000000000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/dataoffset.yul b/test/libyul/ewasmTranslationTests/dataoffset.yul new file mode 100644 index 000000000..d9dc12c9c --- /dev/null +++ b/test/libyul/ewasmTranslationTests/dataoffset.yul @@ -0,0 +1,16 @@ +object "main" +{ + code { + sstore(0, dataoffset("main")) + sstore(1, dataoffset("sub")) + } + object "sub" { code { sstore(0, 1) } } +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 000000000000000000000000000000000000000000000000000000000000070c +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000006e +// 0000000000000000000000000000000000000000000000000000000000000001: 000000000000000000000000000000000000000000000000000000000000070c diff --git a/test/libyul/ewasmTranslationTests/datasize.yul b/test/libyul/ewasmTranslationTests/datasize.yul new file mode 100644 index 000000000..637c2c36d --- /dev/null +++ b/test/libyul/ewasmTranslationTests/datasize.yul @@ -0,0 +1,16 @@ +object "main" +{ + code { + sstore(0, datasize("main")) + sstore(1, datasize("sub")) + } + object "sub" { code { sstore(0, 1) } } +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 0000000000000000000000000000000000000000000000000000000000000109 +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000b64 +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000109 diff --git a/test/libyul/ewasmTranslationTests/mstore_mload.yul b/test/libyul/ewasmTranslationTests/mstore_mload.yul new file mode 100644 index 000000000..95646abe2 --- /dev/null +++ b/test/libyul/ewasmTranslationTests/mstore_mload.yul @@ -0,0 +1,14 @@ +{ + mstore(0x20, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20) + mstore(0x40, mload(0x20)) + sstore(1, mload(0x40)) +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 +// 60: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 +// 80: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000001: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 diff --git a/test/libyul/ewasmTranslationTests/shl.yul b/test/libyul/ewasmTranslationTests/shl.yul new file mode 100644 index 000000000..f49092a30 --- /dev/null +++ b/test/libyul/ewasmTranslationTests/shl.yul @@ -0,0 +1,17 @@ +{ + let x := 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + let y := shl(120, x) + let z := shr(136, y) + sstore(0, y) + sstore(1, z) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 101112131415161718191a1b1c1d1e1f20000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000101112131415161718191a1b1c1d1e diff --git a/test/libyul/ewasmTranslationTests/simple_mstore.yul b/test/libyul/ewasmTranslationTests/simple_mstore.yul new file mode 100644 index 000000000..dd9ac1d5e --- /dev/null +++ b/test/libyul/ewasmTranslationTests/simple_mstore.yul @@ -0,0 +1,8 @@ +{ + mstore(0x20, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20) +} +// ---- +// Trace: +// Memory dump: +// 60: 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 +// Storage dump: diff --git a/test/libyul/ewasmTranslationTests/simple_sstore.yul b/test/libyul/ewasmTranslationTests/simple_sstore.yul new file mode 100644 index 000000000..8fbb4923a --- /dev/null +++ b/test/libyul/ewasmTranslationTests/simple_sstore.yul @@ -0,0 +1,12 @@ +{ + sstore(1, 7) + sstore(2, sub(0, 1)) +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000002 +// 20: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000007 +// 0000000000000000000000000000000000000000000000000000000000000002: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libyul/ewasmTranslationTests/smoke.yul b/test/libyul/ewasmTranslationTests/smoke.yul new file mode 100644 index 000000000..f7f1a1aef --- /dev/null +++ b/test/libyul/ewasmTranslationTests/smoke.yul @@ -0,0 +1,5 @@ +{} +// ---- +// Trace: +// Memory dump: +// Storage dump: diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index f02b092bd..70a79ffdf 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(isoltest ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/Common.cpp + ../libyul/EWasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp ../libyul/ObjectCompilerTest.cpp ../libyul/YulOptimizerTest.cpp From 2bfa3a7c97724a2dbccafc1a9186b43b404bc5ff Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 24 Nov 2019 18:46:43 +0100 Subject: [PATCH 055/130] Rewrite GenericVisitor --- libdevcore/Visitor.h | 128 +++++---------------- libyul/AsmAnalysis.cpp | 8 +- libyul/AsmScope.h | 2 - libyul/backends/evm/EVMCodeTransform.cpp | 16 +-- libyul/optimiser/ControlFlowSimplifier.cpp | 6 +- libyul/optimiser/FullInliner.cpp | 13 ++- libyul/optimiser/StructuralSimplifier.cpp | 5 +- libyul/optimiser/VarDeclInitializer.cpp | 4 +- 8 files changed, 59 insertions(+), 123 deletions(-) diff --git a/libdevcore/Visitor.h b/libdevcore/Visitor.h index c7f117d38..b8d0708d4 100644 --- a/libdevcore/Visitor.h +++ b/libdevcore/Visitor.h @@ -20,109 +20,41 @@ #pragma once -#include -#include - namespace dev { -/// Generic visitor used as follows: -/// std::visit(GenericVisitor( -/// [](Class1& _c) { _c.f(); }, -/// [](Class2& _c) { _c.g(); } -/// ), variant); -/// This one does not have a fallback and will fail at -/// compile-time if you do not specify all variants. +/** + * Generic visitor used as follows: + * std::visit(GenericVisitor{ + * [](Class1& _c) { _c.f(); }, + * [](Class2& _c) { _c.g(); } + * }, variant); + * This one does not have a fallback and will fail at + * compile-time if you do not specify all variants. + * + * Fallback with no return: + * std::visit(GenericVisitor{ + * VisitorFallback<>{}, + * [](Class1& _c) { _c.f(); }, + * [](Class2& _c) { _c.g(); } + * }, variant); + * + * Fallback with return type R: + * std::visit(GenericVisitor{ + * VisitorFallback{}, + * [](Class1& _c) { _c.f(); }, + * [](Class2& _c) { _c.g(); } + * }, variant); + */ -template -struct GenericVisitor{}; +template struct VisitorFallback; -template -struct GenericVisitor: public GenericVisitor -{ - using GenericVisitor::operator (); - explicit GenericVisitor( - std::function _visitor, - std::function... _otherVisitors - ): - GenericVisitor(std::move(_otherVisitors)...), - m_visitor(std::move(_visitor)) - {} +template +struct VisitorFallback { template R operator()(T&&) const { return {}; } }; - void operator()(Visitable& _v) const { m_visitor(_v); } - - std::function m_visitor; -}; -template <> -struct GenericVisitor<>: public boost::static_visitor<> { - void operator()() const {} -}; - -/// Generic visitor with fallback: -/// std::visit(GenericFallbackVisitor( -/// [](Class1& _c) { _c.f(); }, -/// [](Class2& _c) { _c.g(); } -/// ), variant); -/// This one DOES have a fallback and will NOT fail at -/// compile-time if you do not specify all variants. - -template -struct GenericFallbackVisitor{}; - -template -struct GenericFallbackVisitor: public GenericFallbackVisitor -{ - explicit GenericFallbackVisitor( - std::function _visitor, - std::function... _otherVisitors - ): - GenericFallbackVisitor(std::move(_otherVisitors)...), - m_visitor(std::move(_visitor)) - {} - - using GenericFallbackVisitor::operator (); - void operator()(Visitable& _v) const { m_visitor(_v); } - - std::function m_visitor; -}; -template <> -struct GenericFallbackVisitor<>: public boost::static_visitor<> { - template - void operator()(T&) const { } -}; - -/// Generic visitor with fallback that can return a value: -/// std::visit(GenericFallbackReturnsVisitor( -/// [](Class1& _c) { return _c.f(); }, -/// [](Class2& _c) { return _c.g(); } -/// ), variant); -/// This one DOES have a fallback and will NOT fail at -/// compile-time if you do not specify all variants. -/// The fallback {}-constructs the return value. - -template -struct GenericFallbackReturnsVisitor{}; - -template -struct GenericFallbackReturnsVisitor: public GenericFallbackReturnsVisitor -{ - explicit GenericFallbackReturnsVisitor( - std::function _visitor, - std::function... _otherVisitors - ): - GenericFallbackReturnsVisitor(std::move(_otherVisitors)...), - m_visitor(std::move(_visitor)) - {} - - using GenericFallbackReturnsVisitor::operator (); - R operator()(Visitable& _v) const { return m_visitor(_v); } - - std::function m_visitor; -}; -template -struct GenericFallbackReturnsVisitor: public boost::static_visitor { - template - R operator()(T&) const { return {}; } -}; +template<> +struct VisitorFallback<> { template void operator()(T&&) const {} }; +template struct GenericVisitor: Visitors... { using Visitors::operator()...; }; +template GenericVisitor(Visitors...) -> GenericVisitor; } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 35e933cd1..80a39f386 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -146,7 +146,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) solAssert(!_identifier.name.empty(), ""); size_t numErrorsBefore = m_errorReporter.errors().size(); bool success = true; - if (m_currentScope->lookup(_identifier.name, Scope::Visitor( + if (m_currentScope->lookup(_identifier.name, GenericVisitor{ [&](Scope::Variable const& _var) { if (!m_activeVariables.count(&_var)) @@ -171,7 +171,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) ); success = false; } - ))) + })) { } else @@ -341,7 +341,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) if (f->literalArguments) needsLiteralArguments = true; } - else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( + else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ [&](Scope::Variable const&) { m_errorReporter.typeError( @@ -364,7 +364,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall) parameters = _fun.arguments.size(); returns = _fun.returns.size(); } - ))) + })) { m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); success = false; diff --git a/libyul/AsmScope.h b/libyul/AsmScope.h index e8cd6ef64..9e3a74d03 100644 --- a/libyul/AsmScope.h +++ b/libyul/AsmScope.h @@ -48,8 +48,6 @@ struct Scope }; using Identifier = std::variant; - using Visitor = dev::GenericVisitor; - using NonconstVisitor = dev::GenericVisitor; bool registerVariable(YulString _name, YulType const& _type); bool registerLabel(YulString _name); diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index ee564ed6b..8e04969c1 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -82,14 +82,14 @@ void VariableReferenceCounter::operator()(Block const& _block) void VariableReferenceCounter::increaseRefIfFound(YulString _variableName) { - m_scope->lookup(_variableName, Scope::Visitor( + m_scope->lookup(_variableName, GenericVisitor{ [=](Scope::Variable const& _var) { ++m_context.variableReferences[&_var]; }, [=](Scope::Label const&) { }, [=](Scope::Function const&) { } - )); + }); } CodeTransform::CodeTransform( @@ -302,11 +302,11 @@ void CodeTransform::operator()(FunctionCall const& _call) } Scope::Function* function = nullptr; - solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor( + solAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{ [=](Scope::Variable&) { solAssert(false, "Expected function name."); }, [=](Scope::Label&) { solAssert(false, "Expected function name."); }, [&](Scope::Function& _function) { function = &_function; } - )), "Function name not found."); + }), "Function name not found."); solAssert(function, ""); solAssert(function->arguments.size() == _call.arguments.size(), ""); for (auto const& arg: _call.arguments | boost::adaptors::reversed) @@ -363,7 +363,7 @@ void CodeTransform::operator()(Identifier const& _identifier) m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. solAssert(m_scope, ""); - if (m_scope->lookup(_identifier.name, Scope::NonconstVisitor( + if (m_scope->lookup(_identifier.name, GenericVisitor{ [=](Scope::Variable& _var) { // TODO: opportunity for optimization: Do not DUP if this is the last reference @@ -383,7 +383,7 @@ void CodeTransform::operator()(Identifier const& _identifier) { solAssert(false, "Function not removed during desugaring."); } - ))) + })) { return; } @@ -682,14 +682,14 @@ void CodeTransform::operator()(Block const& _block) AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier) { AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); - if (!m_scope->lookup(_identifier.name, Scope::NonconstVisitor( + if (!m_scope->lookup(_identifier.name, GenericVisitor{ [=](Scope::Variable&) { solAssert(false, "Expected label"); }, [&](Scope::Label& _label) { label = labelID(_label); }, [=](Scope::Function&) { solAssert(false, "Expected label"); } - ))) + })) { solAssert(false, "Identifier not found."); } diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 65da3e8fb..4ebb35871 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -180,7 +180,8 @@ void ControlFlowSimplifier::visit(Statement& _st) void ControlFlowSimplifier::simplify(std::vector& _statements) { - GenericFallbackReturnsVisitor const visitor( + GenericVisitor visitor{ + VisitorFallback{}, [&](If& _ifStmt) -> OptionalStatements { if (_ifStmt.body.statements.empty() && m_dialect.discardFunction()) { @@ -205,8 +206,7 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) return {}; } - ); - + }; iterateReplacing( _statements, [&](Statement& _stmt) -> OptionalStatements diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 54c8555d2..75bbb49c3 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -157,17 +157,19 @@ void InlineModifier::operator()(Block& _block) std::optional> InlineModifier::tryInlineStatement(Statement& _statement) { // Only inline for expression statements, assignments and variable declarations. - Expression* e = std::visit(GenericFallbackReturnsVisitor( + Expression* e = std::visit(GenericVisitor{ + VisitorFallback{}, [](ExpressionStatement& _s) { return &_s.expression; }, [](Assignment& _s) { return _s.value.get(); }, [](VariableDeclaration& _s) { return _s.value.get(); } - ), _statement); + }, _statement); if (e) { // Only inline direct function calls. - FunctionCall* funCall = std::visit(GenericFallbackReturnsVisitor( + FunctionCall* funCall = std::visit(GenericVisitor{ + VisitorFallback{}, [](FunctionCall& _e) { return &_e; } - ), *e); + }, *e); if (funCall && m_driver.shallInline(*funCall, m_currentFunction)) return performInline(_statement, *funCall); } @@ -205,7 +207,8 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC Statement newBody = BodyCopier(m_nameDispenser, variableReplacements)(function->body); newStatements += std::move(std::get(newBody).statements); - std::visit(GenericFallbackVisitor{ + std::visit(GenericVisitor{ + VisitorFallback<>{}, [&](Assignment& _assignment) { for (size_t i = 0; i < _assignment.variableNames.size(); ++i) diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 424caf575..fb97e22a8 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -71,7 +71,8 @@ void StructuralSimplifier::operator()(Block& _block) void StructuralSimplifier::simplify(std::vector& _statements) { - GenericFallbackReturnsVisitor const visitor( + GenericVisitor visitor{ + VisitorFallback{}, [&](If& _ifStmt) -> OptionalStatements { if (expressionAlwaysTrue(*_ifStmt.condition)) return {std::move(_ifStmt.body.statements)}; @@ -89,7 +90,7 @@ void StructuralSimplifier::simplify(std::vector& _statements) return {std::move(_forLoop.pre.statements)}; return {}; } - ); + }; iterateReplacing( _statements, diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index d6f07bc1e..abedfe069 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -30,7 +30,8 @@ void VarDeclInitializer::operator()(Block& _block) ASTModifier::operator()(_block); using OptionalStatements = std::optional>; - GenericFallbackReturnsVisitor visitor{ + GenericVisitor visitor{ + VisitorFallback{}, [](VariableDeclaration& _varDecl) -> OptionalStatements { if (_varDecl.value) @@ -51,5 +52,6 @@ void VarDeclInitializer::operator()(Block& _block) } } }; + iterateReplacing(_block.statements, [&](auto&& _statement) { return std::visit(visitor, _statement); }); } From e48f902c28bfdb812ddc21307144968a409fd2ac Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 26 Nov 2019 15:54:57 +0100 Subject: [PATCH 056/130] Clarify documentation --- libdevcore/Visitor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libdevcore/Visitor.h b/libdevcore/Visitor.h index b8d0708d4..3d79e03e8 100644 --- a/libdevcore/Visitor.h +++ b/libdevcore/Visitor.h @@ -32,14 +32,14 @@ namespace dev * This one does not have a fallback and will fail at * compile-time if you do not specify all variants. * - * Fallback with no return: + * Fallback with no return (it will not fail if you do not specify all variants): * std::visit(GenericVisitor{ * VisitorFallback<>{}, * [](Class1& _c) { _c.f(); }, * [](Class2& _c) { _c.g(); } * }, variant); * - * Fallback with return type R: + * Fallback with return type R (the fallback returns `R{}`: * std::visit(GenericVisitor{ * VisitorFallback{}, * [](Class1& _c) { _c.f(); }, From f7d6c7e332c10caba33af2f0b23051378452ea77 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 18 Nov 2019 13:10:34 +0100 Subject: [PATCH 057/130] [DOC] Some more explanation for selfdestruct. --- docs/units-and-global-variables.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 8b8c06735..6c625e997 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -262,7 +262,15 @@ Contract Related the current contract, explicitly convertible to :ref:`address` ``selfdestruct(address payable recipient)``: - destroy the current contract, sending its funds to the given :ref:`address` + Destroy the current contract, sending its funds to the given :ref:`address` + and end execution. + Note that ``selfdestruct`` has some peculiarities inherited from the EVM: + + - the receiving contract's receive function is not executed. + - the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction. + + + Furthermore, all functions of the current contract are callable directly including the current function. From 91b85ccb537fc41a17dbd248322da3587d6ad00f Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 26 Nov 2019 16:53:22 +0100 Subject: [PATCH 058/130] We can't have 13 PRs it's bad luck Context: https://twitter.com/ethchris/status/1199354471920349184 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0af8dd905..1ccc2e53a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # The Solidity Contract-Oriented Programming Language [![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. +Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform ## Table of Contents From 831014634e2c303e14ecd00894c2036a9daffa13 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 26 Nov 2019 16:55:31 +0100 Subject: [PATCH 059/130] Revert "We can't have 13 PRs" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ccc2e53a..0af8dd905 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # The Solidity Contract-Oriented Programming Language [![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform +Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. ## Table of Contents From 41bdc9b67347b73a1b839e898ee09c15147c2ab9 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Tue, 5 Nov 2019 23:37:04 +0100 Subject: [PATCH 060/130] yul proto fuzzer: Generalize variable references --- test/tools/ossfuzz/protoToYul.cpp | 125 +++++++++++++------ test/tools/ossfuzz/protoToYul.h | 36 ++++-- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 5 +- 3 files changed, 113 insertions(+), 53 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 6fe9c2452..95a42f49f 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -90,12 +90,33 @@ string ProtoConverter::visit(Literal const& _x) } } +void ProtoConverter::consolidateVarDeclsInFunctionDef() +{ + m_currentFuncVars.clear(); + auto &scopes = m_funcVars.back(); + for (auto &s: scopes) + m_currentFuncVars.insert(m_currentFuncVars.end(), s.begin(), s.end()); +} + +void ProtoConverter::consolidateGlobalVarDecls() +{ + m_globalVars.clear(); + for (auto &scope: m_variables) + m_globalVars.insert(m_globalVars.end(), scope.begin(), scope.end()); +} + bool ProtoConverter::varDeclAvailable() { if (m_inFunctionDef) - return m_scopeVars.top().size() > 0; + { + consolidateVarDeclsInFunctionDef(); + return m_currentFuncVars.size() > 0; + } else - return m_variables.size() > 0; + { + consolidateGlobalVarDecls(); + return m_globalVars.size() > 0; + } } bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type) @@ -109,14 +130,14 @@ void ProtoConverter::visit(VarRef const& _x) if (m_inFunctionDef) { // Ensure that there is at least one variable declaration to reference in function scope. - yulAssert(m_scopeVars.top().size() > 0, "Proto fuzzer: No variables to reference."); - m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()]; + yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference."); + m_output << m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()]; } else { // Ensure that there is at least one variable declaration to reference in nested scopes. - yulAssert(m_variables.size() > 0, "Proto fuzzer: No variables to reference."); - m_output << m_variables[_x.varnum() % m_variables.size()]; + yulAssert(m_globalVars.size() > 0, "Proto fuzzer: No global variables to reference."); + m_output << m_globalVars[_x.varnum() % m_globalVars.size()]; } } @@ -258,8 +279,10 @@ void ProtoConverter::visit(VarDecl const& _x) m_output << "let " << varName << " := "; visit(_x.expr()); m_output << "\n"; - m_scopeVars.top().push_back(varName); - m_variables.push_back(varName); + if (m_inFunctionDef) + m_funcVars.back().back().push_back(varName); + else + m_variables.back().push_back(varName); } void ProtoConverter::visit(TypedVarDecl const& _x) @@ -324,8 +347,10 @@ void ProtoConverter::visit(TypedVarDecl const& _x) m_output << " : u256\n"; break; } - m_scopeVars.top().push_back(varName); - m_variables.push_back(varName); + if (m_inFunctionDef) + m_funcVars.back().back().push_back(varName); + else + m_variables.back().push_back(varName); } void ProtoConverter::visit(UnaryOp const& _x) @@ -1102,12 +1127,19 @@ void ProtoConverter::visit(Statement const& _x) } } -void ProtoConverter::openScope(vector const& _funcParams) +void ProtoConverter::openBlockScope() { - m_scopeVars.push({}); - m_scopeFuncs.push({}); - if (!_funcParams.empty()) - addVarsToScope(_funcParams); + m_scopeFuncs.push_back({}); + // Create new block scope inside current function scope + if (m_inFunctionDef) + m_funcVars.back().push_back(vector{}); + else + m_variables.push_back(vector{}); +} + +void ProtoConverter::openFunctionScope(vector const& _funcParams) +{ + m_funcVars.push_back(vector>({_funcParams})); } void ProtoConverter::updateFunctionMaps(string const& _var) @@ -1124,21 +1156,9 @@ void ProtoConverter::updateFunctionMaps(string const& _var) yulAssert(erased == 2, "Proto fuzzer: Function maps not updated"); } -void ProtoConverter::closeScope() +void ProtoConverter::closeBlockScope() { - for (auto const& var: m_scopeVars.top()) - { - unsigned numVarsRemoved = m_variables.size(); - m_variables.erase(remove(m_variables.begin(), m_variables.end(), var), m_variables.end()); - numVarsRemoved -= m_variables.size(); - yulAssert( - numVarsRemoved == 1, - "Proto fuzzer: Nothing or too much went out of scope" - ); - } - m_scopeVars.pop(); - - for (auto const& f: m_scopeFuncs.top()) + for (auto const& f: m_scopeFuncs.back()) { unsigned numFuncsRemoved = m_functions.size(); m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end()); @@ -1149,21 +1169,40 @@ void ProtoConverter::closeScope() ); updateFunctionMaps(f); } - m_scopeFuncs.pop(); + if (!m_scopeFuncs.empty()) + m_scopeFuncs.pop_back(); + if (!m_inFunctionDef) + { + if (!m_variables.empty()) + m_variables.pop_back(); + } + else + { + // Variables that have been declared in a + // function block, go out of scope + if (!m_funcVars.empty()) + if (!m_funcVars.back().empty()) + m_funcVars.back().pop_back(); + } +} + +void ProtoConverter::closeFunctionScope() +{ + if (!m_funcVars.empty()) + m_funcVars.pop_back(); } void ProtoConverter::addVarsToScope(vector const& _vars) { - for (string const& i: _vars) - { - m_variables.push_back(i); - m_scopeVars.top().push_back(i); - } + if (m_inFunctionDef) + m_funcVars.back().back().insert(m_funcVars.back().back().end(), _vars.begin(), _vars.end()); + else + m_variables.back().insert(m_variables.back().end(), _vars.begin(), _vars.end()); } -void ProtoConverter::visit(Block const& _x, vector _funcParams) +void ProtoConverter::visit(Block const& _x) { - openScope(_funcParams); + openBlockScope(); // Register function declarations in this scope unless this // scope belongs to for-init (in which function declarations @@ -1181,7 +1220,7 @@ void ProtoConverter::visit(Block const& _x, vector _funcParams) } else m_output << "{}\n"; - closeScope(); + closeBlockScope(); } vector ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx) @@ -1224,7 +1263,7 @@ void ProtoConverter::registerFunction(FunctionDef const* _x) auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams))); yulAssert(ret.second, "Proto fuzzer: Function already exists."); m_functions.push_back(funcName); - m_scopeFuncs.top().push_back(funcName); + m_scopeFuncs.back().push_back(funcName); m_functionDefMap.emplace(make_pair(_x, funcName)); } @@ -1363,8 +1402,12 @@ void ProtoConverter::createFunctionDefAndCall( bool wasInFunctionDef = m_inFunctionDef; m_inFunctionDef = true; - // Body - visit(_x.block(), varsVec); + // Create new function scope and add function input and return + // parameters to it. + openFunctionScope(varsVec); + // Visit function body + visit(_x.block()); + closeFunctionScope(); m_inForBodyScope = wasInForBody; m_inFunctionDef = wasInFunctionDef; diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index ef149d198..6823a2925 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -42,6 +42,8 @@ class ProtoConverter public: ProtoConverter() { + m_funcVars = std::vector>>{}; + m_variables = std::vector>{}; m_inForBodyScope = false; m_inForInitScope = false; m_numNestedForLoops = 0; @@ -62,7 +64,7 @@ private: /// @param _block Reference to a basic block of yul statements. /// @param _funcParams List of function parameter names, defaults to /// an empty vector. - void visit(Block const& _block, std::vector _funcParams = {}); + void visit(Block const& _block); std::string visit(Literal const&); void visit(VarRef const&); @@ -98,11 +100,15 @@ private: void visit(Code const&); void visit(Program const&); - /// Creates a new scope, and adds @a _funcParams to it if it + /// Creates a new block scope. + void openBlockScope(); + /// Creates a new function scope, and adds @a _funcParams to it if it /// is non-empty. - void openScope(std::vector const& _funcParams); - /// Closes current scope - void closeScope(); + void openFunctionScope(std::vector const& _funcParams); + /// Closes current block scope + void closeBlockScope(); + /// Closes current function scope + void closeFunctionScope(); /// Adds @a _vars to current scope void addVarsToScope(std::vector const& _vars); @@ -138,6 +144,14 @@ private: /// Multiple -> "m" std::string functionTypeToString(NumFunctionReturns _type); + /// Builds a single vector containing variables declared in + /// function scope. + void consolidateVarDeclsInFunctionDef(); + + /// Builds a single vector containing variables declared in + /// global scope. + void consolidateGlobalVarDecls(); + /// Return true if at least one variable declaration is in scope, /// false otherwise. /// @return True in the following cases: @@ -295,12 +309,16 @@ private: } std::ostringstream m_output; - /// Variables in current scope - std::stack> m_scopeVars; + /// Variables in all function definitions + std::vector>> m_funcVars; + /// Variables in current function definition + std::vector m_currentFuncVars; + /// Variables in global scope + std::vector m_globalVars; /// Functions in current scope - std::stack> m_scopeFuncs; + std::vector> m_scopeFuncs; /// Variables - std::vector m_variables; + std::vector> m_variables; /// Functions std::vector m_functions; /// Maps FunctionDef object to its name diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 57441e942..155973e1f 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -55,8 +55,7 @@ void printErrors(ostream& _stream, ErrorList const& _errors) DEFINE_PROTO_FUZZER(Program const& _input) { - ProtoConverter converter; - string yul_source = converter.programToString(_input); + string yul_source = ProtoConverter().programToString(_input); if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) { @@ -82,7 +81,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) !stack.parserResult()->analysisInfo) { printErrors(std::cout, stack.errors()); - return; + yulAssert(false, "Proto fuzzer generated malformed program"); } } catch (Exception const&) From ea9e849ee4f23ac769456de7ab6559abbe6399b7 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 18 Nov 2019 12:12:30 +0100 Subject: [PATCH 061/130] Use pointer to string for efficiency --- test/tools/ossfuzz/protoToYul.cpp | 34 +++++++++++--------- test/tools/ossfuzz/protoToYul.h | 8 ++--- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 23 +++++-------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 95a42f49f..f79d1f4da 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -93,16 +93,18 @@ string ProtoConverter::visit(Literal const& _x) void ProtoConverter::consolidateVarDeclsInFunctionDef() { m_currentFuncVars.clear(); - auto &scopes = m_funcVars.back(); - for (auto &s: scopes) - m_currentFuncVars.insert(m_currentFuncVars.end(), s.begin(), s.end()); + auto const& scopes = m_funcVars.back(); + for (auto const& s: scopes) + for (auto const& var: s) + m_currentFuncVars.push_back(&var); } void ProtoConverter::consolidateGlobalVarDecls() { - m_globalVars.clear(); - for (auto &scope: m_variables) - m_globalVars.insert(m_globalVars.end(), scope.begin(), scope.end()); + m_currentGlobalVars.clear(); + for (auto const& scope: m_globalVars) + for (auto const& var: scope) + m_currentGlobalVars.push_back(&var); } bool ProtoConverter::varDeclAvailable() @@ -115,7 +117,7 @@ bool ProtoConverter::varDeclAvailable() else { consolidateGlobalVarDecls(); - return m_globalVars.size() > 0; + return m_currentGlobalVars.size() > 0; } } @@ -131,13 +133,13 @@ void ProtoConverter::visit(VarRef const& _x) { // Ensure that there is at least one variable declaration to reference in function scope. yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference."); - m_output << m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()]; + m_output << *m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()]; } else { // Ensure that there is at least one variable declaration to reference in nested scopes. - yulAssert(m_globalVars.size() > 0, "Proto fuzzer: No global variables to reference."); - m_output << m_globalVars[_x.varnum() % m_globalVars.size()]; + yulAssert(m_currentGlobalVars.size() > 0, "Proto fuzzer: No global variables to reference."); + m_output << *m_currentGlobalVars[_x.varnum() % m_currentGlobalVars.size()]; } } @@ -282,7 +284,7 @@ void ProtoConverter::visit(VarDecl const& _x) if (m_inFunctionDef) m_funcVars.back().back().push_back(varName); else - m_variables.back().push_back(varName); + m_globalVars.back().push_back(varName); } void ProtoConverter::visit(TypedVarDecl const& _x) @@ -350,7 +352,7 @@ void ProtoConverter::visit(TypedVarDecl const& _x) if (m_inFunctionDef) m_funcVars.back().back().push_back(varName); else - m_variables.back().push_back(varName); + m_globalVars.back().push_back(varName); } void ProtoConverter::visit(UnaryOp const& _x) @@ -1134,7 +1136,7 @@ void ProtoConverter::openBlockScope() if (m_inFunctionDef) m_funcVars.back().push_back(vector{}); else - m_variables.push_back(vector{}); + m_globalVars.push_back(vector{}); } void ProtoConverter::openFunctionScope(vector const& _funcParams) @@ -1173,8 +1175,8 @@ void ProtoConverter::closeBlockScope() m_scopeFuncs.pop_back(); if (!m_inFunctionDef) { - if (!m_variables.empty()) - m_variables.pop_back(); + if (!m_globalVars.empty()) + m_globalVars.pop_back(); } else { @@ -1197,7 +1199,7 @@ void ProtoConverter::addVarsToScope(vector const& _vars) if (m_inFunctionDef) m_funcVars.back().back().insert(m_funcVars.back().back().end(), _vars.begin(), _vars.end()); else - m_variables.back().insert(m_variables.back().end(), _vars.begin(), _vars.end()); + m_globalVars.back().insert(m_globalVars.back().end(), _vars.begin(), _vars.end()); } void ProtoConverter::visit(Block const& _x) diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index 6823a2925..a0a7240ab 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -43,7 +43,7 @@ public: ProtoConverter() { m_funcVars = std::vector>>{}; - m_variables = std::vector>{}; + m_globalVars = std::vector>{}; m_inForBodyScope = false; m_inForInitScope = false; m_numNestedForLoops = 0; @@ -312,13 +312,13 @@ private: /// Variables in all function definitions std::vector>> m_funcVars; /// Variables in current function definition - std::vector m_currentFuncVars; + std::vector m_currentFuncVars; /// Variables in global scope - std::vector m_globalVars; + std::vector m_currentGlobalVars; /// Functions in current scope std::vector> m_scopeFuncs; /// Variables - std::vector> m_variables; + std::vector> m_globalVars; /// Functions std::vector m_functions; /// Maps FunctionDef object to its name diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 155973e1f..689085240 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -69,24 +69,17 @@ DEFINE_PROTO_FUZZER(Program const& _input) // AssemblyStack entry point AssemblyStack stack( - langutil::EVMVersion(langutil::EVMVersion::istanbul()), + langutil::EVMVersion::istanbul(), AssemblyStack::Language::StrictAssembly, dev::solidity::OptimiserSettings::full() ); - try + // Parse protobuf mutated YUL code + if (!stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code || + !stack.parserResult()->analysisInfo) { - // Parse protobuf mutated YUL code - if (!stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code || - !stack.parserResult()->analysisInfo) - { - printErrors(std::cout, stack.errors()); - yulAssert(false, "Proto fuzzer generated malformed program"); - } - } - catch (Exception const&) - { - return; + printErrors(std::cout, stack.errors()); + yulAssert(false, "Proto fuzzer generated malformed program"); } ostringstream os1; @@ -94,7 +87,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( os1, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())) + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::istanbul()) ); if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) @@ -104,7 +97,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())), + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::istanbul()), (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) ); From 8fd95484fe3072a3c0b77f319019fb988b3ab714 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 26 Nov 2019 17:32:45 +0100 Subject: [PATCH 062/130] Add comprehensive tests to fromHex/toHex --- test/libdevcore/CommonData.cpp | 62 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/test/libdevcore/CommonData.cpp b/test/libdevcore/CommonData.cpp index 8da937de8..18f7be6f8 100644 --- a/test/libdevcore/CommonData.cpp +++ b/test/libdevcore/CommonData.cpp @@ -18,7 +18,7 @@ * Unit tests for the StringUtils routines. */ - +#include #include #include #include // for IntegerType @@ -28,6 +28,9 @@ using namespace std; using namespace dev::solidity; +// TODO: Fix Boost... +BOOST_TEST_DONT_PRINT_LOG_VALUE(dev::bytes); + namespace dev { namespace test @@ -35,9 +38,62 @@ namespace test BOOST_AUTO_TEST_SUITE(CommonData) -BOOST_AUTO_TEST_CASE(test_to_hex) +BOOST_AUTO_TEST_CASE(fromhex_char) { - BOOST_CHECK_EQUAL(toHex(fromHex("FF"), HexPrefix::DontAdd, HexCase::Lower), "ff"); + BOOST_CHECK_EQUAL(fromHex('0', WhenError::DontThrow), 0x0); + BOOST_CHECK_EQUAL(fromHex('a', WhenError::DontThrow), 0xa); + BOOST_CHECK_EQUAL(fromHex('x', WhenError::DontThrow), -1); + BOOST_CHECK_EQUAL(fromHex('x', static_cast(42)), -1); + + BOOST_CHECK_EQUAL(fromHex('0', WhenError::Throw), 0x0); + BOOST_CHECK_EQUAL(fromHex('a', WhenError::Throw), 0xa); + BOOST_CHECK_THROW(fromHex('x', WhenError::Throw), BadHexCharacter); +} + +BOOST_AUTO_TEST_CASE(fromhex_string) +{ + bytes expectation_even = {{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}; + bytes expectation_odd = {{0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0xab, 0xbc, 0xcd, 0xde, 0xef, 0xf0}}; + + // Defaults to WhenError::DontThrow + BOOST_CHECK_EQUAL(fromHex(""), bytes()); + BOOST_CHECK_EQUAL(fromHex("00112233445566778899aabbccddeeff"), expectation_even); + BOOST_CHECK_EQUAL(fromHex("0x00112233445566778899aabbccddeeff"), expectation_even); + BOOST_CHECK_EQUAL(fromHex("0x00112233445566778899aabbccddeeff0"), expectation_odd); + BOOST_CHECK_EQUAL(fromHex("gg"), bytes()); + BOOST_CHECK_EQUAL(fromHex("0xgg"), bytes()); + + BOOST_CHECK_EQUAL(fromHex("", WhenError::Throw), bytes()); + BOOST_CHECK_EQUAL(fromHex("00112233445566778899aabbccddeeff", WhenError::Throw), expectation_even); + BOOST_CHECK_EQUAL(fromHex("0x00112233445566778899aabbccddeeff", WhenError::Throw), expectation_even); + BOOST_CHECK_EQUAL(fromHex("0x00112233445566778899aabbccddeeff0", WhenError::Throw), expectation_odd); + BOOST_CHECK_THROW(fromHex("gg", WhenError::Throw), BadHexCharacter); + BOOST_CHECK_THROW(fromHex("0xgg", WhenError::Throw), BadHexCharacter); +} + +BOOST_AUTO_TEST_CASE(tohex_uint8) +{ + BOOST_CHECK_EQUAL(toHex(0xaa), "aa"); + BOOST_CHECK_EQUAL(toHex(0xaa, HexCase::Lower), "aa"); + BOOST_CHECK_EQUAL(toHex(0xaa, HexCase::Upper), "AA"); + BOOST_CHECK_THROW(toHex(0xaa, HexCase::Mixed), BadHexCase); + // Defaults to lower case on invalid setting. + BOOST_CHECK_EQUAL(toHex(0xaa, static_cast(42)), "aa"); +} + +BOOST_AUTO_TEST_CASE(tohex_bytes) +{ + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::DontAdd, HexCase::Lower), "00112233445566778899aabbccddeeff"); + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::DontAdd, HexCase::Upper), "00112233445566778899AABBCCDDEEFF"); + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::DontAdd, HexCase::Mixed), "00112233445566778899aabbCCDDeeff"); + // Defaults to lower case on invalid setting. + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::DontAdd, static_cast(42)), "00112233445566778899aabbccddeeff"); + + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::Add, HexCase::Lower), "0x00112233445566778899aabbccddeeff"); + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::Add, HexCase::Upper), "0x00112233445566778899AABBCCDDEEFF"); + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899AaBbCcDdEeFf"), HexPrefix::Add, HexCase::Mixed), "0x00112233445566778899aabbCCDDeeff"); + // Defaults to lower case on invalid setting. + BOOST_CHECK_EQUAL(toHex(fromHex("00112233445566778899aAbBcCdDeEfF"), HexPrefix::Add, static_cast(42)), "0x00112233445566778899aabbccddeeff"); } BOOST_AUTO_TEST_CASE(test_format_number) From e8b37c3bb5b7c650334910679754038d20aade98 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 22 Nov 2019 11:12:25 +0000 Subject: [PATCH 063/130] Use EVMC literals in EVMCHost --- test/EVMHost.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 64ef1c5e5..3fff5754c 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -33,7 +33,7 @@ using namespace std; using namespace dev; using namespace dev::test; - +using namespace evmc::literals; evmc::VM* EVMHost::getVM(string const& _path) { @@ -118,21 +118,21 @@ void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _ben evmc::result EVMHost::call(evmc_message const& _message) noexcept { - if (_message.destination == convertToEVMC(Address(1))) + if (_message.destination == 0x0000000000000000000000000000000000000001_address) return precompileECRecover(_message); - else if (_message.destination == convertToEVMC(Address(2))) + else if (_message.destination == 0x0000000000000000000000000000000000000002_address) return precompileSha256(_message); - else if (_message.destination == convertToEVMC(Address(3))) + else if (_message.destination == 0x0000000000000000000000000000000000000003_address) return precompileRipeMD160(_message); - else if (_message.destination == convertToEVMC(Address(4))) + else if (_message.destination == 0x0000000000000000000000000000000000000004_address) return precompileIdentity(_message); - else if (_message.destination == convertToEVMC(Address(5))) + else if (_message.destination == 0x0000000000000000000000000000000000000005_address) return precompileModExp(_message); - else if (_message.destination == convertToEVMC(Address(6))) + else if (_message.destination == 0x0000000000000000000000000000000000000006_address) return precompileALTBN128G1Add(_message); - else if (_message.destination == convertToEVMC(Address(7))) + else if (_message.destination == 0x0000000000000000000000000000000000000007_address) return precompileALTBN128G1Mul(_message); - else if (_message.destination == convertToEVMC(Address(8))) + else if (_message.destination == 0x0000000000000000000000000000000000000008_address) return precompileALTBN128PairingProduct(_message); State stateBackup = m_state; @@ -224,10 +224,11 @@ evmc_tx_context EVMHost::get_tx_context() noexcept ctx.block_timestamp = m_state.timestamp; ctx.block_number = m_state.blockNumber; ctx.block_coinbase = m_coinbase; + // TODO: support short literals in EVMC and use them here ctx.block_difficulty = convertToEVMC(u256("200000000")); ctx.block_gas_limit = 20000000; ctx.tx_gas_price = convertToEVMC(u256("3000000000")); - ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292")); + ctx.tx_origin = 0x9292929292929292929292929292929292929292_address; // Mainnet according to EIP-155 ctx.chain_id = convertToEVMC(u256(1)); return ctx; From 9e9b643b935d5e02940b2161c67bd47e66de4918 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 11:51:06 +0100 Subject: [PATCH 064/130] Tune the suite. --- libyul/optimiser/Suite.cpp | 12 +++++++++++ ...remove_redundant_assignments_in_switch.yul | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index e35146e93..c7177e655 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -184,6 +184,18 @@ void OptimiserSuite::run( }, ast); } + { + // Prune a bit more in SSA + suite.runSequence({ + ExpressionSplitter::name, + SSATransform::name, + RedundantAssignEliminator::name, + UnusedPruner::name, + RedundantAssignEliminator::name, + UnusedPruner::name, + }, ast); + } + { // Turn into SSA again and simplify suite.runSequence({ diff --git a/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul new file mode 100644 index 000000000..fa1364297 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul @@ -0,0 +1,20 @@ + { + let x := 0 + switch mload(x) + case 0 { x := x } + case 1 { x := 1 } + default { invalid() } + mstore(1, 1) +} +// ==== +// step: fullSuite +// ---- +// { +// { +// switch mload(0) +// case 0 { } +// case 1 { } +// default { invalid() } +// mstore(1, 1) +// } +// } From 0bb88dabb7ec5da5dc6e56c68c1c08aafbd76b28 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 17:24:21 +0100 Subject: [PATCH 065/130] Restrict usage of plain "new". --- libevmasm/PathGasMeter.cpp | 4 ++-- libsolidity/analysis/ControlFlowBuilder.cpp | 2 +- libsolidity/analysis/NameAndTypeResolver.cpp | 8 ++++---- libsolidity/ast/Types.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.h | 2 +- libsolidity/interface/CompilerStack.cpp | 14 +++++++------- libsolidity/parsing/Parser.cpp | 4 ++-- libyul/AsmParser.cpp | 2 +- solc/CommandLineInterface.cpp | 2 +- test/boostTest.cpp | 2 +- test/contracts/AuctionRegistrar.cpp | 2 +- test/contracts/FixedFeeRegistrar.cpp | 2 +- test/contracts/Wallet.cpp | 2 +- test/libsolidity/ABIJsonTest.h | 2 +- test/libsolidity/ASTJSONTest.h | 2 +- test/libyul/EWasmTranslationTest.h | 2 +- test/libyul/FunctionSideEffects.h | 2 +- test/libyul/ObjectCompilerTest.h | 2 +- test/libyul/YulInterpreterTest.h | 2 +- test/libyul/YulOptimizerTest.cpp | 6 +++++- test/libyul/YulOptimizerTest.h | 2 +- 22 files changed, 37 insertions(+), 33 deletions(-) diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp index 761defdc5..9d14ac947 100644 --- a/libevmasm/PathGasMeter.cpp +++ b/libevmasm/PathGasMeter.cpp @@ -40,7 +40,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax( shared_ptr const& _state ) { - auto path = unique_ptr(new GasPath()); + auto path = make_unique(); path->index = _startIndex; path->state = _state->copy(); queue(move(path)); @@ -120,7 +120,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem() for (u256 const& tag: jumpTags) { - auto newPath = unique_ptr(new GasPath()); + auto newPath = make_unique(); newPath->index = m_items.size(); if (m_tagPositions.count(tag)) newPath->index = m_tagPositions.at(tag); diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 6bafc4caa..37d8c824f 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -35,7 +35,7 @@ unique_ptr ControlFlowBuilder::createFunctionFlow( FunctionDefinition const& _function ) { - auto functionFlow = unique_ptr(new FunctionFlow()); + auto functionFlow = make_unique(); functionFlow->entry = _nodeContainer.newNode(); functionFlow->exit = _nodeContainer.newNode(); functionFlow->revert = _nodeContainer.newNode(); diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index cc28220ca..008e37bdb 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -48,7 +48,7 @@ NameAndTypeResolver::NameAndTypeResolver( m_globalContext(_globalContext) { if (!m_scopes[nullptr]) - m_scopes[nullptr].reset(new DeclarationContainer()); + m_scopes[nullptr] = make_shared(); for (Declaration const* declaration: _globalContext.declarations()) { solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration."); @@ -545,7 +545,7 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit) { if (!m_scopes[&_sourceUnit]) // By importing, it is possible that the container already exists. - m_scopes[&_sourceUnit].reset(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); + m_scopes[&_sourceUnit] = make_shared(m_currentScope, m_scopes[m_currentScope].get()); m_currentScope = &_sourceUnit; return true; } @@ -561,7 +561,7 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import) SourceUnit const* importee = _import.annotation().sourceUnit; solAssert(!!importee, ""); if (!m_scopes[importee]) - m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); + m_scopes[importee] = make_shared(nullptr, m_scopes[nullptr].get()); m_scopes[&_import] = m_scopes[importee]; registerDeclaration(_import, false); return true; @@ -705,7 +705,7 @@ void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { map>::iterator iter; bool newlyAdded; - shared_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); + shared_ptr container{make_shared(m_currentScope, m_scopes[m_currentScope].get())}; tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); solAssert(newlyAdded, "Unable to add new scope."); m_currentScope = &_subScope; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index ab0d492d4..d1c579d2b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -329,7 +329,7 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const MemberList::MemberMap members = nativeMembers(_currentScope); if (_currentScope) members += boundFunctions(*this, *_currentScope); - m_members[_currentScope] = unique_ptr(new MemberList(move(members))); + m_members[_currentScope] = make_unique(move(members)); } return *m_members[_currentScope]; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 62ae2dd63..9a7b45412 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -345,7 +345,7 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) if (_tuple.components().size() == 1) m_currentLValue = move(lvalues[0]); else - m_currentLValue.reset(new TupleObject(m_context, move(lvalues))); + m_currentLValue = make_unique(m_context, move(lvalues)); } } return false; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 645d67067..07754dedc 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -136,7 +136,7 @@ template void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments const&... _arguments) { solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one."); - std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...)); + std::unique_ptr<_LValueType> lvalue = std::make_unique<_LValueType>(m_context, _arguments...); if (_expression.annotation().lValueRequested) m_currentLValue = move(lvalue); else diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 423cdcceb..55640b482 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -512,7 +512,7 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const if (!c.sourceMapping) { if (auto items = assemblyItems(_contractName)) - c.sourceMapping.reset(new string(computeSourceMapping(*items))); + c.sourceMapping = make_unique(computeSourceMapping(*items)); } return c.sourceMapping.get(); } @@ -526,7 +526,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c if (!c.runtimeSourceMapping) { if (auto items = runtimeAssemblyItems(_contractName)) - c.runtimeSourceMapping.reset(new string(computeSourceMapping(*items))); + c.runtimeSourceMapping = make_unique(computeSourceMapping(*items)); } return c.runtimeSourceMapping.get(); } @@ -663,7 +663,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const // caches the result if (!_contract.abi) - _contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract))); + _contract.abi = make_unique(ABI::generate(*_contract.contract)); return *_contract.abi; } @@ -685,7 +685,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const // caches the result if (!_contract.storageLayout) - _contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract))); + _contract.storageLayout = make_unique(StorageLayout().generate(*_contract.contract)); return *_contract.storageLayout; } @@ -707,7 +707,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const // caches the result if (!_contract.userDocumentation) - _contract.userDocumentation.reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); + _contract.userDocumentation = make_unique(Natspec::userDocumentation(*_contract.contract)); return *_contract.userDocumentation; } @@ -729,7 +729,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const // caches the result if (!_contract.devDocumentation) - _contract.devDocumentation.reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); + _contract.devDocumentation = make_unique(Natspec::devDocumentation(*_contract.contract)); return *_contract.devDocumentation; } @@ -762,7 +762,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const // cache the result if (!_contract.metadata) - _contract.metadata.reset(new string(createMetadata(_contract))); + _contract.metadata = make_unique(createMetadata(_contract)); return *_contract.metadata; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ff00df90b..07f0ef5f7 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -350,7 +350,7 @@ ASTPointer Parser::parseInheritanceSpecifier() if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); - arguments.reset(new vector>(parseFunctionCallListArguments())); + arguments = make_unique>>(parseFunctionCallListArguments()); nodeFactory.markEndPosition(); expectToken(Token::RParen); } @@ -811,7 +811,7 @@ ASTPointer Parser::parseModifierInvocation() if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); - arguments.reset(new vector>(parseFunctionCallListArguments())); + arguments = make_unique>>(parseFunctionCallListArguments()); nodeFactory.markEndPosition(); expectToken(Token::RParen); } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 75e488290..de98f7d11 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -217,7 +217,7 @@ Statement Parser::parseStatement() expectToken(Token::AssemblyAssign); - assignment.value.reset(new Expression(parseExpression())); + assignment.value = make_unique(parseExpression()); assignment.location.end = locationOf(*assignment.value).end; return Statement{std::move(assignment)}; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index f85b8c3f9..b37507076 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -933,7 +933,7 @@ bool CommandLineInterface::processInput() return link(); } - m_compiler.reset(new CompilerStack(fileReader)); + m_compiler = make_unique(fileReader); unique_ptr formatter; if (m_args.count(g_argNewReporter)) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index fd33f667e..27db38486 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -96,7 +96,7 @@ int registerTests( { static vector> filenames; - filenames.emplace_back(new string(_path.string())); + filenames.emplace_back(make_unique(_path.string())); _suite.add(make_test_case( [config, _testCaseCreator] { diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index b643754e9..183cbdf98 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -223,7 +223,7 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar"))); + s_compiledRegistrar = make_unique(compileContract(registrarCode, "GlobalRegistrar")); sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(m_transactionSuccessful); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index e82f389fc..d26cc94a9 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -132,7 +132,7 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar"))); + s_compiledRegistrar = make_unique(compileContract(registrarCode, "FixedFeeRegistrar")); sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(m_transactionSuccessful); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index d0cbb9956..f9d3b0f0a 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -448,7 +448,7 @@ protected: ) { if (!s_compiledWallet) - s_compiledWallet.reset(new bytes(compileContract(walletCode, "Wallet"))); + s_compiledWallet = make_unique(compileContract(walletCode, "Wallet")); bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); diff --git a/test/libsolidity/ABIJsonTest.h b/test/libsolidity/ABIJsonTest.h index e7be7947f..82a19da3f 100644 --- a/test/libsolidity/ABIJsonTest.h +++ b/test/libsolidity/ABIJsonTest.h @@ -36,7 +36,7 @@ class ABIJsonTest: public TestCase { public: static std::unique_ptr create(Config const& _config) - { return std::unique_ptr(new ABIJsonTest(_config.filename)); } + { return std::make_unique(_config.filename); } ABIJsonTest(std::string const& _filename); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h index dcd385623..cf508e87b 100644 --- a/test/libsolidity/ASTJSONTest.h +++ b/test/libsolidity/ASTJSONTest.h @@ -36,7 +36,7 @@ class ASTJSONTest: public TestCase { public: static std::unique_ptr create(Config const& _config) - { return std::unique_ptr(new ASTJSONTest(_config.filename)); } + { return std::make_unique(_config.filename); } ASTJSONTest(std::string const& _filename); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; diff --git a/test/libyul/EWasmTranslationTest.h b/test/libyul/EWasmTranslationTest.h index 351f83607..c63c305f9 100644 --- a/test/libyul/EWasmTranslationTest.h +++ b/test/libyul/EWasmTranslationTest.h @@ -37,7 +37,7 @@ class EWasmTranslationTest: public dev::solidity::test::EVMVersionRestrictedTest public: static std::unique_ptr create(Config const& _config) { - return std::unique_ptr(new EWasmTranslationTest(_config.filename)); + return std::make_unique(_config.filename); } explicit EWasmTranslationTest(std::string const& _filename); diff --git a/test/libyul/FunctionSideEffects.h b/test/libyul/FunctionSideEffects.h index 8fad2961c..582b45432 100644 --- a/test/libyul/FunctionSideEffects.h +++ b/test/libyul/FunctionSideEffects.h @@ -34,7 +34,7 @@ class FunctionSideEffects: public dev::solidity::test::TestCase { public: static std::unique_ptr create(Config const& _config) - { return std::unique_ptr(new FunctionSideEffects(_config.filename)); } + { return std::make_unique(_config.filename); } explicit FunctionSideEffects(std::string const& _filename); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h index df7fd0905..6e89e059d 100644 --- a/test/libyul/ObjectCompilerTest.h +++ b/test/libyul/ObjectCompilerTest.h @@ -42,7 +42,7 @@ class ObjectCompilerTest: public dev::solidity::test::TestCase public: static std::unique_ptr create(Config const& _config) { - return std::unique_ptr(new ObjectCompilerTest(_config.filename)); + return std::make_unique(_config.filename); } explicit ObjectCompilerTest(std::string const& _filename); diff --git a/test/libyul/YulInterpreterTest.h b/test/libyul/YulInterpreterTest.h index 4263b7336..eb456f1d0 100644 --- a/test/libyul/YulInterpreterTest.h +++ b/test/libyul/YulInterpreterTest.h @@ -42,7 +42,7 @@ class YulInterpreterTest: public dev::solidity::test::EVMVersionRestrictedTestCa public: static std::unique_ptr create(Config const& _config) { - return std::unique_ptr(new YulInterpreterTest(_config.filename)); + return std::make_unique(_config.filename); } explicit YulInterpreterTest(std::string const& _filename); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 07dc8a5c3..76e9af3df 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -424,7 +424,11 @@ void YulOptimizerTest::disambiguate() void YulOptimizerTest::updateContext() { m_nameDispenser = make_unique(*m_dialect, *m_ast, m_reservedIdentifiers); - m_context = unique_ptr(new OptimiserStepContext{*m_dialect, *m_nameDispenser, m_reservedIdentifiers}); + m_context = make_unique(OptimiserStepContext{ + *m_dialect, + *m_nameDispenser, + m_reservedIdentifiers + }); } void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors) diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 87248cfad..a9cc04ab1 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -51,7 +51,7 @@ class YulOptimizerTest: public dev::solidity::test::EVMVersionRestrictedTestCase public: static std::unique_ptr create(Config const& _config) { - return std::unique_ptr(new YulOptimizerTest(_config.filename)); + return std::make_unique(_config.filename); } explicit YulOptimizerTest(std::string const& _filename); From 23181f549eacf3a524ea913091c83dead1e239a4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 18:03:09 +0100 Subject: [PATCH 066/130] Remove plain new operators from AST. --- libsolidity/ast/AST.cpp | 51 +++++++++++++++++++---------------------- libsolidity/ast/AST.h | 4 ++-- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 60703f76e..90df0deb2 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -55,11 +55,6 @@ ASTNode::ASTNode(SourceLocation const& _location): { } -ASTNode::~ASTNode() -{ - delete m_annotation; -} - void ASTNode::resetID() { IDDispenser::reset(); @@ -68,14 +63,14 @@ void ASTNode::resetID() ASTAnnotation& ASTNode::annotation() const { if (!m_annotation) - m_annotation = new ASTAnnotation(); + m_annotation = make_unique(); return *m_annotation; } SourceUnitAnnotation& SourceUnit::annotation() const { if (!m_annotation) - m_annotation = new SourceUnitAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -99,7 +94,7 @@ set SourceUnit::referencedSourceUnits(bool _recurse, set(); return dynamic_cast(*m_annotation); } @@ -168,7 +163,7 @@ vector const& ContractDefinition::interfaceEvents() cons if (!m_interfaceEvents) { set eventsSeen; - m_interfaceEvents.reset(new vector()); + m_interfaceEvents = make_unique>(); for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (EventDefinition const* e: contract->events()) { @@ -193,7 +188,7 @@ vector, FunctionTypePointer>> const& ContractDefinition::inter if (!m_interfaceFunctionList) { set signaturesSeen; - m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); + m_interfaceFunctionList = make_unique, FunctionTypePointer>>>(); for (ContractDefinition const* contract: annotation().linearizedBaseContracts) { vector functions; @@ -225,7 +220,7 @@ vector const& ContractDefinition::inheritableMembers() const { if (!m_inheritableMembers) { - m_inheritableMembers.reset(new vector()); + m_inheritableMembers = make_unique>(); auto addInheritableMember = [&](Declaration const* _decl) { solAssert(_decl, "addInheritableMember got a nullpointer."); @@ -259,14 +254,14 @@ TypePointer ContractDefinition::type() const ContractDefinitionAnnotation& ContractDefinition::annotation() const { if (!m_annotation) - m_annotation = new ContractDefinitionAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } TypeNameAnnotation& TypeName::annotation() const { if (!m_annotation) - m_annotation = new TypeNameAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -278,7 +273,7 @@ TypePointer StructDefinition::type() const TypeDeclarationAnnotation& StructDefinition::annotation() const { if (!m_annotation) - m_annotation = new TypeDeclarationAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -297,7 +292,7 @@ TypePointer EnumDefinition::type() const TypeDeclarationAnnotation& EnumDefinition::annotation() const { if (!m_annotation) - m_annotation = new TypeDeclarationAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -357,7 +352,7 @@ string FunctionDefinition::externalSignature() const FunctionDefinitionAnnotation& FunctionDefinition::annotation() const { if (!m_annotation) - m_annotation = new FunctionDefinitionAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -369,7 +364,7 @@ TypePointer ModifierDefinition::type() const ModifierDefinitionAnnotation& ModifierDefinition::annotation() const { if (!m_annotation) - m_annotation = new ModifierDefinitionAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -389,14 +384,14 @@ FunctionTypePointer EventDefinition::functionType(bool _internal) const EventDefinitionAnnotation& EventDefinition::annotation() const { if (!m_annotation) - m_annotation = new EventDefinitionAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const { if (!m_annotation) - m_annotation = new UserDefinedTypeNameAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } @@ -604,63 +599,63 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const VariableDeclarationAnnotation& VariableDeclaration::annotation() const { if (!m_annotation) - m_annotation = new VariableDeclarationAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } StatementAnnotation& Statement::annotation() const { if (!m_annotation) - m_annotation = new StatementAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } InlineAssemblyAnnotation& InlineAssembly::annotation() const { if (!m_annotation) - m_annotation = new InlineAssemblyAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } ReturnAnnotation& Return::annotation() const { if (!m_annotation) - m_annotation = new ReturnAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } ExpressionAnnotation& Expression::annotation() const { if (!m_annotation) - m_annotation = new ExpressionAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } MemberAccessAnnotation& MemberAccess::annotation() const { if (!m_annotation) - m_annotation = new MemberAccessAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } BinaryOperationAnnotation& BinaryOperation::annotation() const { if (!m_annotation) - m_annotation = new BinaryOperationAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } FunctionCallAnnotation& FunctionCall::annotation() const { if (!m_annotation) - m_annotation = new FunctionCallAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } IdentifierAnnotation& Identifier::annotation() const { if (!m_annotation) - m_annotation = new IdentifierAnnotation(); + m_annotation = make_unique(); return dynamic_cast(*m_annotation); } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index b86b28bc6..dbde1c9f9 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -67,7 +67,7 @@ public: using SourceLocation = langutil::SourceLocation; explicit ASTNode(SourceLocation const& _location); - virtual ~ASTNode(); + virtual ~ASTNode() {} /// @returns an identifier of this AST node that is unique for a single compilation run. size_t id() const { return m_id; } @@ -111,7 +111,7 @@ public: protected: size_t const m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). - mutable ASTAnnotation* m_annotation = nullptr; + mutable std::unique_ptr m_annotation; private: SourceLocation m_location; From 38a20190f42e34f4c0cda7d55703528005412fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 27 Nov 2019 17:37:26 +0100 Subject: [PATCH 067/130] test: Use evmc::VM directly The evmc::VM works as a RAII wrapper similarly to unique_ptr, so there is no point in using additional unique_ptr. --- test/EVMHost.cpp | 19 ++++++++----------- test/EVMHost.h | 6 +++--- test/tools/ossfuzz/abiV2ProtoFuzzer.cpp | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 64ef1c5e5..a925cf325 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -35,22 +35,19 @@ using namespace dev; using namespace dev::test; -evmc::VM* EVMHost::getVM(string const& _path) +evmc::VM& EVMHost::getVM(string const& _path) { - static unique_ptr theVM; + static evmc::VM theVM; if (!theVM && !_path.empty()) { evmc_loader_error_code errorCode = {}; - evmc_vm* vm = evmc_load_and_configure(_path.c_str(), &errorCode); + auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)}; if (vm && errorCode == EVMC_LOADER_SUCCESS) { - if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1)) - theVM = make_unique(vm); + if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1) + theVM = std::move(vm); else - { - evmc_destroy(vm); cerr << "VM loaded does not support EVM1" << endl; - } } else { @@ -60,10 +57,10 @@ evmc::VM* EVMHost::getVM(string const& _path) cerr << endl; } } - return theVM.get(); + return theVM; } -EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): +EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): m_vm(_vm), m_evmVersion(_evmVersion) { @@ -192,7 +189,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept evmc::address currentAddress = m_currentAddress; m_currentAddress = message.destination; - evmc::result result = m_vm->execute(*this, m_evmRevision, message, code.data(), code.size()); + evmc::result result = m_vm.execute(*this, m_evmRevision, message, code.data(), code.size()); m_currentAddress = currentAddress; if (message.kind == EVMC_CREATE) diff --git a/test/EVMHost.h b/test/EVMHost.h index a0adb67bc..15d069d72 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -40,9 +40,9 @@ public: /// Tries to dynamically load libevmone. @returns nullptr on failure. /// The path has to be provided for the first successful run and will be ignored /// afterwards. - static evmc::VM* getVM(std::string const& _path = {}); + static evmc::VM& getVM(std::string const& _path = {}); - explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm = getVM()); + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); struct Account { @@ -179,7 +179,7 @@ private: /// @note The return value is only valid as long as @a _data is alive! static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; - evmc::VM* m_vm = nullptr; + evmc::VM& m_vm; // EVM version requested by the testing tool langutil::EVMVersion m_evmVersion; // EVM version requested from EVMC (matches the above) diff --git a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp index 9d7308299..07e062b8f 100644 --- a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp +++ b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp @@ -142,7 +142,7 @@ DEFINE_PROTO_FUZZER(Contract const& _input) // We target the default EVM which is the latest langutil::EVMVersion version = {}; - EVMHost hostContext(version, &evmone); + EVMHost hostContext(version, evmone); // Deploy contract and signal failure if deploy failed evmc::result createResult = deployContract(hostContext, byteCode); From 240ff30878ededff0537407353538b307675f93d Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 27 Nov 2019 22:34:33 +0100 Subject: [PATCH 068/130] [SMTChecker] Do not visit the name of a modifier invocation --- libsolidity/formal/SMTEncoder.cpp | 13 ++++++++++--- libsolidity/formal/SMTEncoder.h | 1 + .../functions/constructor_base_basic.sol | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 5fd364f8b..b31ff37d2 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -537,6 +537,15 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) } } +bool SMTEncoder::visit(ModifierInvocation const& _node) +{ + if (auto const* args = _node.arguments()) + for (auto const& arg: *args) + if (arg) + arg->accept(*this); + return false; +} + void SMTEncoder::initContract(ContractDefinition const& _contract) { solAssert(m_currentContract == nullptr, ""); @@ -605,9 +614,7 @@ void SMTEncoder::endVisit(Identifier const& _identifier) defineExpr(_identifier, m_context.thisAddress()); m_uninterpretedTerms.insert(&_identifier); } - else if ( - _identifier.annotation().type->category() != Type::Category::Modifier - ) + else createExpr(_identifier); } diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 4f8c5ac8c..a81092f84 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -82,6 +82,7 @@ protected: bool visit(BinaryOperation const& _node) override; void endVisit(BinaryOperation const& _node) override; void endVisit(FunctionCall const& _node) override; + bool visit(ModifierInvocation const& _node) override; void endVisit(Identifier const& _node) override; void endVisit(ElementaryTypeNameExpression const& _node) override; void endVisit(Literal const& _node) override; diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol b/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol new file mode 100644 index 000000000..8e5c57cf6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + constructor() public { + x = 2; + } +} + +contract B is A { + constructor() A() public { + x = 3; + } +} +// ---- +// Warning: (56-90): Assertion checker does not yet support constructors. +// Warning: (113-151): Assertion checker does not yet support constructors. From 3b2d1eb981c7663a45540d3a0d3fb70b2b9f7cff Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 23:14:03 +0100 Subject: [PATCH 069/130] Remove unrelated link. --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 75056dbe6..00b341e82 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -204,7 +204,7 @@ The evaluation order of expressions is not specified (more formally, the order in which the children of one node in the expression tree are evaluated is not specified, but they are of course evaluated before the node itself). It is only guaranteed that statements are executed in order and short-circuiting for -boolean expressions is done. See :ref:`order` for more information. +boolean expressions is done. .. index:: ! assignment From 4edab3d76d72381abeb4ce7089bb02df9ae9fb33 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 27 Nov 2019 23:40:19 +0100 Subject: [PATCH 070/130] Update to jsoncpp 1.9.2 --- cmake/jsoncpp.cmake | 6 +++--- libdevcore/JSON.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index 3e72a8be2..1377041b9 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -37,9 +37,9 @@ endif() ExternalProject_Add(jsoncpp-project PREFIX "${prefix}" DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads" - DOWNLOAD_NAME jsoncpp-1.8.4.tar.gz - URL https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz - URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6 + DOWNLOAD_NAME jsoncpp-1.9.2.tar.gz + URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz + URL_HASH SHA256=77a402fb577b2e0e5d0bdc1cf9c65278915cdb25171e3452c68b6da8a561f8f0 CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp index 0b08616da..80c116bbe 100644 --- a/libdevcore/JSON.cpp +++ b/libdevcore/JSON.cpp @@ -32,8 +32,8 @@ using namespace std; static_assert( - (JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 8) && (JSONCPP_VERSION_PATCH == 4), - "Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.8.4." + (JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 9) && (JSONCPP_VERSION_PATCH == 2), + "Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.9.2." ); namespace dev From 29955a22ad8474f7ee80b7ac4f3093009eacb16d Mon Sep 17 00:00:00 2001 From: William Entriken Date: Wed, 27 Nov 2019 20:13:37 -0500 Subject: [PATCH 071/130] Update markdown --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0af8dd905..e7e5bdb7c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i A "Hello World" program in Solidity is of even less use than in other languages, but still: -``` +```solidity pragma solidity ^0.5.0; contract HelloWorld { From 66ba683da067f5dd9ea928237677b89230d1c21e Mon Sep 17 00:00:00 2001 From: William Entriken Date: Wed, 27 Nov 2019 20:15:30 -0500 Subject: [PATCH 072/130] Fix all markdown language hints --- .circleci/README.md | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index c754b5df0..466aaa76a 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -4,7 +4,7 @@ The docker images are build locally on the developer machine: -```!sh +```sh cd .circleci/docker/ docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Dockerfile.ubuntu1904 . @@ -21,7 +21,7 @@ where the image tag reflects the target OS and revision to build Solidity and ru ### Testing docker images locally -```!sh +```sh cd solidity # Mounts your local solidity directory in docker container for testing docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904- /bin/bash diff --git a/README.md b/README.md index 0af8dd905..e7e5bdb7c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i A "Hello World" program in Solidity is of even less use than in other languages, but still: -``` +```solidity pragma solidity ^0.5.0; contract HelloWorld { From db60d123d0ce672abed0cc73e5634c609009ac93 Mon Sep 17 00:00:00 2001 From: mingchuan Date: Wed, 18 Sep 2019 16:47:49 +0800 Subject: [PATCH 073/130] [YulOpt] Implement loop-invariant code motion --- Changelog.md | 1 + libyul/CMakeLists.txt | 2 + libyul/optimiser/LoopInvariantCodeMotion.cpp | 115 ++++++++++++++++++ libyul/optimiser/LoopInvariantCodeMotion.h | 67 ++++++++++ libyul/optimiser/NameCollector.cpp | 15 +-- libyul/optimiser/NameCollector.h | 13 +- libyul/optimiser/SSAValueTracker.cpp | 10 ++ libyul/optimiser/SSAValueTracker.h | 2 + libyul/optimiser/Suite.cpp | 5 +- scripts/codespell_whitelist.txt | 1 + test/libyul/YulOptimizerTest.cpp | 7 ++ .../fullSuite/abi_example1.yul | 2 +- .../yulOptimizerTests/fullSuite/aztec.yul | 2 +- .../fullSuite/clear_after_if_continue.yul | 2 +- .../fullSuite/loopInvariantCodeMotion.yul | 34 ++++++ .../dependOnVarInLoop.yul | 23 ++++ .../loopInvariantCodeMotion/multi.yul | 26 ++++ .../loopInvariantCodeMotion/non-ssavar.yul | 23 ++++ .../loopInvariantCodeMotion/nonMovable.yul | 21 ++++ .../loopInvariantCodeMotion/recursive.yul | 25 ++++ .../loopInvariantCodeMotion/simple.yul | 21 ++++ test/tools/yulopti.cpp | 6 +- 22 files changed, 408 insertions(+), 15 deletions(-) create mode 100644 libyul/optimiser/LoopInvariantCodeMotion.cpp create mode 100644 libyul/optimiser/LoopInvariantCodeMotion.h create mode 100644 test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul diff --git a/Changelog.md b/Changelog.md index f820c0ad8..054a8283e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Language Features: Compiler Features: * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. + * Yul Optimizer: Perform loop-invariant code motion. Bugfixes: diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index f71171597..da45a2941 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -110,6 +110,8 @@ add_library(yul optimiser/KnowledgeBase.h optimiser/LoadResolver.cpp optimiser/LoadResolver.h + optimiser/LoopInvariantCodeMotion.cpp + optimiser/LoopInvariantCodeMotion.h optimiser/MainFunction.cpp optimiser/MainFunction.h optimiser/Metrics.cpp diff --git a/libyul/optimiser/LoopInvariantCodeMotion.cpp b/libyul/optimiser/LoopInvariantCodeMotion.cpp new file mode 100644 index 000000000..00375a71d --- /dev/null +++ b/libyul/optimiser/LoopInvariantCodeMotion.cpp @@ -0,0 +1,115 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) +{ + map functionSideEffects = + SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); + + set ssaVars = SSAValueTracker::ssaVariables(_ast); + LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects}(_ast); +} + +void LoopInvariantCodeMotion::operator()(Block& _block) +{ + iterateReplacing( + _block.statements, + [&](Statement& _s) -> optional> + { + visit(_s); + if (holds_alternative(_s)) + return rewriteLoop(get(_s)); + else + return {}; + } + ); +} + +bool LoopInvariantCodeMotion::canBePromoted( + VariableDeclaration const& _varDecl, + set const& _varsDefinedInCurrentScope +) const +{ + // A declaration can be promoted iff + // 1. Its LHS is a SSA variable + // 2. Its RHS only references SSA variables declared outside of the current scope + // 3. Its RHS is movable + + for (auto const& var: _varDecl.variables) + if (!m_ssaVariables.count(var.name)) + return false; + if (_varDecl.value) + { + for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) + if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) + return false; + if (!SideEffectsCollector{m_dialect, *_varDecl.value, &m_functionSideEffects}.movable()) + return false; + } + return true; +} + +optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) +{ + assertThrow(_for.pre.statements.empty(), OptimizerException, ""); + vector replacement; + for (Block* block: {&_for.post, &_for.body}) + { + set varsDefinedInScope; + iterateReplacing( + block->statements, + [&](Statement& _s) -> optional> + { + if (holds_alternative(_s)) + { + VariableDeclaration const& varDecl = std::get(_s); + if (canBePromoted(varDecl, varsDefinedInScope)) + { + replacement.emplace_back(std::move(_s)); + // Do not add the variables declared here to varsDefinedInScope because we are moving them. + return vector{}; + } + for (auto const& var: varDecl.variables) + varsDefinedInScope.insert(var.name); + } + return {}; + } + ); + } + if (replacement.empty()) + return {}; + else + { + replacement.emplace_back(std::move(_for)); + return { std::move(replacement) }; + } +} diff --git a/libyul/optimiser/LoopInvariantCodeMotion.h b/libyul/optimiser/LoopInvariantCodeMotion.h new file mode 100644 index 000000000..f5f250515 --- /dev/null +++ b/libyul/optimiser/LoopInvariantCodeMotion.h @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + +#include +#include +#include + +namespace yul +{ + +/** + * Loop-invariant code motion. + * + * This optimization moves movable SSA variable declarations outside the loop. + * + * Only statements at the top level in a loop's body or post block are considered, i.e variable + * declarations inside conditional branches will not be moved out of the loop. + * + * Requirements: + * - The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. + * - Expression splitter and SSA transform should be run upfront to obtain better result. + */ + +class LoopInvariantCodeMotion: public ASTModifier +{ +public: + static constexpr char const* name{"LoopInvariantCodeMotion"}; + static void run(OptimiserStepContext& _context, Block& _ast); + + void operator()(Block& _block) override; + +private: + explicit LoopInvariantCodeMotion( + Dialect const& _dialect, + std::set const& _ssaVariables, + std::map const& _functionSideEffects + ): + m_dialect(_dialect), + m_ssaVariables(_ssaVariables), + m_functionSideEffects(_functionSideEffects) + { } + + /// @returns true if the given variable declaration can be moved to in front of the loop. + bool canBePromoted(VariableDeclaration const& _varDecl, std::set const& _varsDefinedInCurrentScope) const; + std::optional> rewriteLoop(ForLoop& _for); + + Dialect const& m_dialect; + std::set const& m_ssaVariables; + std::map const& m_functionSideEffects; +}; + +} diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 04631a86a..dab2f290a 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -49,27 +49,28 @@ void ReferencesCounter::operator()(Identifier const& _identifier) void ReferencesCounter::operator()(FunctionCall const& _funCall) { - ++m_references[_funCall.functionName.name]; + if (m_countWhat == VariablesAndFunctions) + ++m_references[_funCall.functionName.name]; ASTWalker::operator()(_funCall); } -map ReferencesCounter::countReferences(Block const& _block) +map ReferencesCounter::countReferences(Block const& _block, CountWhat _countWhat) { - ReferencesCounter counter; + ReferencesCounter counter(_countWhat); counter(_block); return counter.references(); } -map ReferencesCounter::countReferences(FunctionDefinition const& _function) +map ReferencesCounter::countReferences(FunctionDefinition const& _function, CountWhat _countWhat) { - ReferencesCounter counter; + ReferencesCounter counter(_countWhat); counter(_function); return counter.references(); } -map ReferencesCounter::countReferences(Expression const& _expression) +map ReferencesCounter::countReferences(Expression const& _expression, CountWhat _countWhat) { - ReferencesCounter counter; + ReferencesCounter counter(_countWhat); counter.visit(_expression); return counter.references(); } diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index b6b4e1e6c..46debdcba 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -54,16 +54,23 @@ private: class ReferencesCounter: public ASTWalker { public: + enum CountWhat { VariablesAndFunctions, OnlyVariables }; + + explicit ReferencesCounter(CountWhat _countWhat = VariablesAndFunctions): + m_countWhat(_countWhat) + {} + using ASTWalker::operator (); virtual void operator()(Identifier const& _identifier); virtual void operator()(FunctionCall const& _funCall); - static std::map countReferences(Block const& _block); - static std::map countReferences(FunctionDefinition const& _function); - static std::map countReferences(Expression const& _expression); + static std::map countReferences(Block const& _block, CountWhat _countWhat = VariablesAndFunctions); + static std::map countReferences(FunctionDefinition const& _function, CountWhat _countWhat = VariablesAndFunctions); + static std::map countReferences(Expression const& _expression, CountWhat _countWhat = VariablesAndFunctions); std::map const& references() const { return m_references; } private: + CountWhat m_countWhat = CountWhat::VariablesAndFunctions; std::map m_references; }; diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index d4feacbd9..3b599644c 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -49,6 +49,16 @@ void SSAValueTracker::operator()(VariableDeclaration const& _varDecl) setValue(_varDecl.variables.front().name, _varDecl.value.get()); } +set SSAValueTracker::ssaVariables(Block const& _ast) +{ + SSAValueTracker t; + t(_ast); + set ssaVars; + for (auto const& value: t.values()) + ssaVars.insert(value.first); + return ssaVars; +} + void SSAValueTracker::setValue(YulString _name, Expression const* _value) { assertThrow( diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index 1062ca8e7..7eac6b6a4 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -49,6 +49,8 @@ public: std::map const& values() const { return m_values; } Expression const* value(YulString _name) const { return m_values.at(_name); } + static std::set ssaVariables(Block const& _ast); + private: void setValue(YulString _name, Expression const* _value); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index e35146e93..054aaf30c 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,8 @@ void OptimiserSuite::run( RedundantAssignEliminator::name, ExpressionSimplifier::name, CommonSubexpressionEliminator::name, - LoadResolver::name + LoadResolver::name, + LoopInvariantCodeMotion::name }, ast); } @@ -345,6 +347,7 @@ map> const& OptimiserSuite::allSteps() FunctionHoister, LiteralRematerialiser, LoadResolver, + LoopInvariantCodeMotion, RedundantAssignEliminator, Rematerialiser, SSAReverser, diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt index bbfe3e05c..0409dc1a2 100644 --- a/scripts/codespell_whitelist.txt +++ b/scripts/codespell_whitelist.txt @@ -10,3 +10,4 @@ fo compilability errorstring hist +otion diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 07dc8a5c3..39ab5d0fb 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -279,6 +280,12 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line ExpressionJoiner::run(*m_context, *m_ast); ExpressionJoiner::run(*m_context, *m_ast); } + else if (m_optimizerStep == "loopInvariantCodeMotion") + { + disambiguate(); + ForLoopInitRewriter::run(*m_context, *m_ast); + LoopInvariantCodeMotion::run(*m_context, *m_ast); + } else if (m_optimizerStep == "controlFlowSimplifier") { disambiguate(); diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 4838eab02..0d37cf1b9 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -483,7 +483,7 @@ // let _5 := 0xffffffffffffffff // if gt(offset, _5) { revert(_1, _1) } // let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_4, offset), _3) -// let offset_1 := calldataload(add(_4, 96)) +// let offset_1 := calldataload(add(_4, 0x60)) // if gt(offset_1, _5) { revert(_1, _1) } // let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3) // sstore(calldataload(_4), calldataload(add(_4, 0x20))) diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 0b82de7e9..35e1bb7b3 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -311,7 +311,7 @@ // } // b := add(b, _5) // } -// if lt(m, n) { validatePairing(0x64) } +// if lt(m, n) { validatePairing(100) } // if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _2), challenge)) // { // mstore(0, 404) diff --git a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul index 19816b445..d958a318f 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul @@ -12,7 +12,7 @@ // { // { // let y := mload(0x20) -// for { } and(y, 8) { if y { revert(0, 0) } } +// for { } iszero(iszero(and(y, 8))) { if y { revert(0, 0) } } // { // if y { continue } // sstore(1, 0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul new file mode 100644 index 000000000..f570d2ecf --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul @@ -0,0 +1,34 @@ +{ + sstore(0, array_sum(calldataload(0))) + + function array_sum(x) -> sum { + let length := calldataload(x) + for { let i := 0 } lt(i, length) { i := add(i, 1) } { + sum := add(sum, array_load(x, i)) + } + } + function array_load(x, i) -> v { + let len := calldataload(x) + if iszero(lt(i, len)) { revert(0, 0) } + let data := add(x, 0x20) + v := calldataload(add(data, mul(i, 0x20))) + // this is just to have some additional code that + // can be moved out of the loop. + v := add(v, calldataload(7)) + } +} +// ==== +// step: fullSuite +// ---- +// { +// { +// let _1 := calldataload(0) +// let sum := 0 +// let i := sum +// for { } lt(i, calldataload(_1)) { i := add(i, 1) } +// { +// sum := add(sum, add(calldataload(add(add(_1, mul(i, 0x20)), 0x20)), calldataload(7))) +// } +// sstore(0, sum) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul new file mode 100644 index 000000000..82ea2bf78 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let c := mload(3) // c cannot be moved because non-movable + let not_inv := add(b, c) // no_inv cannot be moved because its value depends on c + a := add(a, 1) + mstore(a, not_inv) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let c := mload(3) +// let not_inv := add(b, c) +// a := add(a, 1) +// mstore(a, not_inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul new file mode 100644 index 000000000..c7e39cc70 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul @@ -0,0 +1,26 @@ +{ + let b := 1 + // tests if c, d, and inv can be moved outside in single pass + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let c := b + let d := mul(c, 2) + let inv := add(c, d) + a := add(a, 1) + mstore(a, inv) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 1 +// let a := 1 +// let c := b +// let d := mul(c, 2) +// let inv := add(c, d) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(a, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul new file mode 100644 index 000000000..86cf1274e --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let not_inv := add(b, 42) + not_inv := add(not_inv, 1) + a := add(a, 1) + mstore(a, not_inv) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let not_inv := add(b, 42) +// not_inv := add(not_inv, 1) +// a := add(a, 1) +// mstore(a, not_inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul new file mode 100644 index 000000000..787c1756b --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul @@ -0,0 +1,21 @@ +{ + let b := 0 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := mload(b) + a := add(a, 1) + mstore(a, inv) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 0 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let inv := mload(b) +// a := add(a, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul new file mode 100644 index 000000000..a489b134b --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul @@ -0,0 +1,25 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + for { let a2 := 1 } iszero(eq(a2, 10)) { a2 := add(a2, 1) } { + let inv := add(b, 42) + mstore(a, inv) + } + a := add(a, 1) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let a2 := 1 +// for { } iszero(eq(a2, 10)) { a2 := add(a2, 1) } +// { mstore(a, inv) } +// a := add(a, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul new file mode 100644 index 000000000..970fec59f --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul @@ -0,0 +1,21 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + a := add(a, 1) + mstore(a, inv) + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(a, 1) +// mstore(a, inv) +// } +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index e77cbf02b..8534115c5 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include @@ -142,7 +143,7 @@ public: cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl; cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl; cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/" << endl; - cout << " (C)onditional simplifier?" << endl; + cout << " (C)onditional simplifier/loop-invariant code (M)otion?" << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -237,6 +238,9 @@ public: case 'L': LoadResolver::run(context, *m_ast); break; + case 'M': + LoopInvariantCodeMotion::run(context, *m_ast); + break; default: cout << "Unknown option." << endl; } From 460861922eed56d0d63ae79d9d214d0eb4ea8e64 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 28 Nov 2019 14:33:38 +0100 Subject: [PATCH 074/130] Change string formatting check to assertion. --- libdevcore/CommonData.cpp | 13 ++++++------- libdevcore/CommonData.h | 1 + libdevcore/Exceptions.h | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 1299939f5..b151ba604 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -179,14 +179,13 @@ bool dev::isValidDecimal(string const& _string) return true; } -// Returns a quoted string if all characters are printable ASCII chars, -// or its hex representation otherwise. -std::string dev::formatAsStringOrNumber(std::string const& _value) +string dev::formatAsStringOrNumber(string const& _value) { - if (_value.length() <= 32) - for (auto const& c: _value) - if (c <= 0x1f || c >= 0x7f || c == '"') - return "0x" + h256(_value, h256::AlignLeft).hex(); + assertThrow(_value.length() <= 32, StringTooLong, "String to be formatted longer than 32 bytes."); + + for (auto const& c: _value) + if (c <= 0x1f || c >= 0x7f || c == '"') + return "0x" + h256(_value, h256::AlignLeft).hex(); return "\"" + _value + "\""; } diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 02b037ce8..9db1e3cf5 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -356,6 +356,7 @@ bool isValidDecimal(std::string const& _string); /// @returns a quoted string if all characters are printable ASCII chars, /// or its hex representation otherwise. +/// _value cannot be longer than 32 bytes. std::string formatAsStringOrNumber(std::string const& _value); template diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index df7a9ba01..851be4ea1 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -49,6 +49,7 @@ DEV_SIMPLE_EXCEPTION(BadHexCharacter); DEV_SIMPLE_EXCEPTION(BadHexCase); DEV_SIMPLE_EXCEPTION(FileError); DEV_SIMPLE_EXCEPTION(DataTooLong); +DEV_SIMPLE_EXCEPTION(StringTooLong); // error information to be added to exceptions using errinfo_comment = boost::error_info; From a352abe00d19cebe606ae9743a801a3af3073887 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 26 Sep 2019 16:12:27 +0200 Subject: [PATCH 075/130] [SMTChecker] Add support to constructors --- Changelog.md | 1 + libsolidity/formal/BMC.cpp | 26 +- libsolidity/formal/CHC.cpp | 184 ++++++++------ libsolidity/formal/CHC.h | 20 +- libsolidity/formal/SMTEncoder.cpp | 235 ++++++++++++++---- libsolidity/formal/SMTEncoder.h | 13 + .../complex/slither/const_state_variables.sol | 3 +- .../complex/slither/data_dependency.sol | 1 - .../functions/constructor_base_basic.sol | 2 - .../functions/constructor_hierarchy.sol | 18 +- .../functions/constructor_hierarchy_2.sol | 13 +- .../functions/constructor_hierarchy_3.sol | 22 ++ .../functions/constructor_hierarchy_4.sol | 23 ++ .../constructor_hierarchy_diamond.sol | 29 +++ .../constructor_hierarchy_diamond_2.sol | 29 +++ .../constructor_hierarchy_diamond_3.sol | 34 +++ ...tructor_hierarchy_diamond_empty_middle.sol | 23 ++ ...rarchy_diamond_empty_middle_empty_base.sol | 19 ++ .../constructor_hierarchy_empty_chain.sol | 22 ++ .../constructor_hierarchy_empty_middle.sol | 20 ++ ...r_hierarchy_empty_middle_no_invocation.sol | 19 ++ .../constructor_hierarchy_mixed_chain.sol | 30 +++ ...uctor_hierarchy_mixed_chain_empty_base.sol | 24 ++ ...ctor_hierarchy_mixed_chain_with_params.sol | 29 +++ ...or_hierarchy_mixed_chain_with_params_2.sol | 27 ++ .../constructor_hierarchy_modifier.sol | 17 ++ .../constructor_hierarchy_same_var.sol | 16 ++ .../functions/constructor_simple.sol | 16 ++ .../functions/constructor_state_value.sol | 16 ++ .../constructor_state_value_inherited.sol | 18 ++ .../constructor_state_value_parameter.sol | 17 ++ ...ctor_hierarchy_mixed_chain_with_params.sol | 29 +++ .../constructor_state_variable_init.sol | 11 + .../constructor_state_variable_init_base.sol | 14 ++ .../constructor_state_variable_init_chain.sol | 22 ++ ...or_state_variable_init_chain_alternate.sol | 21 ++ ...ctor_state_variable_init_chain_run_all.sol | 27 ++ ...or_state_variable_init_chain_run_all_2.sol | 26 ++ ...onstructor_state_variable_init_diamond.sol | 20 ++ ...tor_state_variable_init_diamond_middle.sol | 22 ++ ...ctor_state_variable_init_function_call.sol | 17 ++ .../inheritance/functions_1.sol | 2 +- .../inheritance/functions_2.sol | 2 +- .../inheritance/functions_3.sol | 8 +- .../implicit_constructor_hierarchy.sol | 16 ++ .../implicit_only_constructor_hierarchy.sol | 20 ++ 46 files changed, 1051 insertions(+), 172 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle_empty_base.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_empty_base.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_simple.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/implicit_constructor_hierarchy.sol create mode 100644 test/libsolidity/smtCheckerTests/inheritance/implicit_only_constructor_hierarchy.sol diff --git a/Changelog.md b/Changelog.md index 054a8283e..064c3bb20 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: + * SMTChecker: Add support to constructors including constructor inheritance. * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. * Yul Optimizer: Perform loop-invariant code motion. diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index dc1f611b6..b852485f8 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -110,11 +110,6 @@ bool BMC::visit(ContractDefinition const& _contract) { initContract(_contract); - /// Check targets created by state variable initialization. - smt::Expression constraints = m_context.assertions(); - checkVerificationTargets(constraints); - m_verificationTargets.clear(); - SMTEncoder::visit(_contract); return false; @@ -122,6 +117,17 @@ bool BMC::visit(ContractDefinition const& _contract) void BMC::endVisit(ContractDefinition const& _contract) { + if (auto constructor = _contract.constructor()) + constructor->accept(*this); + else + { + inlineConstructorHierarchy(_contract); + /// Check targets created by state variable initialization. + smt::Expression constraints = m_context.assertions(); + checkVerificationTargets(constraints); + m_verificationTargets.clear(); + } + SMTEncoder::endVisit(_contract); } @@ -132,10 +138,14 @@ bool BMC::visit(FunctionDefinition const& _function) solAssert(m_currentContract, ""); auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; if (find(hierarchy.begin(), hierarchy.end(), contract) == hierarchy.end()) - initializeStateVariables(*contract); + createStateVariables(*contract); if (m_callStack.empty()) + { reset(); + initFunction(_function); + resetStateVariables(); + } /// Already visits the children. SMTEncoder::visit(_function); @@ -447,10 +457,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) // is that there we don't have `_funCall`. pushCallStack({funDef, &_funCall}); - // If an internal function is called to initialize - // a state variable. - if (m_callStack.empty()) - initFunction(*funDef); funDef->accept(*this); } diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index a917faf82..2cc7f13fc 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -65,6 +65,15 @@ void CHC::analyze(SourceUnit const& _source) m_context.setAssertionAccumulation(false); m_variableUsage.setFunctionInlining(false); + auto boolSort = make_shared(smt::Kind::Bool); + auto genesisSort = make_shared( + vector(), + boolSort + ); + m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); + auto genesis = (*m_genesisPredicate)({}); + addRule(genesis, genesis.name); + _source.accept(*this); } @@ -94,10 +103,10 @@ bool CHC::visit(ContractDefinition const& _contract) else m_stateSorts.push_back(smt::smtSort(*var->type())); - clearIndices(); + clearIndices(&_contract); - string interfaceName = "interface_" + _contract.name() + "_" + to_string(_contract.id()); - m_interfacePredicate = createSymbolicBlock(interfaceSort(), interfaceName); + string suffix = _contract.name() + "_" + to_string(_contract.id()); + m_interfacePredicate = createSymbolicBlock(interfaceSort(), "interface_" + suffix); // TODO create static instances for Bool/Int sorts in SolverInterface. auto boolSort = make_shared(smt::Kind::Bool); @@ -105,27 +114,11 @@ bool CHC::visit(ContractDefinition const& _contract) vector(), boolSort ); - m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error"); - // If the contract has a constructor it is handled as a function. - // Otherwise we zero-initialize all state vars. - if (!_contract.constructor()) - { - string constructorName = "constructor_" + _contract.name() + "_" + to_string(_contract.id()); - m_constructorPredicate = createSymbolicBlock(constructorSort(), constructorName); - smt::Expression constructorPred = (*m_constructorPredicate)({}); - addRule(constructorPred, constructorName); - - for (auto const& var: m_stateVariables) - { - auto const& symbVar = m_context.variable(*var); - symbVar->increaseIndex(); - m_interface->declareVariable(symbVar->currentName(), symbVar->sort()); - m_context.setZeroValue(*symbVar); - } - - connectBlocks(constructorPred, interface()); - } + m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix); + m_constructorPredicate = createSymbolicBlock(constructorSort(), "implicit_constructor_" + to_string(_contract.id())); + auto stateExprs = currentStateVariables(); + setCurrentBlock(*m_interfacePredicate, &stateExprs); SMTEncoder::visit(_contract); return false; @@ -136,6 +129,23 @@ void CHC::endVisit(ContractDefinition const& _contract) if (!shouldVisit(_contract)) return; + for (auto const& var: m_stateVariables) + { + solAssert(m_context.knownVariable(*var), ""); + m_context.setZeroValue(*var); + } + auto genesisPred = (*m_genesisPredicate)({}); + auto implicitConstructor = (*m_constructorPredicate)(currentStateVariables()); + connectBlocks(genesisPred, implicitConstructor); + m_currentBlock = implicitConstructor; + + if (auto constructor = _contract.constructor()) + constructor->accept(*this); + else + inlineConstructorHierarchy(_contract); + + connectBlocks(m_currentBlock, interface()); + for (unsigned i = 0; i < m_verificationTargets.size(); ++i) { auto const& target = m_verificationTargets.at(i); @@ -152,6 +162,16 @@ bool CHC::visit(FunctionDefinition const& _function) if (!shouldVisit(_function)) return false; + // This is the case for base constructor inlining. + if (m_currentFunction) + { + solAssert(m_currentFunction->isConstructor(), ""); + solAssert(_function.isConstructor(), ""); + solAssert(_function.scope() != m_currentContract, ""); + SMTEncoder::visit(_function); + return false; + } + solAssert(!m_currentFunction, "Inlining internal function calls not yet implemented"); m_currentFunction = &_function; @@ -163,20 +183,11 @@ bool CHC::visit(FunctionDefinition const& _function) auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); auto bodyPred = predicate(*bodyBlock); - // Store the constraints related to variable initialization. - smt::Expression const& initAssertions = m_context.assertions(); - m_context.pushSolver(); - - connectBlocks(interface(), functionPred); + connectBlocks(m_currentBlock, functionPred); connectBlocks(functionPred, bodyPred); - m_context.popSolver(); - setCurrentBlock(*bodyBlock); - // We need to re-add the constraints that were created for initialization of variables. - m_context.addAssertion(initAssertions); - SMTEncoder::visit(*m_currentFunction); return false; @@ -187,10 +198,37 @@ void CHC::endVisit(FunctionDefinition const& _function) if (!shouldVisit(_function)) return; - connectBlocks(m_currentBlock, interface()); + // This is the case for base constructor inlining. + if (m_currentFunction != &_function) + { + solAssert(m_currentFunction && m_currentFunction->isConstructor(), ""); + solAssert(_function.isConstructor(), ""); + solAssert(_function.scope() != m_currentContract, ""); + } + else + { + // We create an extra exit block for constructors that simply + // connects to the interface in case an explicit constructor + // exists in the hierarchy. + // It is not connected directly here, as normal functions are, + // because of the case where there are only implicit constructors. + // This is done in endVisit(ContractDefinition). + if (_function.isConstructor()) + { + auto constructorExit = createBlock(&_function, "exit_"); + connectBlocks(m_currentBlock, predicate(*constructorExit)); + setCurrentBlock(*constructorExit); + } + else + { + connectBlocks(m_currentBlock, interface()); + clearIndices(m_currentContract, m_currentFunction); + auto stateExprs = currentStateVariables(); + setCurrentBlock(*m_interfacePredicate, &stateExprs); + } + m_currentFunction = nullptr; + } - solAssert(&_function == m_currentFunction, ""); - m_currentFunction = nullptr; SMTEncoder::endVisit(_function); } @@ -445,7 +483,6 @@ void CHC::reset() m_verificationTargets.clear(); m_safeAssertions.clear(); m_unknownFunctionCallSeen = false; - m_blockCounter = 0; m_breakDest = nullptr; m_continueDest = nullptr; } @@ -470,28 +507,31 @@ bool CHC::shouldVisit(FunctionDefinition const& _function) const { if ( _function.isPublic() && - _function.isImplemented() && - !_function.isConstructor() + _function.isImplemented() ) return true; return false; } -void CHC::setCurrentBlock(smt::SymbolicFunctionVariable const& _block) +void CHC::setCurrentBlock( + smt::SymbolicFunctionVariable const& _block, + vector const* _arguments +) { m_context.popSolver(); - clearIndices(); + solAssert(m_currentContract, ""); + clearIndices(m_currentContract, m_currentFunction); m_context.pushSolver(); - m_currentBlock = predicate(_block); + if (_arguments) + m_currentBlock = predicate(_block, *_arguments); + else + m_currentBlock = predicate(_block); } smt::SortPointer CHC::constructorSort() { - solAssert(m_currentContract, ""); - auto boolSort = make_shared(smt::Kind::Bool); - if (!m_currentContract->constructor()) - return make_shared(vector{}, boolSort); - return sort(*m_currentContract->constructor()); + // TODO this will change once we support function calls. + return interfaceSort(); } smt::SortPointer CHC::interfaceSort() @@ -556,19 +596,6 @@ unique_ptr CHC::createSymbolicBlock(smt::SortPoin return block; } -smt::Expression CHC::constructor() -{ - solAssert(m_currentContract, ""); - - if (!m_currentContract->constructor()) - return (*m_constructorPredicate)({}); - - vector paramExprs; - for (auto const& var: m_currentContract->constructor()->parameters()) - paramExprs.push_back(m_context.variable(*var)->currentValue()); - return (*m_constructorPredicate)(paramExprs); -} - smt::Expression CHC::interface() { vector paramExprs; @@ -613,37 +640,31 @@ void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to addRule(edge, _from.name + "_to_" + _to.name); } +vector CHC::currentStateVariables() +{ + solAssert(m_currentContract, ""); + vector exprs; + for (auto const& var: m_stateVariables) + exprs.push_back(m_context.variable(*var)->currentValue()); + return exprs; +} + vector CHC::currentFunctionVariables() { - solAssert(m_currentFunction, ""); vector paramExprs; - for (auto const& var: m_stateVariables) - paramExprs.push_back(m_context.variable(*var)->currentValue()); - for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) - paramExprs.push_back(m_context.variable(*var)->currentValue()); - return paramExprs; + if (m_currentFunction) + for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) + paramExprs.push_back(m_context.variable(*var)->currentValue()); + return currentStateVariables() + paramExprs; } vector CHC::currentBlockVariables() { - solAssert(m_currentFunction, ""); vector paramExprs; - for (auto const& var: m_currentFunction->localVariables()) - paramExprs.push_back(m_context.variable(*var)->currentValue()); - return currentFunctionVariables() + paramExprs; -} - -void CHC::clearIndices() -{ - for (auto const& var: m_stateVariables) - m_context.variable(*var)->resetIndex(); if (m_currentFunction) - { - for (auto const& var: m_currentFunction->parameters() + m_currentFunction->returnParameters()) - m_context.variable(*var)->resetIndex(); for (auto const& var: m_currentFunction->localVariables()) - m_context.variable(*var)->resetIndex(); - } + paramExprs.push_back(m_context.variable(*var)->currentValue()); + return currentFunctionVariables() + paramExprs; } string CHC::predicateName(ASTNode const* _node) @@ -674,7 +695,6 @@ smt::Expression CHC::predicate( return _block(_arguments); } - void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) { m_interface->addRule(_rule, _ruleName); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index a9fb9f2b7..9aa5871d5 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -83,7 +83,7 @@ private: void eraseKnowledge(); bool shouldVisit(ContractDefinition const& _contract) const; bool shouldVisit(FunctionDefinition const& _function) const; - void setCurrentBlock(smt::SymbolicFunctionVariable const& _block); + void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector const* _arguments = nullptr); //@} /// Sort helpers. @@ -99,8 +99,6 @@ private: /// @returns a new block of given _sort and _name. std::unique_ptr createSymbolicBlock(smt::SortPointer _sort, std::string const& _name); - /// Constructor predicate over current variables. - smt::Expression constructor(); /// Interface predicate over current variables. smt::Expression interface(); /// Error predicate over current variables. @@ -116,17 +114,16 @@ private: void connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints = smt::Expression(true)); + /// @returns the current symbolic values of the current state variables. + std::vector currentStateVariables(); + /// @returns the current symbolic values of the current function's /// input and output parameters. std::vector currentFunctionVariables(); - /// @returns the samve as currentFunctionVariables plus + /// @returns the same as currentFunctionVariables plus /// local variables. std::vector currentBlockVariables(); - /// Sets the SSA indices of the variables in scope to 0. - /// Used when starting a new block. - void clearIndices(); - /// @returns the predicate name for a given node. std::string predicateName(ASTNode const* _node); /// @returns a predicate application over the current scoped variables. @@ -152,8 +149,11 @@ private: /// Predicates. //@{ - /// Constructor predicate. - /// Default constructor sets state vars to 0. + /// Genesis predicate. + std::unique_ptr m_genesisPredicate; + + /// Implicit constructor predicate. + /// Explicit constructors are handled as functions. std::unique_ptr m_constructorPredicate; /// Artificial Interface predicate. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index b31ff37d2..b18cf6719 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -22,6 +22,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -39,11 +40,28 @@ bool SMTEncoder::visit(ContractDefinition const& _contract) solAssert(m_currentContract, ""); for (auto const& node: _contract.subNodes()) - if (!dynamic_pointer_cast(node)) + if ( + !dynamic_pointer_cast(node) && + !dynamic_pointer_cast(node) + ) node->accept(*this); vector resolvedFunctions = _contract.definedFunctions(); for (auto const& base: _contract.annotation().linearizedBaseContracts) + { + // Look for all the constructor invocations bottom up. + if (auto const& constructor = base->constructor()) + for (auto const& invocation: constructor->modifiers()) + { + auto refDecl = invocation->name()->annotation().referencedDeclaration; + if (auto const& baseContract = dynamic_cast(refDecl)) + { + solAssert(!m_baseConstructorCalls.count(baseContract), ""); + m_baseConstructorCalls[baseContract] = invocation.get(); + } + } + + // Check for function overrides. for (auto const& baseFunction: base->definedFunctions()) { if (baseFunction->isConstructor()) @@ -62,9 +80,18 @@ bool SMTEncoder::visit(ContractDefinition const& _contract) if (!overridden) resolvedFunctions.push_back(baseFunction); } + } + // Functions are visited first since they might be used + // for state variable initialization which is part of + // the constructor. + // Constructors are visited as part of the constructor + // hierarchy inlining. for (auto const& function: resolvedFunctions) - function->accept(*this); + if (!function->isConstructor()) + function->accept(*this); + + // Constructors need to be handled by the engines separately. return false; } @@ -73,13 +100,16 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract) { m_context.resetAllVariables(); + m_baseConstructorCalls.clear(); + solAssert(m_currentContract == &_contract, ""); m_currentContract = nullptr; } void SMTEncoder::endVisit(VariableDeclaration const& _varDecl) { - if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value()) + // State variables are handled by the constructor. + if (_varDecl.isLocalVariable() &&_varDecl.value()) assignment(_varDecl, *_varDecl.value()); } @@ -90,25 +120,22 @@ bool SMTEncoder::visit(ModifierDefinition const&) bool SMTEncoder::visit(FunctionDefinition const& _function) { - // Not visited by a function call - if (m_callStack.empty()) - initFunction(_function); - m_modifierDepthStack.push_back(-1); + if (_function.isConstructor()) - { - m_errorReporter.warning( - _function.location(), - "Assertion checker does not yet support constructors." - ); - } - else - { - _function.parameterList().accept(*this); - if (_function.returnParameterList()) - _function.returnParameterList()->accept(*this); - visitFunctionOrModifier(); - } + inlineConstructorHierarchy(dynamic_cast(*_function.scope())); + + // Base constructors' parameters should be set by explicit calls, + // but the most derived one needs to be initialized. + if (_function.scope() == m_currentContract) + initializeLocalVariables(_function); + + _function.parameterList().accept(*this); + if (_function.returnParameterList()) + _function.returnParameterList()->accept(*this); + + visitFunctionOrModifier(); + return false; } @@ -130,27 +157,87 @@ void SMTEncoder::visitFunctionOrModifier() solAssert(m_modifierDepthStack.back() < int(function.modifiers().size()), ""); ASTPointer const& modifierInvocation = function.modifiers()[m_modifierDepthStack.back()]; solAssert(modifierInvocation, ""); - modifierInvocation->accept(*this); - auto const& modifierDef = dynamic_cast( - *modifierInvocation->name()->annotation().referencedDeclaration - ); - vector modifierArgsExpr; - if (auto const* arguments = modifierInvocation->arguments()) - { - auto const& modifierParams = modifierDef.parameters(); - solAssert(modifierParams.size() == arguments->size(), ""); - for (unsigned i = 0; i < arguments->size(); ++i) - modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); - } - initializeFunctionCallParameters(modifierDef, modifierArgsExpr); - pushCallStack({&modifierDef, modifierInvocation.get()}); - modifierDef.body().accept(*this); - popCallStack(); + auto refDecl = modifierInvocation->name()->annotation().referencedDeclaration; + if (dynamic_cast(refDecl)) + visitFunctionOrModifier(); + else if (auto modifierDef = dynamic_cast(refDecl)) + inlineModifierInvocation(modifierInvocation.get(), modifierDef); + else + solAssert(false, ""); } --m_modifierDepthStack.back(); } +void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition) +{ + solAssert(_invocation, ""); + _invocation->accept(*this); + + vector args; + if (auto const* arguments = _invocation->arguments()) + { + auto const& modifierParams = _definition->parameters(); + solAssert(modifierParams.size() == arguments->size(), ""); + for (unsigned i = 0; i < arguments->size(); ++i) + args.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); + } + + initializeFunctionCallParameters(*_definition, args); + + pushCallStack({_definition, _invocation}); + if (auto modifier = dynamic_cast(_definition)) + { + modifier->body().accept(*this); + popCallStack(); + } + else if (auto function = dynamic_cast(_definition)) + { + if (function->isImplemented()) + function->accept(*this); + // Functions are popped from the callstack in endVisit(FunctionDefinition) + } +} + +void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract) +{ + auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; + auto it = find(begin(hierarchy), end(hierarchy), &_contract); + solAssert(it != end(hierarchy), ""); + + auto nextBase = it + 1; + // Initialize the base contracts here as long as their constructors are implicit, + // stop when the first explicit constructor is found. + while (nextBase != end(hierarchy)) + { + if (auto baseConstructor = (*nextBase)->constructor()) + { + createLocalVariables(*baseConstructor); + // If any subcontract explicitly called baseConstructor, use those arguments. + if (m_baseConstructorCalls.count(*nextBase)) + inlineModifierInvocation(m_baseConstructorCalls.at(*nextBase), baseConstructor); + else if (baseConstructor->isImplemented()) + { + // The first constructor found is handled like a function + // and its pushed into the callstack there. + // This if avoids duplication in the callstack. + if (!m_callStack.empty()) + pushCallStack({baseConstructor, nullptr}); + baseConstructor->accept(*this); + // popped by endVisit(FunctionDefinition) + } + break; + } + else + { + initializeStateVariables(**nextBase); + ++nextBase; + } + } + + initializeStateVariables(_contract); +} + bool SMTEncoder::visit(PlaceholderStatement const&) { solAssert(!m_callStack.empty(), ""); @@ -551,20 +638,25 @@ void SMTEncoder::initContract(ContractDefinition const& _contract) solAssert(m_currentContract == nullptr, ""); m_currentContract = &_contract; - initializeStateVariables(_contract); + m_context.reset(); + m_context.pushSolver(); + createStateVariables(_contract); + clearIndices(m_currentContract, nullptr); } void SMTEncoder::initFunction(FunctionDefinition const& _function) { solAssert(m_callStack.empty(), ""); + solAssert(m_currentContract, ""); m_context.reset(); m_context.pushSolver(); m_pathConditions.clear(); pushCallStack({&_function, nullptr}); m_uninterpretedTerms.clear(); - resetStateVariables(); - initializeLocalVariables(_function); + createStateVariables(*m_currentContract); + createLocalVariables(_function); m_arrayAssignmentHappened = false; + clearIndices(m_currentContract, &_function); } void SMTEncoder::visitAssert(FunctionCall const& _funCall) @@ -1239,26 +1331,61 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu } } -void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) +void SMTEncoder::createStateVariables(ContractDefinition const& _contract) { for (auto var: _contract.stateVariablesIncludingInherited()) createVariable(*var); } -void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) +void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) +{ + for (auto var: _contract.stateVariables()) + { + solAssert(m_context.knownVariable(*var), ""); + m_context.setZeroValue(*var); + } + + for (auto var: _contract.stateVariables()) + if (var->value()) + { + var->value()->accept(*this); + assignment(*var, *var->value()); + } +} + +void SMTEncoder::createLocalVariables(FunctionDefinition const& _function) { for (auto const& variable: _function.localVariables()) - if (createVariable(*variable)) - m_context.setZeroValue(*variable); + createVariable(*variable); for (auto const& param: _function.parameters()) - if (createVariable(*param)) - m_context.setUnknownValue(*param); + createVariable(*param); if (_function.returnParameterList()) for (auto const& retParam: _function.returnParameters()) - if (createVariable(*retParam)) - m_context.setZeroValue(*retParam); + createVariable(*retParam); +} + +void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) +{ + for (auto const& variable: _function.localVariables()) + { + solAssert(m_context.knownVariable(*variable), ""); + m_context.setZeroValue(*variable); + } + + for (auto const& param: _function.parameters()) + { + solAssert(m_context.knownVariable(*param), ""); + m_context.setUnknownValue(*param); + } + + if (_function.returnParameterList()) + for (auto const& retParam: _function.returnParameters()) + { + solAssert(m_context.knownVariable(*retParam), ""); + m_context.setZeroValue(*retParam); + } } void SMTEncoder::resetStateVariables() @@ -1438,6 +1565,20 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices) m_context.variable(*var.first)->index() = var.second; } +void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function) +{ + solAssert(_contract, ""); + for (auto var: _contract->stateVariablesIncludingInherited()) + m_context.variable(*var)->resetIndex(); + if (_function) + { + for (auto const& var: _function->parameters() + _function->returnParameters()) + m_context.variable(*var)->resetIndex(); + for (auto const& var: _function->localVariables()) + m_context.variable(*var)->resetIndex(); + } +} + Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) { Expression const* base = &_indexAccess.baseExpression(); diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index a81092f84..8931e8dcf 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -122,6 +122,12 @@ protected: /// visit depth. void visitFunctionOrModifier(); + /// Inlines a modifier or base constructor call. + void inlineModifierInvocation(ModifierInvocation const* _invocation, CallableDeclaration const* _definition); + + /// Inlines the constructor hierarchy into a single constructor. + void inlineConstructorHierarchy(ContractDefinition const& _contract); + /// Defines a new global variable or function. void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); @@ -161,7 +167,9 @@ protected: using CallStackEntry = std::pair; + void createStateVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract); + void createLocalVariables(FunctionDefinition const& _function); void initializeLocalVariables(FunctionDefinition const& _function); void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector const& _callArgs); void resetStateVariables(); @@ -209,6 +217,9 @@ protected: VariableIndices copyVariableIndices(); /// Resets the variable indices. void resetVariableIndices(VariableIndices const& _indices); + /// Used when starting a new block. + void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr); + /// @returns variables that are touched in _node's subtree. std::set touchedVariables(ASTNode const& _node); @@ -253,6 +264,8 @@ protected: /// Needs to be a stack because of function calls. std::vector m_modifierDepthStack; + std::map m_baseConstructorCalls; + ContractDefinition const* m_currentContract = nullptr; /// Stores the context of the encoding. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol index 8404c1a89..8610f1840 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol @@ -53,6 +53,5 @@ contract MyConc{ // ---- // Warning: (773-792): This declaration shadows an existing declaration. // Warning: (1009-1086): Function state mutability can be restricted to view -// Warning: (874-879): Underflow (resulting value less than 0) happens here. -// Warning: (874-879): Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning: (985-1002): Underflow (resulting value less than 0) happens here. // Warning: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol b/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol index 2053fff48..29f15c689 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol @@ -137,4 +137,3 @@ contract PropagateThroughReturnValue { // Warning: (748-755): Assertion checker does not yet support this expression. // Warning: (748-751): Assertion checker does not yet implement type struct Reference.St storage pointer // Warning: (748-770): Assertion checker does not yet implement such assignments. -// Warning: (849-905): Assertion checker does not yet support constructors. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol b/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol index 8e5c57cf6..f1ea2e053 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_base_basic.sol @@ -13,5 +13,3 @@ contract B is A { } } // ---- -// Warning: (56-90): Assertion checker does not yet support constructors. -// Warning: (113-151): Assertion checker does not yet support constructors. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol index ebc674edf..9a772b27e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol @@ -1,6 +1,16 @@ pragma experimental SMTChecker; -contract C { constructor(uint) public {} } -contract A is C { constructor() C(2) public {} } +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract A is C { + constructor() C(2) public { + assert(a == 2); + assert(a == 3); + } +} // ---- -// Warning: (45-72): Assertion checker does not yet support constructors. -// Warning: (93-121): Assertion checker does not yet support constructors. +// Warning: (166-180): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol index 0b96c566c..b5a3f36d8 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol @@ -1,10 +1,7 @@ pragma experimental SMTChecker; -contract C { constructor(uint) public {} } -contract A is C { constructor() C(2) public {} } -contract B is C { constructor() C(3) public {} } -contract J is C { constructor() C(3) public {} } +contract C { uint a; constructor(uint x) public { a = x; } } +contract A is C { constructor() C(2) public { assert(a == 2); } } +contract B is C { constructor() C(3) public { assert(a == 3); } } +contract J is C { constructor() C(3) public { assert(a == 4); } } // ---- -// Warning: (45-72): Assertion checker does not yet support constructors. -// Warning: (93-121): Assertion checker does not yet support constructors. -// Warning: (142-170): Assertion checker does not yet support constructors. -// Warning: (191-219): Assertion checker does not yet support constructors. +// Warning: (271-285): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol new file mode 100644 index 000000000..4db811ee2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B is C { + constructor(uint x) public { + a = x; + } +} + +contract A is B { + constructor(uint x) B(x) C(x + 2) public { + assert(a == x); + assert(a == x + 1); + } +} +// ---- +// Warning: (244-262): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol new file mode 100644 index 000000000..63a229614 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B is C { + constructor(uint x) public { + a = x; + } +} + +contract A is B { + constructor(uint x) C(x + 2) B(x + 1) public { + assert(a == x + 1); + } +} +// ---- +// Warning: (212-217): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (242-247): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol new file mode 100644 index 000000000..44e600a0d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B1 is C { + constructor(uint x) public { + a = x; + } +} + +contract B2 is C { + constructor(uint x) C(x + 2) public { + a = x; + } +} + +contract A is B2, B1 { + constructor(uint x) B2(x) B1(x) public { + assert(a == x); + assert(a == x + 1); + } +} +// ---- +// Warning: (205-210): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (321-339): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol new file mode 100644 index 000000000..03922d16e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B1 is C { + constructor(uint x) public { + a = x; + } +} + +contract B2 is C { + constructor(uint x) C(x + 2) public { + a = x; + } +} + +contract A is B2, B1 { + constructor(uint x) B1(x) B2(x) public { + assert(a == x); + assert(a == x + 1); + } +} +// ---- +// Warning: (205-210): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (321-339): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol new file mode 100644 index 000000000..429b5d135 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B1 is C { + uint b1; + constructor(uint x) public { + b1 = x + a; + } +} + +contract B2 is C { + uint b2; + constructor(uint x) C(x + 2) public { + b2 = x + a; + } +} + +contract A is B2, B1 { + constructor(uint x) B2(x) B1(x) public { + assert(b1 == b2); + assert(b1 != b2); + } +} +// ---- +// Warning: (165-170): Underflow (resulting value less than 0) happens here +// Warning: (165-170): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (253-258): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (353-369): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol new file mode 100644 index 000000000..7819019e9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor() public { + a = 2; + } +} + +contract B is C { +} + +contract B2 is C { +} + +contract A is B, B2 { + constructor(uint x) public { + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning: (171-177): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (208-222): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle_empty_base.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle_empty_base.sol new file mode 100644 index 000000000..7c35f4fae --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle_empty_base.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor() public { + a = 2; + } +} + +contract B is C { +} + +contract B2 is C { + constructor() public { + assert(a == 2); + } +} + +contract A is B, B2 { +} diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol new file mode 100644 index 000000000..2c7d8827d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor() public { + a = 2; + } +} + +contract E is F {} +contract D is E {} +contract C is D {} +contract B is C {} + +contract A is B { + constructor(uint x) public { + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning: (201-207): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (238-252): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol new file mode 100644 index 000000000..fbd3436dc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor() public { + a = 2; + } +} + +contract B is C { +} + +contract A is B { + constructor(uint x) B() public { + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning: (145-151): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (186-200): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol new file mode 100644 index 000000000..8b94c53a9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor() public { + a = 2; + } +} + +contract B is C { +} + +contract A is B { + constructor(uint x) public { + assert(a == 3); + } +} +// ---- +// Warning: (145-151): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (164-178): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol new file mode 100644 index 000000000..ec534a431 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor() public { + a = 2; + } +} + +contract E is F {} +contract D is E { + constructor() public { + a = 3; + } +} +contract C is D {} +contract B is C { + constructor() public { + a = 4; + } +} + +contract A is B { + constructor(uint x) public { + assert(a == 4); + assert(a == 5); + } +} +// ---- +// Warning: (275-281): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (312-326): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_empty_base.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_empty_base.sol new file mode 100644 index 000000000..71cbbd13c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_empty_base.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor() public { + a = 2; + } +} + +contract E is F {} +contract D is E { + constructor() public { + a = 3; + } +} +contract C is D {} +contract B is C { + constructor() public { + assert(a == 3); + a = 4; + } +} + +contract A is B { +} diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol new file mode 100644 index 000000000..66380328d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract E is F {} +contract D is E { + constructor() public { + a = 3; + } +} +contract C is D {} +contract B is C { + constructor(uint x) F(x + 1) public { + } +} + +contract A is B { + constructor(uint x) B(x) public { + assert(a == 3); + assert(a == 4); + } +} +// ---- +// Warning: (234-239): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (329-343): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol new file mode 100644 index 000000000..12890009a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract E is F {} +contract D is E { + constructor() public { + a = 3; + } +} +contract C is D {} +contract B is C { + constructor() F(1) public { + assert(a == 3); + assert(a == 2); + } +} + +contract A is B { +} +// ---- +// Warning: (260-274): Assertion violation happens here +// Warning: (260-274): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol new file mode 100644 index 000000000..3feff4dae --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; +contract C { + uint a; + modifier n { _; a = 7; } + constructor(uint x) n public { + a = x; + } +} + +contract A is C { + modifier m { a = 5; _; } + constructor() C(2) public { + assert(a == 4); + } +} +// ---- +// Warning: (202-216): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol new file mode 100644 index 000000000..a607a9556 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract A is C { + constructor() C(2) public { + assert(a == 0); + assert(C.a == 0); + } +} +// ---- +// Warning: (148-162): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol b/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol new file mode 100644 index 000000000..b4f68e33a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint x; + + constructor() public { + assert(x == 0); + x = 10; + } + + function f(uint y) public view { + assert(y == x); + } +} +// ---- +// Warning: (148-162): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol new file mode 100644 index 000000000..1df298f13 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint x = 5; + + constructor() public { + assert(x == 5); + x = 10; + } + + function f(uint y) public view { + assert(y == x); + } +} +// ---- +// Warning: (152-166): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol new file mode 100644 index 000000000..e7349e044 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract B { + uint x = 5; +} + +contract C is B { + constructor() public { + assert(x == 5); + x = 10; + } + + function f(uint y) public view { + assert(y == x); + } +} +// ---- +// Warning: (172-186): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol new file mode 100644 index 000000000..214eb1cd6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + uint x = 5; + + constructor(uint a, uint b) public { + assert(x == 5); + x = a + b; + } + + function f(uint y) public view { + assert(y == x); + } +} +// ---- +// Warning: (169-183): Assertion violation happens here +// Warning: (122-127): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol new file mode 100644 index 000000000..66380328d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract E is F {} +contract D is E { + constructor() public { + a = 3; + } +} +contract C is D {} +contract B is C { + constructor(uint x) F(x + 1) public { + } +} + +contract A is B { + constructor(uint x) B(x) public { + assert(a == 3); + assert(a == 4); + } +} +// ---- +// Warning: (234-239): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (329-343): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol new file mode 100644 index 000000000..bef807b81 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint x = 2; + constructor () public { + assert(x == 2); + assert(x == 3); + } +} +// ---- +// Warning: (104-118): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol new file mode 100644 index 000000000..179b2c6c6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint x = 2; +} + +contract D is C { + constructor() public { + assert(x == 2); + assert(x == 3); + } +} +// ---- +// Warning: (124-138): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol new file mode 100644 index 000000000..e43d9e73a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 1; +} + +contract B is A { + constructor() public { x = 2; } +} + +contract C is B { + constructor() public { x = 3; } +} + +contract D is C { + constructor() public { + assert(x == 3); + assert(x == 2); + } +} +// ---- +// Warning: (232-246): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol new file mode 100644 index 000000000..ee1b098a5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 1; +} + +contract B is A { + constructor() public { x = 2; } +} + +contract C is B { +} + +contract D is C { + constructor() public { + assert(x == 2); + assert(x == 3); + } +} +// ---- +// Warning: (199-213): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol new file mode 100644 index 000000000..edc6eeb33 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B is C { + uint b; + constructor(uint x) public { + b = a + x; + } +} + +contract A is B { + constructor(uint x) B(x) C(x + 2) public { + assert(a == x + 2); + assert(b == x + x + 2); + assert(a == x + 5); + } +} + +// ---- +// Warning: (162-167): Underflow (resulting value less than 0) happens here +// Warning: (162-167): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (287-305): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol new file mode 100644 index 000000000..7efffde55 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; +contract C { + uint a; + constructor(uint x) public { + a = x; + } +} + +contract B is C { + uint b; + constructor(uint x) public { + b = x + 10; + } +} + +contract A is B { + constructor(uint x) B(x) C(x + 2) public { + assert(a == x + 2); + assert(b == x + 10); + assert(b == x + 5); + } +} + +// ---- +// Warning: (162-168): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (285-303): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol new file mode 100644 index 000000000..65fddfe52 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 2; +} + +contract B is A { +} + +contract C is A { +} + +contract D is B, C { + constructor() public { + assert(x == 2); + assert(x == 3); + } +} +// ---- +// Warning: (169-183): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol new file mode 100644 index 000000000..5a28788c4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 1; +} + +contract B is A { + constructor() public { x = 2; } +} + +contract C is A { + constructor() public { x = 3; } +} + +contract D is B, C { + constructor() public { + assert(x == 3); + assert(x == 4); + } +} +// ---- +// Warning: (235-249): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol new file mode 100644 index 000000000..91798ec0c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + uint x = f(2); + constructor () public { + assert(x == 2); + } + + function f(uint y) internal view returns (uint) { + assert(y > 0); + assert(x == 0); + return y; + } +} +// ---- +// Warning: (162-175): Assertion violation happens here +// Warning: (179-193): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol index d43e90955..23b8180bb 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol @@ -15,7 +15,7 @@ contract A { // 2 warnings, B.f and A.g contract B is A { function f() public view { - assert(x == 0); + assert(x == 1); } } // ---- diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol index c23eb0037..dd278a78a 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol @@ -17,7 +17,7 @@ contract B is A { uint y; function f() public view { - assert(x == 0); + assert(x == 1); } } // ---- diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol index d8fbdabfc..2005eb5e7 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol @@ -17,10 +17,10 @@ contract B is A { uint y; function f() public view { - assert(x == 0); + assert(x == 1); } function h() public view { - assert(x == 2); + assert(x == 1); } } @@ -29,10 +29,10 @@ contract C is B { uint z; function f() public view { - assert(x == 0); + assert(x == 1); } function i() public view { - assert(x == 0); + assert(x == 1); } } // ---- diff --git a/test/libsolidity/smtCheckerTests/inheritance/implicit_constructor_hierarchy.sol b/test/libsolidity/smtCheckerTests/inheritance/implicit_constructor_hierarchy.sol new file mode 100644 index 000000000..1eeb4e640 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/implicit_constructor_hierarchy.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + constructor (uint y) public { assert(x == 0); x = y; } +} + +contract B is A { + constructor () A(2) public { assert(x == 2); } +} + +contract C is B { + function f() public view { + assert(x == 2); + } +} diff --git a/test/libsolidity/smtCheckerTests/inheritance/implicit_only_constructor_hierarchy.sol b/test/libsolidity/smtCheckerTests/inheritance/implicit_only_constructor_hierarchy.sol new file mode 100644 index 000000000..c724b7289 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/implicit_only_constructor_hierarchy.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function h() public view { + assert(x == 0); + } +} + +contract B is A { + function g() public view { + assert(x == 0); + } +} + +contract C is B { + function f() public view { + assert(x == 0); + } +} From f7d7c97b8f4597e2951757098ab2f53abee3e1d4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 26 Nov 2019 19:12:44 +0100 Subject: [PATCH 076/130] Allow EVM to EWasm translation in assembly mode. --- solc/CommandLineInterface.cpp | 42 ++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index b37507076..02a5a9386 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -122,6 +122,7 @@ static string const g_strHelp = "help"; static string const g_strInputFile = "input-file"; static string const g_strInterface = "interface"; static string const g_strYul = "yul"; +static string const g_strYulDialect = "yul-dialect"; static string const g_strIR = "ir"; static string const g_strEWasm = "ewasm"; static string const g_strLicense = "license"; @@ -220,6 +221,13 @@ static set const g_machineArgs g_streWasm }; +/// Possible arguments to for --yul-dialect +static set const g_yulDialectArgs +{ + g_strEVM, + g_streWasm +}; + static void version() { sout() << @@ -685,15 +693,20 @@ Allowed options)", ) ( g_argAssemble.c_str(), - "Switch to assembly mode, ignoring all options except --machine and --optimize and assumes input is assembly." + "Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly." ) ( g_argYul.c_str(), - "Switch to Yul mode, ignoring all options except --machine and --optimize and assumes input is Yul." + "Switch to Yul mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is Yul." ) ( g_argStrictAssembly.c_str(), - "Switch to strict assembly mode, ignoring all options except --machine and --optimize and assumes input is strict assembly." + "Switch to strict assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is strict assembly." + ) + ( + g_strYulDialect.c_str(), + po::value()->value_name(boost::join(g_yulDialectArgs, ",")), + "Input dialect to use in assembly or yul mode." ) ( g_argMachine.c_str(), @@ -911,6 +924,26 @@ bool CommandLineInterface::processInput() } if (targetMachine == Machine::eWasm && inputLanguage == Input::StrictAssembly) inputLanguage = Input::EWasm; + if (m_args.count(g_strYulDialect)) + { + string dialect = m_args[g_strYulDialect].as(); + if (dialect == g_strEVM) + inputLanguage = Input::StrictAssembly; + else if (dialect == g_streWasm) + { + inputLanguage = Input::EWasm; + if (targetMachine != Machine::eWasm) + { + serr() << "If you select eWasm as --yul-dialect, --machine has to be eWasm as well." << endl; + return false; + } + } + else + { + serr() << "Invalid option for --yul-dialect: " << dialect << endl; + return false; + } + } if (optimize && inputLanguage != Input::StrictAssembly) { serr() << @@ -1351,6 +1384,9 @@ bool CommandLineInterface::assemble( for (auto const& src: m_sourceCodes) { + // TODO translate from EVM to eWasm if + // _language is EVM and _targetMachine is EWasm + string machine = _targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" : _targetMachine == yul::AssemblyStack::Machine::EVM15 ? "EVM 1.5" : From c1db89161b6fd389dce0b9cd6a62385022fcccce Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 27 Nov 2019 12:12:35 +0100 Subject: [PATCH 077/130] Allow enabling optimisations for Yul + Wasm --- solc/CommandLineInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 02a5a9386..6f22e08e2 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -944,7 +944,7 @@ bool CommandLineInterface::processInput() return false; } } - if (optimize && inputLanguage != Input::StrictAssembly) + if (optimize && (inputLanguage != Input::StrictAssembly && inputLanguage != Input::EWasm)) { serr() << "Optimizer can only be used for strict assembly. Use --" << From ccfc1840a9d4f77eecad330d6cdb0cb29317a24b Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 19:36:58 +0100 Subject: [PATCH 078/130] Allow assembly stack to translate its source. --- libsolidity/interface/CompilerStack.cpp | 22 +++++++-------------- libyul/AssemblyStack.cpp | 18 +++++++++++++++++ libyul/AssemblyStack.h | 3 +++ solc/CommandLineInterface.cpp | 26 ++++++++++++++++--------- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 55640b482..e605c6b48 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1060,25 +1060,17 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract) return; // Re-parse the Yul IR in EVM dialect - yul::AssemblyStack evmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); - evmStack.parseAndAnalyze("", compiledContract.yulIROptimized); + yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); + stack.parseAndAnalyze("", compiledContract.yulIROptimized); - // Turn into eWasm dialect - yul::Object ewasmObject = yul::EVMToEWasmTranslator( - yul::EVMDialect::strictAssemblyForEVMObjects(m_evmVersion) - ).run(*evmStack.parserResult()); + stack.optimize(); + stack.translate(yul::AssemblyStack::Language::EWasm); + stack.optimize(); - // Re-inject into an assembly stack for the eWasm dialect - yul::AssemblyStack ewasmStack(m_evmVersion, yul::AssemblyStack::Language::EWasm, m_optimiserSettings); - // TODO this is a hack for now - provide as structured AST! - ewasmStack.parseAndAnalyze("", "{}"); - *ewasmStack.parserResult() = move(ewasmObject); - ewasmStack.optimize(); - - //cout << yul::AsmPrinter{}(*ewasmStack.parserResult()->code) << endl; + //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; // Turn into eWasm text representation. - auto result = ewasmStack.assemble(yul::AssemblyStack::Machine::eWasm); + auto result = stack.assemble(yul::AssemblyStack::Machine::eWasm); compiledContract.eWasm = std::move(result.assembly); compiledContract.eWasmObject = std::move(*result.bytecode); } diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 87df95f37..7851101af 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,23 @@ void AssemblyStack::optimize() solAssert(analyzeParsed(), "Invalid source code after optimization."); } +void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) +{ + if (m_language == _targetLanguage) + return; + + solAssert( + m_language == Language::StrictAssembly && _targetLanguage == Language::EWasm, + "Invalid language combination" + ); + + *m_parserResult = EVMToEWasmTranslator( + languageToDialect(m_language, m_evmVersion) + ).run(*parserResult()); + + m_language = _targetLanguage; +} + bool AssemblyStack::analyzeParsed() { solAssert(m_parserResult, ""); diff --git a/libyul/AssemblyStack.h b/libyul/AssemblyStack.h index 8367ce786..a6dcd0b37 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/AssemblyStack.h @@ -81,6 +81,9 @@ public: /// If the settings (see constructor) disabled the optimizer, nothing is done here. void optimize(); + /// Translate the source to a different language / dialect. + void translate(Language _targetLanguage); + /// Run the assembly step (should only be called after parseAndAnalyze). MachineAssemblyObject assemble(Machine _machine) const; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6f22e08e2..48f04d564 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -1364,14 +1364,14 @@ bool CommandLineInterface::assemble( for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; - unique_ptr formatter; - if (m_args.count(g_argNewReporter)) - formatter = make_unique(serr(false), m_coloredOutput); - else - formatter = make_unique(serr(false)); - for (auto const& error: stack.errors()) { + unique_ptr formatter; + if (m_args.count(g_argNewReporter)) + formatter = make_unique(serr(false), m_coloredOutput); + else + formatter = make_unique(serr(false)); + g_hasOutput = true; formatter->printErrorInformation(*error); } @@ -1384,19 +1384,27 @@ bool CommandLineInterface::assemble( for (auto const& src: m_sourceCodes) { - // TODO translate from EVM to eWasm if - // _language is EVM and _targetMachine is EWasm - string machine = _targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" : _targetMachine == yul::AssemblyStack::Machine::EVM15 ? "EVM 1.5" : "eWasm"; sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; + yul::AssemblyStack& stack = assemblyStacks[src.first]; sout() << endl << "Pretty printed source:" << endl; sout() << stack.print() << endl; + if (_language != yul::AssemblyStack::Language::EWasm && _targetMachine == yul::AssemblyStack::Machine::eWasm) + { + stack.translate(yul::AssemblyStack::Language::EWasm); + stack.optimize(); + + sout() << endl << "==========================" << endl; + sout() << endl << "Translated source:" << endl; + sout() << stack.print() << endl; + } + yul::MachineAssemblyObject object; try { From 6499e52a860c38a1a8f3439192d3607a35c139b6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 27 Nov 2019 19:41:25 +0100 Subject: [PATCH 079/130] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 064c3bb20..c8bed5337 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: + * Commandline Interface: Allow translation from yul / strict assembly to EWasm using ``solc --yul --yul-dialect evm --machine eWasm`` * SMTChecker: Add support to constructors including constructor inheritance. * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. * Yul Optimizer: Perform loop-invariant code motion. From bee2b9e1206fdac92018f71e5885d291fd0312e1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 28 Nov 2019 16:22:24 +0100 Subject: [PATCH 080/130] Commandline test. --- test/cmdlineTests/evm_to_wasm/args | 1 + test/cmdlineTests/evm_to_wasm/err | 1 + test/cmdlineTests/evm_to_wasm/input.sol | 3 + test/cmdlineTests/evm_to_wasm/output | 104 ++++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 test/cmdlineTests/evm_to_wasm/args create mode 100644 test/cmdlineTests/evm_to_wasm/err create mode 100644 test/cmdlineTests/evm_to_wasm/input.sol create mode 100644 test/cmdlineTests/evm_to_wasm/output diff --git a/test/cmdlineTests/evm_to_wasm/args b/test/cmdlineTests/evm_to_wasm/args new file mode 100644 index 000000000..099ebdc3a --- /dev/null +++ b/test/cmdlineTests/evm_to_wasm/args @@ -0,0 +1 @@ +--assemble --optimize --yul-dialect evm --machine ewasm diff --git a/test/cmdlineTests/evm_to_wasm/err b/test/cmdlineTests/evm_to_wasm/err new file mode 100644 index 000000000..aa7ea77f9 --- /dev/null +++ b/test/cmdlineTests/evm_to_wasm/err @@ -0,0 +1 @@ +Warning: Yul and its optimizer are still experimental. Please use the output with care. diff --git a/test/cmdlineTests/evm_to_wasm/input.sol b/test/cmdlineTests/evm_to_wasm/input.sol new file mode 100644 index 000000000..f21cd2b7e --- /dev/null +++ b/test/cmdlineTests/evm_to_wasm/input.sol @@ -0,0 +1,3 @@ +{ + sstore(0, 1) +} diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output new file mode 100644 index 000000000..3b1e0fab7 --- /dev/null +++ b/test/cmdlineTests/evm_to_wasm/output @@ -0,0 +1,104 @@ + +======= evm_to_wasm/input.sol (eWasm) ======= + +Pretty printed source: +object "object" { + code { { sstore(0, 1) } } +} + + +========================== + +Translated source: +object "object" { + code { + function main() + { + let _1 := 0 + mstore_internal(0, _1, _1, _1, _1) + mstore_internal(32, _1, _1, _1, 1) + eth.storageStore(0, 32) + } + function endian_swap_16(x) -> y + { + y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) + } + function endian_swap_32(x) -> y + { + let hi := i64.shl(endian_swap_16(x), 16) + y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + } + function endian_swap(x) -> y + { + let hi := i64.shl(endian_swap_32(x), 32) + y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + } + function mstore_internal(pos, y1, y2, y3, y4) + { + i64.store(pos, endian_swap(y1)) + i64.store(i64.add(pos, 8), endian_swap(y2)) + i64.store(i64.add(pos, 16), endian_swap(y3)) + i64.store(i64.add(pos, 24), endian_swap(y4)) + } + } +} + + +Binary representation: +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021004200200020002000200010054220200020002000420110054200a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b + +Text representation: +(module + (import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32))) + (memory $memory (export "memory") 1) + (export "main" (func $main)) + +(func $main + (local $_1 i64) + (local.set $_1 (i64.const 0)) + (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) + (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) +) + +(func $endian_swap_16 + (param $x i64) + (result i64) + (local $y i64) + (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) + (local.get $y) +) + +(func $endian_swap_32 + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.get $y) +) + +(func $endian_swap + (param $x i64) + (result i64) + (local $y i64) + (local $hi i64) + (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (local.get $y) +) + +(func $mstore_internal + (param $pos i64) + (param $y1 i64) + (param $y2 i64) + (param $y3 i64) + (param $y4 i64) + (i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1))) + (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 8))) (call $endian_swap (local.get $y2))) + (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 16))) (call $endian_swap (local.get $y3))) + (i64.store (i32.wrap_i64 (i64.add (local.get $pos) (i64.const 24))) (call $endian_swap (local.get $y4))) +) + +) From 909296582fa482d3a32ae771635110eb8f0037ea Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 28 Nov 2019 21:31:12 +0100 Subject: [PATCH 081/130] Remove zeppelin truffle workaround --- test/externalTests/zeppelin.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index b24617f37..aa784c6ad 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -35,8 +35,6 @@ function zeppelin_test run_install install_fn CONFIG="truffle-config.js" - replace_libsolc_call - run_test compile_fn test_fn } From cdbaf067c7311070e577c56b5a799549895f3d3e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 28 Nov 2019 20:41:28 +0100 Subject: [PATCH 082/130] Internal emscripten build cleanup --- cmake/EthCompilerSettings.cmake | 3 + scripts/travis-emscripten/emscripten.jam | 195 ++++++++++++++++++++++ scripts/travis-emscripten/install_deps.sh | 5 +- 3 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 scripts/travis-emscripten/emscripten.jam diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index eb55feef1..4d24668e6 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -113,6 +113,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") # Do not build as a WebAssembly target - we need an asm.js output. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") + + # Disable warnings about not being pure asm.js due to memory growth. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm") endif() endif() diff --git a/scripts/travis-emscripten/emscripten.jam b/scripts/travis-emscripten/emscripten.jam new file mode 100644 index 000000000..cff1977c5 --- /dev/null +++ b/scripts/travis-emscripten/emscripten.jam @@ -0,0 +1,195 @@ +# This is a copy of emscripten.jam from https://github.com/tee3/boost-build-emscripten +# which is released under the following license: +# +# Boost Software License - Version 1.0 - August 17th, 2003 +# +# Permission is hereby granted, free of charge, to any person or organization +# obtaining a copy of the software and accompanying documentation covered by +# this license (the "Software") to use, reproduce, display, distribute, +# execute, and transmit the Software, and to prepare derivative works of the +# Software, and to permit third-parties to whom the Software is furnished to +# do so, all subject to the following: +# +# The copyright notices in the Software and this entire statement, including +# the above license grant, this restriction and the following disclaimer, +# must be included in all copies of the Software, in whole or in part, and +# all derivative works of the Software, unless such copies or derivative +# works are solely in the form of machine-executable object code generated by +# a source language processor. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# Boost.Build support for Emscipten. +# +# @todo add support for dynamic linking +# @todo add support for --js-library, --pre-js, and --post-js options + +import generators ; +import type ; +import toolset ; +import feature ; +import common ; +import errors ; + +if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +{ + .debug-configuration = true ; +} + +# add an emscripten toolset +feature.extend toolset : emscripten ; + +# extend the target-os list to include emscripten +feature.extend target-os : emscripten ; +# make emscripten the default target-os when compiling with the emscripten toolset +feature.set-default target-os : emscripten ; + +# initialize the emscripten toolset +rule init ( version ? : command * : options * ) +{ + command = [ common.get-invocation-command emscripten : em++ : $(command) ] ; + + root = ; + if $(command) + { + root = [ common.get-absolute-tool-path $(command[-1]) ] ; + root = $(root:P) ; + + version ?= [ MATCH "^([0-9.]+)" : [ SHELL \""$(command)\" --version" ] ] ; + if $(version) + { + local actual_version = [ MATCH "^([0-9.]+)" : [ SHELL \""$(command)\" --version" ] ] ; + if $(actual_version) != $(version) + { + errors.user-error "emscripten: detected version $(actual_version) does not match desired $(version)" ; + } + } + } + else + { + errors.user-error "emscripten: em++ not found" ; + } + + local condition = [ common.check-init-parameters emscripten : version $(version) ] ; + + common.handle-options emscripten : $(condition) : $(command) : $(options) ; + + # @todo this seems to be the right way, but this is a list somehow + toolset.add-requirements emscripten:node ; + + if $(.debug-configuration) + { + ECHO "emscripten: using compiler" $(version) "at" $(root) ; + } + + toolset.flags emscripten.compile STDHDRS $(condition) : $(root)/libexec/system/include ; + toolset.flags emscripten.link STDLIBPATH $(condition) : $(root)/libexec/system/lib ; + toolset.flags emscripten AR $(condition) : $(root)/bin/emar ; + toolset.flags emscripten RANLIB $(condition) : $(root)/bin/emranlib ; +} + +type.set-generated-target-suffix EXE : emscripten : js ; +#type.set-generated-target-suffix STATIC_LIB : emscripten : bc ; +#type.set-generated-target-suffix SHARED_LIB : emscripten : bc ; +#type.set-generated-target-suffix OBJ : emscripten : bc ; + +generators.register-linker emscripten.link : OBJ STATIC_LIB : EXE : emscripten ; + +generators.register-archiver emscripten.archive : OBJ : STATIC_LIB : emscripten ; + +generators.register-c-compiler emscripten.compile.c++.preprocess : CPP : PREPROCESSED_CPP : emscripten ; +generators.register-c-compiler emscripten.compile.c.preprocess : C : PREPROCESSED_C : emscripten ; +generators.register-c-compiler emscripten.compile.c++ : CPP : OBJ : emscripten ; +generators.register-c-compiler emscripten.compile.c : C : OBJ : emscripten ; + +# Declare flags + +toolset.flags emscripten.compile OPTIONS off : -O0 ; +toolset.flags emscripten.compile OPTIONS speed : -O3 ; +toolset.flags emscripten.compile OPTIONS space : -Os ; + +toolset.flags emscripten.compile OPTIONS off : -fno-inline ; +toolset.flags emscripten.compile OPTIONS on : -Wno-inline ; +toolset.flags emscripten.compile OPTIONS full : -finline-functions -Wno-inline ; + +toolset.flags emscripten.compile OPTIONS off : -w ; +toolset.flags emscripten.compile OPTIONS on : -Wall ; +toolset.flags emscripten.compile OPTIONS all : -Wall -pedantic ; +toolset.flags emscripten.compile OPTIONS on : -Werror ; + +toolset.flags emscripten.compile OPTIONS on : -g ; +toolset.flags emscripten.compile OPTIONS on : -pg ; + +toolset.flags emscripten.compile.c++ OPTIONS off : -fno-rtti ; +toolset.flags emscripten.compile.c++ OPTIONS off : -fno-exceptions ; + +toolset.flags emscripten.compile USER_OPTIONS ; +toolset.flags emscripten.compile.c++ USER_OPTIONS ; +toolset.flags emscripten.compile DEFINES ; +toolset.flags emscripten.compile INCLUDES ; +toolset.flags emscripten.compile.c++ TEMPLATE_DEPTH ; +toolset.flags emscripten.compile.fortran USER_OPTIONS ; + +toolset.flags emscripten.link DEFAULTS : -Wno-warn-absolute-paths ; + +toolset.flags emscripten.link LIBRARY_PATH ; +toolset.flags emscripten.link FINDLIBS_ST ; +toolset.flags emscripten.link FINDLIBS_SA ; +toolset.flags emscripten.link LIBRARIES ; + +toolset.flags emscripten.link OPTIONS ; + +toolset.flags emscripten.archive AROPTIONS ; + +rule compile.c++ ( targets * : sources * : properties * ) +{ + # Some extensions are compiled as C++ by default. For others, we need to + # pass -x c++. We could always pass -x c++ but distcc does not work with it. + if ! $(>:S) in .cc .cp .cxx .cpp .c++ .C + { + LANG on $(<) = "-x c++" ; + } +} + +rule compile.c ( targets * : sources * : properties * ) +{ + LANG on $(<) = "-x c" ; +} + +actions compile.c++ +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<:W)" "$(>:W)" +} + +actions compile.c +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" +} + +actions compile.c++.preprocess +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" "$(>:W)" -E >"$(<:W)" +} + +actions compile.c.preprocess +{ + "$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" "$(>)" -E >$(<) +} + +actions link +{ + "$(CONFIG_COMMAND)" $(DEFAULTS) $(OPTIONS) -L"$(LIBRARY_PATH:W)" -L"$(STDLIBPATH:W)" -o "$(<:W)" "$(>:W)" -l"$(LIBRARIES:W)" -l"$(STDLIBRARIES:W)" +} + +RM = [ common.rm-command ] ; +actions piecemeal archive +{ + $(RM) "$(<)" + $(AR) $(AROPTIONS) rc "$(<:W)" "$(>:W)" +} diff --git a/scripts/travis-emscripten/install_deps.sh b/scripts/travis-emscripten/install_deps.sh index fc81851c2..7544da4e2 100755 --- a/scripts/travis-emscripten/install_deps.sh +++ b/scripts/travis-emscripten/install_deps.sh @@ -29,6 +29,8 @@ set -ev +SCRIPT_DIR="$(realpath $(dirname $0))" + echo -en 'travis_fold:start:installing_dependencies\\r' test -e boost_1_70_0_install/include/boost/version.hpp || ( rm -rf boost_1_70_0 @@ -40,8 +42,7 @@ tar -xzf boost.tar.gz rm boost.tar.gz cd boost_1_70_0 ./bootstrap.sh -wget -q 'https://raw.githubusercontent.com/tee3/boost-build-emscripten/master/emscripten.jam' -test "$(shasum emscripten.jam)" = "a7e13fc2c1e53b0e079ef440622f879aa6da3049 emscripten.jam" +cp "${SCRIPT_DIR}/emscripten.jam" . echo "using emscripten : : em++ ;" >> project-config.jam ) cd .. From edd80a986d1fb10242a3378eb43bd7844380a8d3 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 28 Nov 2019 13:36:25 +0100 Subject: [PATCH 083/130] Update emscripten to 1.39.3 --- .circleci/config.yml | 2 +- .travis.yml | 4 ++-- Changelog.md | 4 ++++ cmake/EthCompilerSettings.cmake | 4 ++-- scripts/build_emscripten.sh | 4 ++-- scripts/travis-emscripten/build_emscripten.sh | 14 ++++++-------- scripts/travis-emscripten/emscripten.jam | 19 +++++-------------- test/externalTests/common.sh | 4 ++-- 8 files changed, 24 insertions(+), 31 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4a54347b3..3dcadf211 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -476,7 +476,7 @@ jobs: b_ems: docker: - - image: trzeci/emscripten:sdk-tag-1.38.22-64bit + - image: trzeci/emscripten:sdk-tag-1.39.3-64bit environment: TERM: xterm steps: diff --git a/.travis.yml b/.travis.yml index 5754fa0a9..38d59fa93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,7 +110,7 @@ matrix: before_install: - nvm install 8 - nvm use 8 - - docker pull trzeci/emscripten:sdk-tag-1.38.22-64bit + - docker pull trzeci/emscripten:sdk-tag-1.39.3-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off @@ -127,7 +127,7 @@ matrix: # # This key here has no significant on anything, apart from caching. Please keep # it in sync with the version above. - - EMSCRIPTEN_VERSION_KEY="1.38.22" + - EMSCRIPTEN_VERSION_KEY="1.39.3" # OS X Mavericks (10.9) # https://en.wikipedia.org/wiki/OS_X_Mavericks diff --git a/Changelog.md b/Changelog.md index 064c3bb20..7b828a989 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,10 @@ Compiler Features: * Yul Optimizer: Perform loop-invariant code motion. +Build System: + * Update to emscripten version 1.39.3. + + Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 4d24668e6..afd145118 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -86,7 +86,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA elseif(EMSCRIPTEN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") # Leave only exported symbols as public and aggressively remove others - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden") # Optimisation level set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") # Re-enable exception catching (optimisations above -O1 disable it) @@ -110,7 +110,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") # Export the Emscripten-generated auxiliary methods which are needed by solc-js. # Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") # Do not build as a WebAssembly target - we need an asm.js output. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 25f6f7f8c..b457b741d 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -34,7 +34,7 @@ else BUILD_DIR="$1" fi -docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.22-64bit \ +docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.39.3-64bit \ ./scripts/travis-emscripten/install_deps.sh -docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.22-64bit \ +docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.39.3-64bit \ ./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 5c8a06e58..8011205db 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -55,11 +55,11 @@ fi WORKSPACE=/root/project # Increase nodejs stack size -if ! [ -e /emsdk_portable/node/bin/node_orig ] +if ! [ -e /emsdk_portable/node/current/bin/node_orig ] then - mv /emsdk_portable/node/bin/node /emsdk_portable/node/bin/node_orig - echo -e '#!/bin/sh\nexec /emsdk_portable/node/bin/node_orig --stack-size=8192 $@' > /emsdk_portable/node/bin/node - chmod 755 /emsdk_portable/node/bin/node + mv /emsdk_portable/node/current/bin/node /emsdk_portable/node/current/bin/node_orig + echo -e '#!/bin/sh\nexec /emsdk_portable/node/current/bin/node_orig --stack-size=8192 $@' > /emsdk_portable/node/current/bin/node + chmod 755 /emsdk_portable/node/current/bin/node fi # Boost @@ -70,8 +70,8 @@ cd "$WORKSPACE"/boost_1_70_0 --with-system --with-filesystem --with-test --with-program_options cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \ --prefix="$WORKSPACE"/boost_1_70_0_install install ) -ln -sf "$WORKSPACE"/boost_1_70_0_install/lib/* /emsdk_portable/sdk/system/lib -ln -sf "$WORKSPACE"/boost_1_70_0_install/include/* /emsdk_portable/sdk/system/include +ln -sf "$WORKSPACE"/boost_1_70_0_install/lib/* /emsdk_portable/emscripten/sdk/system/lib +ln -sf "$WORKSPACE"/boost_1_70_0_install/include/* /emsdk_portable/emscripten/sdk/system/include echo -en 'travis_fold:end:compiling_boost\\r' echo -en 'travis_fold:start:install_cmake.sh\\r' @@ -94,8 +94,6 @@ make -j 4 cd .. mkdir -p upload -# Patch soljson.js to provide backwards-compatibility with older emscripten versions -echo ";/* backwards compatibility */ Module['Runtime'] = Module;" >> $BUILD_DIR/libsolc/soljson.js cp $BUILD_DIR/libsolc/soljson.js upload/ cp $BUILD_DIR/libsolc/soljson.js ./ diff --git a/scripts/travis-emscripten/emscripten.jam b/scripts/travis-emscripten/emscripten.jam index cff1977c5..0cc92bd56 100644 --- a/scripts/travis-emscripten/emscripten.jam +++ b/scripts/travis-emscripten/emscripten.jam @@ -1,4 +1,4 @@ -# This is a copy of emscripten.jam from https://github.com/tee3/boost-build-emscripten +# Modified version of emscripten.jam from https://github.com/tee3/boost-build-emscripten # which is released under the following license: # # Boost Software License - Version 1.0 - August 17th, 2003 @@ -55,12 +55,8 @@ rule init ( version ? : command * : options * ) { command = [ common.get-invocation-command emscripten : em++ : $(command) ] ; - root = ; if $(command) { - root = [ common.get-absolute-tool-path $(command[-1]) ] ; - root = $(root:P) ; - version ?= [ MATCH "^([0-9.]+)" : [ SHELL \""$(command)\" --version" ] ] ; if $(version) { @@ -83,15 +79,10 @@ rule init ( version ? : command * : options * ) # @todo this seems to be the right way, but this is a list somehow toolset.add-requirements emscripten:node ; - if $(.debug-configuration) - { - ECHO "emscripten: using compiler" $(version) "at" $(root) ; - } - - toolset.flags emscripten.compile STDHDRS $(condition) : $(root)/libexec/system/include ; - toolset.flags emscripten.link STDLIBPATH $(condition) : $(root)/libexec/system/lib ; - toolset.flags emscripten AR $(condition) : $(root)/bin/emar ; - toolset.flags emscripten RANLIB $(condition) : $(root)/bin/emranlib ; + toolset.flags emscripten.compile STDHDRS $(condition) : /emsdk_portable/emscripten/sdk/system/include ; + toolset.flags emscripten.link STDLIBPATH $(condition) : /emsdk_portable/emscripten/sdk/system/lib ; + toolset.flags emscripten AR $(condition) : /emsdk_portable/emscripten/sdk/emar ; + toolset.flags emscripten RANLIB $(condition) : /emsdk_portable/emscripten/sdk/emranlib ; } type.set-generated-target-suffix EXE : emscripten : js ; diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index dd9aa77aa..e5a2dd3e7 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -37,7 +37,7 @@ function setup_solcjs cd "$dir" printLog "Setting up solc-js..." - git clone --depth 1 -b v0.5.0 https://github.com/ethereum/solc-js.git solc + git clone --depth 1 -b master https://github.com/ethereum/solc-js.git solc cd solc npm install @@ -107,7 +107,7 @@ function force_solc_truffle_modules if [ -d "$d" ]; then cd $d rm -rf solc - git clone --depth 1 -b v0.5.0 https://github.com/ethereum/solc-js.git solc + git clone --depth 1 -b master https://github.com/ethereum/solc-js.git solc cp "$1" solc/soljson.js fi ) From c09da092d25e86efda84e19ab02305aa6c178702 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 29 Nov 2019 16:59:15 +0100 Subject: [PATCH 084/130] [SMTChecker] Fix constructors with local vars --- libsolidity/formal/CHC.cpp | 8 +++-- ...uctor_hierarchy_mixed_chain_local_vars.sol | 35 +++++++++++++++++++ .../functions/constructor_this.sol | 13 +++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/constructor_this.sol diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 2cc7f13fc..0bc6a1adc 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -215,9 +215,11 @@ void CHC::endVisit(FunctionDefinition const& _function) // This is done in endVisit(ContractDefinition). if (_function.isConstructor()) { - auto constructorExit = createBlock(&_function, "exit_"); - connectBlocks(m_currentBlock, predicate(*constructorExit)); - setCurrentBlock(*constructorExit); + auto constructorExit = createSymbolicBlock(interfaceSort(), "constructor_exit_" + to_string(_function.id())); + connectBlocks(m_currentBlock, predicate(*constructorExit, currentStateVariables())); + clearIndices(m_currentContract, m_currentFunction); + auto stateExprs = currentStateVariables(); + setCurrentBlock(*constructorExit, &stateExprs); } else { diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol new file mode 100644 index 000000000..eaa7c5dc4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol @@ -0,0 +1,35 @@ +pragma experimental SMTChecker; +contract F { + uint a; + constructor() public { + uint f = 2; + a = f; + } +} + +contract E is F {} +contract D is E { + constructor() public { + uint d = 3; + a = d; + } +} +contract C is D {} +contract B is C { + constructor() public { + uint b = 4; + a = b; + } +} + +contract A is B { + constructor(uint x) public { + uint a1 = 4; + uint a2 = 5; + assert(a == a1); + assert(a == a2); + } +} +// ---- +// Warning: (317-323): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (385-400): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_this.sol b/test/libsolidity/smtCheckerTests/functions/constructor_this.sol new file mode 100644 index 000000000..0a308a166 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/constructor_this.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure {} + constructor() public { + C c = this; + c.f(); // this does not warn now, but should warn in the future + this.f(); + (this).f(); + } +} +// ---- +// Warning: (204-208): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. +// Warning: (223-227): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. From 9eda95caf97e22b00204cbd2d6b6be6f497207f2 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 29 Nov 2019 17:20:50 +0100 Subject: [PATCH 085/130] [SMTChecker] Fix visit to IndexAccess that has type Type --- libsolidity/formal/SMTEncoder.cpp | 5 +---- .../special/abi_decode_memory_v2.sol | 13 ++++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index b18cf6719..643e88df1 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -899,11 +899,8 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) return; } - if (!_indexAccess.indexExpression()) - { - solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, ""); + if (_indexAccess.annotation().type->category() == Type::Category::TypeType) return; - } solAssert(array, ""); defineExpr(_indexAccess, smt::Expression::select( diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol index e469f06ba..eb82932c3 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -3,13 +3,16 @@ pragma experimental "ABIEncoderV2"; contract C { struct S { uint x; uint[] b; } - function f() public pure returns (S memory, bytes memory) { - return abi.decode("abc", (S, bytes)); + function f() public pure returns (S memory, bytes memory, uint[][2] memory) { + return abi.decode("abc", (S, bytes, uint[][2])); } } // ---- // Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments. // Warning: (151-159): Assertion checker does not yet support the type of this variable. -// Warning: (188-191): Assertion checker does not yet implement type abi -// Warning: (207-208): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning: (188-217): Assertion checker does not yet implement this type of function call. +// Warning: (206-209): Assertion checker does not yet implement type abi +// Warning: (225-226): Assertion checker does not yet implement type type(struct C.S storage pointer) +// Warning: (235-241): Assertion checker does not yet implement type type(uint256[] memory) +// Warning: (235-241): Assertion checker does not yet implement this expression. +// Warning: (235-244): Assertion checker does not yet implement type type(uint256[] memory[2] memory) +// Warning: (206-246): Assertion checker does not yet implement this type of function call. From 5adc2a40b9782c55bedfaf1460ffd3f156f4730d Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 29 Nov 2019 18:06:44 +0100 Subject: [PATCH 086/130] [SMTChecker] Fix ICE for arrays and mappings of functions. --- Changelog.md | 1 + libsolidity/formal/SymbolicTypes.cpp | 11 +++++++++-- libsolidity/formal/SymbolicTypes.h | 3 +++ .../types/data_location_in_function_type.sol | 5 +++++ .../function_type_array_as_reference_type.sol | 9 +++++++++ .../smtCheckerTests/types/function_type_arrays.sol | 14 ++++++++++++++ .../types/mapping_and_array_of_functions.sol | 7 +++++++ 7 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/data_location_in_function_type.sol create mode 100644 test/libsolidity/smtCheckerTests/types/function_type_array_as_reference_type.sol create mode 100644 test/libsolidity/smtCheckerTests/types/function_type_arrays.sol create mode 100644 test/libsolidity/smtCheckerTests/types/mapping_and_array_of_functions.sol diff --git a/Changelog.md b/Changelog.md index 7b828a989..93cb33af7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,7 @@ Build System: Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. + * SMTChecker: Fix internal error when using arrays or mappings of functions. diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 591158a25..0fe05e723 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -63,7 +63,7 @@ SortPointer smtSort(solidity::Type const& _type) { auto mapType = dynamic_cast(&_type); solAssert(mapType, ""); - return make_shared(smtSort(*mapType->keyType()), smtSort(*mapType->valueType())); + return make_shared(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType())); } else if (isStringLiteral(_type.category())) { @@ -77,7 +77,7 @@ SortPointer smtSort(solidity::Type const& _type) solAssert(isArray(_type.category()), ""); auto arrayType = dynamic_cast(&_type); solAssert(arrayType, ""); - return make_shared(make_shared(Kind::Int), smtSort(*arrayType->baseType())); + return make_shared(make_shared(Kind::Int), smtSortAbstractFunction(*arrayType->baseType())); } } default: @@ -94,6 +94,13 @@ vector smtSort(vector const& _types) return sorts; } +SortPointer smtSortAbstractFunction(solidity::Type const& _type) +{ + if (isFunction(_type.category())) + return make_shared(Kind::Int); + return smtSort(_type); +} + Kind smtKind(solidity::Type::Category _category) { if (isNumber(_category)) diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 30a341431..2db2807ac 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -32,6 +32,9 @@ namespace smt /// Returns the SMT sort that models the Solidity type _type. SortPointer smtSort(solidity::Type const& _type); std::vector smtSort(std::vector const& _types); +/// If _type has type Function, abstract it to Integer. +/// Otherwise return smtSort(_type). +SortPointer smtSortAbstractFunction(solidity::Type const& _type); /// Returns the SMT kind that models the Solidity type type category _category. Kind smtKind(solidity::Type::Category _category); diff --git a/test/libsolidity/smtCheckerTests/types/data_location_in_function_type.sol b/test/libsolidity/smtCheckerTests/types/data_location_in_function_type.sol new file mode 100644 index 000000000..9aa72ea7d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/data_location_in_function_type.sol @@ -0,0 +1,5 @@ +pragma experimental SMTChecker; +library L { + struct Nested { uint y; } + function c(function(Nested memory) external returns (uint)[] storage) external pure {} +} diff --git a/test/libsolidity/smtCheckerTests/types/function_type_array_as_reference_type.sol b/test/libsolidity/smtCheckerTests/types/function_type_array_as_reference_type.sol new file mode 100644 index 000000000..2500d3ec7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_array_as_reference_type.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + struct Nested { uint y; } + // ensure that we consider array of function pointers as reference type + function b(function(Nested memory) external returns (uint)[] storage) internal pure {} + function c(function(Nested memory) external returns (uint)[] memory) public pure {} + function d(function(Nested memory) external returns (uint)[] calldata) external pure {} +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol b/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol new file mode 100644 index 000000000..f004b5514 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + function(uint) external returns (uint)[] public x; + function(uint) internal returns (uint)[10] y; + function f() view public { + function(uint) returns (uint)[10] memory a; + function(uint) returns (uint)[10] storage b = y; + function(uint) external returns (uint)[] memory c; + c = new function(uint) external returns (uint)[](200); + a; b; + } +} +// ---- +// Warning: (361-410): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/mapping_and_array_of_functions.sol b/test/libsolidity/smtCheckerTests/types/mapping_and_array_of_functions.sol new file mode 100644 index 000000000..a10559ece --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/mapping_and_array_of_functions.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract test { + mapping (address => function() internal returns (uint)) a; + mapping (address => function() external) b; + mapping (address => function() external[]) c; + function() external[] d; +} From 805d4d5a57974ec84128b9c02ad3f54f20f97e58 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 30 Nov 2019 11:56:13 +0100 Subject: [PATCH 087/130] LLL: fix test cases after chaind/selfbalance was added --- test/liblll/Compiler.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index 3e2b38889..d44ccb49f 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -193,6 +193,8 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) "4300", "4400", "4500", + "4600", + "4700", "60005000", "60005100", "600060005200", @@ -420,6 +422,8 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "4300", "4400", "4500", + "4600", + "4700", "60005000", "60005100", "600060005200", @@ -559,6 +563,8 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "(asm NUMBER)", "(asm DIFFICULTY)", "(asm GASLIMIT)", + "(asm CHAINID)", + "(asm SELFBALANCE)", "(asm 0 POP)", "(asm 0 MLOAD)", "(asm 0 0 MSTORE)", From 6a254e27a1762f967af0d8295eed6044adf5ffb2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 30 Nov 2019 11:58:40 +0100 Subject: [PATCH 088/130] Turn on LLL testing in test_ubuntu1904_clang --- .circleci/config.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3dcadf211..87236581a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -295,6 +295,7 @@ jobs: environment: CC: clang CXX: clang++ + CMAKE_OPTIONS: -DLLL=ON steps: - checkout - run: *run_build @@ -304,6 +305,8 @@ jobs: b_ubu: &build_ubuntu1904 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >> + environment: + CMAKE_OPTIONS: -DLLL=ON steps: - checkout - run: *run_build @@ -314,12 +317,13 @@ jobs: <<: *build_ubuntu1904 environment: FORCE_RELEASE: ON + CMAKE_OPTIONS: -DLLL=ON b_ubu18: &build_ubuntu1804 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >> environment: - CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 + CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 -DLLL=ON CMAKE_BUILD_TYPE: RelWithDebugInfo steps: - checkout @@ -364,7 +368,7 @@ jobs: <<: *build_ubuntu1904 environment: CMAKE_BUILD_TYPE: Debug - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF + CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF -DLLL=ON steps: - checkout - run: *run_build @@ -404,6 +408,7 @@ jobs: - image: archlinux/base environment: TERM: xterm + CMAKE_OPTIONS: -DLLL=ON steps: - run: name: Install build dependencies @@ -420,6 +425,7 @@ jobs: environment: TERM: xterm CMAKE_BUILD_TYPE: Debug + CMAKE_OPTIONS: -DLLL=ON steps: - checkout - restore_cache: From ddf6e067ed606c562ab131928649ee20bbd6ee1f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 2 Dec 2019 19:00:17 +0100 Subject: [PATCH 089/130] EVMHost: disallow Byzantium precompiles on earlier EVM versions --- test/EVMHost.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index c61e579c0..e2dd7bf17 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -123,13 +123,13 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept return precompileRipeMD160(_message); else if (_message.destination == 0x0000000000000000000000000000000000000004_address) return precompileIdentity(_message); - else if (_message.destination == 0x0000000000000000000000000000000000000005_address) + else if (_message.destination == 0x0000000000000000000000000000000000000005_address && m_evmVersion >= langutil::EVMVersion::byzantium()) return precompileModExp(_message); - else if (_message.destination == 0x0000000000000000000000000000000000000006_address) + else if (_message.destination == 0x0000000000000000000000000000000000000006_address && m_evmVersion >= langutil::EVMVersion::byzantium()) return precompileALTBN128G1Add(_message); - else if (_message.destination == 0x0000000000000000000000000000000000000007_address) + else if (_message.destination == 0x0000000000000000000000000000000000000007_address && m_evmVersion >= langutil::EVMVersion::byzantium()) return precompileALTBN128G1Mul(_message); - else if (_message.destination == 0x0000000000000000000000000000000000000008_address) + else if (_message.destination == 0x0000000000000000000000000000000000000008_address && m_evmVersion >= langutil::EVMVersion::byzantium()) return precompileALTBN128PairingProduct(_message); State stateBackup = m_state; From fca0f6e2c1ee42c0c9d2bb3d2deb7610a25a54af Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 2 Dec 2019 16:31:14 +0100 Subject: [PATCH 090/130] Disable EndToEnd/snark test on Date: Thu, 28 Nov 2019 13:10:32 +0100 Subject: [PATCH 091/130] EVMHost: Mark precompiles implementation as static methods --- test/EVMHost.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/EVMHost.h b/test/EVMHost.h index 15d069d72..edef88d01 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -166,15 +166,15 @@ public: evmc::address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878")); private: - evmc::result precompileECRecover(evmc_message const& _message) noexcept; - evmc::result precompileSha256(evmc_message const& _message) noexcept; - evmc::result precompileRipeMD160(evmc_message const& _message) noexcept; - evmc::result precompileIdentity(evmc_message const& _message) noexcept; - evmc::result precompileModExp(evmc_message const& _message) noexcept; - evmc::result precompileALTBN128G1Add(evmc_message const& _message) noexcept; - evmc::result precompileALTBN128G1Mul(evmc_message const& _message) noexcept; - evmc::result precompileALTBN128PairingProduct(evmc_message const& _message) noexcept; - evmc::result precompileGeneric(evmc_message const& _message, std::map const& _inOut) noexcept; + static evmc::result precompileECRecover(evmc_message const& _message) noexcept; + static evmc::result precompileSha256(evmc_message const& _message) noexcept; + static evmc::result precompileRipeMD160(evmc_message const& _message) noexcept; + static evmc::result precompileIdentity(evmc_message const& _message) noexcept; + static evmc::result precompileModExp(evmc_message const& _message) noexcept; + static evmc::result precompileALTBN128G1Add(evmc_message const& _message) noexcept; + static evmc::result precompileALTBN128G1Mul(evmc_message const& _message) noexcept; + static evmc::result precompileALTBN128PairingProduct(evmc_message const& _message) noexcept; + static evmc::result precompileGeneric(evmc_message const& _message, std::map const& _inOut) noexcept; /// @returns a result object with no gas usage and result data taken from @a _data. /// @note The return value is only valid as long as @a _data is alive! static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; From 2a62425f0ab0d31e9ee1e8b1580fa1273015e3de Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 30 Nov 2019 01:02:27 +0100 Subject: [PATCH 092/130] EVMHost: mark precompiles as existing accounts in the constructor --- test/EVMHost.cpp | 14 ++++++++++++++ test/EVMHost.h | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index e2dd7bf17..779e9e000 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -86,6 +86,20 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): assertThrow(false, Exception, "Berlin is not supported yet."); else //if (_evmVersion == langutil::EVMVersion::petersburg()) m_evmRevision = EVMC_PETERSBURG; + + // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). + // NOTE: keep this in sync with `EVMHost::call` below. + // + // A lot of precompile addresses had a balance before they became valid addresses for precompiles. + // For example all the precompile addresses allocated in Byzantium had a 1 wei balance sent to them + // roughly 22 days before the update went live. + for (unsigned precompiledAddress = 1; precompiledAddress <= 8; precompiledAddress++) + { + evmc::address address{}; + address.bytes[19] = precompiledAddress; + // 1wei + m_state.accounts[address].balance.bytes[31] = 1; + } } evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept diff --git a/test/EVMHost.h b/test/EVMHost.h index edef88d01..4cbb045f0 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -70,10 +70,6 @@ public: Account* account(evmc::address const& _address) { - // Make all precompiled contracts exist. - // Be future-proof and consider everything below 1024 as precompiled contract. - if (u160(convertFromEVMC(_address)) < 1024) - m_state.accounts[_address]; auto it = m_state.accounts.find(_address); return it == m_state.accounts.end() ? nullptr : &it->second; } From aba2ee0f68d7731a96ec6d67a470365b80d069de Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 21 Nov 2019 21:57:22 +0000 Subject: [PATCH 093/130] Update EVMC to 7.1.0 --- test/evmc/evmc.h | 2 +- test/evmc/evmc.hpp | 44 +++--- test/evmc/loader.c | 6 + test/evmc/mocked_host.hpp | 315 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 20 deletions(-) create mode 100644 test/evmc/mocked_host.hpp diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index fe950d379..3fc3ca610 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -530,7 +530,7 @@ typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context, const evmc_address* address); /** - * Get code size callback function. + * Get code hash callback function. * * This callback function is used by a VM to get the keccak256 hash of the code stored * in the account at the given address. For existing accounts not having a code, this diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index 90321429d..0b44cd12c 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -294,7 +294,7 @@ public: /// Destructor responsible for automatically releasing attached resources. ~result() noexcept { - if (release) + if (release != nullptr) release(this); } @@ -342,10 +342,10 @@ public: virtual ~HostInterface() noexcept = default; /// @copydoc evmc_host_interface::account_exists - virtual bool account_exists(const address& addr) noexcept = 0; + virtual bool account_exists(const address& addr) const noexcept = 0; /// @copydoc evmc_host_interface::get_storage - virtual bytes32 get_storage(const address& addr, const bytes32& key) noexcept = 0; + virtual bytes32 get_storage(const address& addr, const bytes32& key) const noexcept = 0; /// @copydoc evmc_host_interface::set_storage virtual evmc_storage_status set_storage(const address& addr, @@ -353,19 +353,19 @@ public: const bytes32& value) noexcept = 0; /// @copydoc evmc_host_interface::get_balance - virtual uint256be get_balance(const address& addr) noexcept = 0; + virtual uint256be get_balance(const address& addr) const noexcept = 0; /// @copydoc evmc_host_interface::get_code_size - virtual size_t get_code_size(const address& addr) noexcept = 0; + virtual size_t get_code_size(const address& addr) const noexcept = 0; /// @copydoc evmc_host_interface::get_code_hash - virtual bytes32 get_code_hash(const address& addr) noexcept = 0; + virtual bytes32 get_code_hash(const address& addr) const noexcept = 0; /// @copydoc evmc_host_interface::copy_code virtual size_t copy_code(const address& addr, size_t code_offset, uint8_t* buffer_data, - size_t buffer_size) noexcept = 0; + size_t buffer_size) const noexcept = 0; /// @copydoc evmc_host_interface::selfdestruct virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0; @@ -374,10 +374,10 @@ public: virtual result call(const evmc_message& msg) noexcept = 0; /// @copydoc evmc_host_interface::get_tx_context - virtual evmc_tx_context get_tx_context() noexcept = 0; + virtual evmc_tx_context get_tx_context() const noexcept = 0; /// @copydoc evmc_host_interface::get_block_hash - virtual bytes32 get_block_hash(int64_t block_number) noexcept = 0; + virtual bytes32 get_block_hash(int64_t block_number) const noexcept = 0; /// @copydoc evmc_host_interface::emit_log virtual void emit_log(const address& addr, @@ -395,7 +395,7 @@ class HostContext : public HostInterface { const evmc_host_interface* host = nullptr; evmc_host_context* context = nullptr; - evmc_tx_context tx_context = {}; + mutable evmc_tx_context tx_context = {}; public: /// Default constructor for null Host context. @@ -408,12 +408,12 @@ public: : host{&interface}, context{ctx} {} - bool account_exists(const address& address) noexcept final + bool account_exists(const address& address) const noexcept final { return host->account_exists(context, &address); } - bytes32 get_storage(const address& address, const bytes32& key) noexcept final + bytes32 get_storage(const address& address, const bytes32& key) const noexcept final { return host->get_storage(context, &address, &key); } @@ -425,17 +425,17 @@ public: return host->set_storage(context, &address, &key, &value); } - uint256be get_balance(const address& address) noexcept final + uint256be get_balance(const address& address) const noexcept final { return host->get_balance(context, &address); } - size_t get_code_size(const address& address) noexcept final + size_t get_code_size(const address& address) const noexcept final { return host->get_code_size(context, &address); } - bytes32 get_code_hash(const address& address) noexcept final + bytes32 get_code_hash(const address& address) const noexcept final { return host->get_code_hash(context, &address); } @@ -443,7 +443,7 @@ public: size_t copy_code(const address& address, size_t code_offset, uint8_t* buffer_data, - size_t buffer_size) noexcept final + size_t buffer_size) const noexcept final { return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } @@ -464,14 +464,14 @@ public: /// by assuming that the block timestamp should never be zero. /// /// @return The cached transaction context. - evmc_tx_context get_tx_context() noexcept final + evmc_tx_context get_tx_context() const noexcept final { if (tx_context.block_timestamp == 0) tx_context = host->get_tx_context(context); return tx_context; } - bytes32 get_block_hash(int64_t number) noexcept final + bytes32 get_block_hash(int64_t number) const noexcept final { return host->get_block_hash(context, number); } @@ -534,7 +534,7 @@ public: /// Destructor responsible for automatically destroying the VM instance. ~VM() noexcept { - if (m_instance) + if (m_instance != nullptr) m_instance->destroy(m_instance); } @@ -570,6 +570,12 @@ public: /// @copydoc evmc_vm::version char const* version() const noexcept { return m_instance->version; } + /// Checks if the VM has the given capability. + bool has_capability(evmc_capabilities capability) const noexcept + { + return (get_capabilities() & static_cast(capability)) != 0; + } + /// @copydoc evmc::vm::get_capabilities evmc_capabilities_flagset get_capabilities() const noexcept { diff --git a/test/evmc/loader.c b/test/evmc/loader.c index d82005438..0cd4834e7 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -297,6 +297,12 @@ struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_err "%s (%s): unsupported value '%s' for option '%s'", vm->name, path, option, name); goto exit; + + default: + ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE, + "%s (%s): unknown error when setting value '%s' for option '%s'", + vm->name, path, option, name); + goto exit; } } diff --git a/test/evmc/mocked_host.hpp b/test/evmc/mocked_host.hpp new file mode 100644 index 000000000..2ff1701a4 --- /dev/null +++ b/test/evmc/mocked_host.hpp @@ -0,0 +1,315 @@ +// EVMC: Ethereum Client-VM Connector API. +// Copyright 2019 The EVMC Authors. +// Licensed under the Apache License, Version 2.0. +#pragma once + +#include +#include +#include +#include +#include + +namespace evmc +{ +/// The string of bytes. +using bytes = std::basic_string; + +/// Extended value (by dirty flag) for account storage. +struct storage_value +{ + /// The storage value. + bytes32 value; + + /// True means this value has been modified already by the current transaction. + bool dirty{false}; + + /// Default constructor. + storage_value() noexcept = default; + + /// Constructor. + storage_value(const bytes32& _value, bool _dirty = false) noexcept // NOLINT + : value{_value}, dirty{_dirty} + {} +}; + +/// Mocked account. +struct MockedAccount +{ + /// The account nonce. + int nonce = 0; + + /// The account code. + bytes code; + + /// The code hash. Can be a value not related to the actual code. + bytes32 codehash; + + /// The account balance. + uint256be balance; + + /// The account storage map. + std::unordered_map storage; + + /// Helper method for setting balance by numeric type. + void set_balance(uint64_t x) noexcept + { + balance = uint256be{}; + for (std::size_t i = 0; i < sizeof(x); ++i) + balance.bytes[sizeof(balance) - 1 - i] = static_cast(x >> (8 * i)); + } +}; + +/// Mocked EVMC Host implementation. +class MockedHost : public Host +{ +public: + /// LOG record. + struct log_record + { + /// The address of the account which created the log. + address creator; + + /// The data attached to the log. + bytes data; + + /// The log topics. + std::vector topics; + + /// Equal operator. + bool operator==(const log_record& other) const noexcept + { + return creator == other.creator && data == other.data && topics == other.topics; + } + }; + + /// SELFDESTRUCT record. + struct selfdestuct_record + { + /// The address of the account which has self-destructed. + address selfdestructed; + + /// The address of the beneficiary account. + address beneficiary; + + /// Equal operator. + bool operator==(const selfdestuct_record& other) const noexcept + { + return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary; + } + }; + + /// The set of all accounts in the Host, organized by their addresses. + std::unordered_map accounts; + + /// The EVMC transaction context to be returned by get_tx_context(). + evmc_tx_context tx_context = {}; + + /// The block header hash value to be returned by get_block_hash(). + bytes32 block_hash = {}; + + /// The call result to be returned by the call() method. + evmc_result call_result = {}; + + /// The record of all block numbers for which get_block_hash() was called. + mutable std::vector recorded_blockhashes; + + /// The record of all account accesses. + mutable std::vector
recorded_account_accesses; + + /// The maximum number of entries in recorded_account_accesses record. + /// This is arbitrary value useful in fuzzing when we don't want the record to explode. + static constexpr auto max_recorded_account_accesses = 200; + + /// The record of all call messages requested in the call() method. + std::vector recorded_calls; + + /// The maximum number of entries in recorded_calls record. + /// This is arbitrary value useful in fuzzing when we don't want the record to explode. + static constexpr auto max_recorded_calls = 100; + + /// The record of all LOGs passed to the emit_log() method. + std::vector recorded_logs; + + /// The record of all SELFDESTRUCTs from the selfdestruct() method. + std::vector recorded_selfdestructs; + +protected: + /// The copy of call inputs for the recorded_calls record. + std::vector m_recorded_calls_inputs; + + /// Record an account access. + /// @param addr The address of the accessed account. + void record_account_access(const address& addr) const + { + if (recorded_account_accesses.empty()) + recorded_account_accesses.reserve(max_recorded_account_accesses); + + if (recorded_account_accesses.size() < max_recorded_account_accesses) + recorded_account_accesses.emplace_back(addr); + } + + /// Returns true if an account exists (EVMC Host method). + bool account_exists(const address& addr) const noexcept override + { + record_account_access(addr); + return accounts.count(addr) != 0; + } + + /// Get the account's storage value at the given key (EVMC Host method). + bytes32 get_storage(const address& addr, const bytes32& key) const noexcept override + { + record_account_access(addr); + + const auto account_iter = accounts.find(addr); + if (account_iter == accounts.end()) + return {}; + + const auto storage_iter = account_iter->second.storage.find(key); + if (storage_iter != account_iter->second.storage.end()) + return storage_iter->second.value; + return {}; + } + + /// Set the account's storage value (EVMC Host method). + evmc_storage_status set_storage(const address& addr, + const bytes32& key, + const bytes32& value) noexcept override + { + record_account_access(addr); + const auto it = accounts.find(addr); + if (it == accounts.end()) + return EVMC_STORAGE_UNCHANGED; + + auto& old = it->second.storage[key]; + + // Follow https://eips.ethereum.org/EIPS/eip-1283 specification. + // WARNING! This is not complete implementation as refund is not handled here. + + if (old.value == value) + return EVMC_STORAGE_UNCHANGED; + + evmc_storage_status status{}; + if (!old.dirty) + { + old.dirty = true; + if (!old.value) + status = EVMC_STORAGE_ADDED; + else if (value) + status = EVMC_STORAGE_MODIFIED; + else + status = EVMC_STORAGE_DELETED; + } + else + status = EVMC_STORAGE_MODIFIED_AGAIN; + + old.value = value; + return status; + } + + /// Get the account's balance (EVMC Host method). + uint256be get_balance(const address& addr) const noexcept override + { + record_account_access(addr); + const auto it = accounts.find(addr); + if (it == accounts.end()) + return {}; + + return it->second.balance; + } + + /// Get the account's code size (EVMC host method). + size_t get_code_size(const address& addr) const noexcept override + { + record_account_access(addr); + const auto it = accounts.find(addr); + if (it == accounts.end()) + return 0; + return it->second.code.size(); + } + + /// Get the account's code hash (EVMC host method). + bytes32 get_code_hash(const address& addr) const noexcept override + { + record_account_access(addr); + const auto it = accounts.find(addr); + if (it == accounts.end()) + return {}; + return it->second.codehash; + } + + /// Copy the account's code to the given buffer (EVMC host method). + size_t copy_code(const address& addr, + size_t code_offset, + uint8_t* buffer_data, + size_t buffer_size) const noexcept override + { + record_account_access(addr); + const auto it = accounts.find(addr); + if (it == accounts.end()) + return 0; + + const auto& code = it->second.code; + + if (code_offset >= code.size()) + return 0; + + const auto n = std::min(buffer_size, code.size() - code_offset); + + if (n > 0) + std::copy_n(&code[code_offset], n, buffer_data); + return n; + } + + /// Selfdestruct the account (EVMC host method). + void selfdestruct(const address& addr, const address& beneficiary) noexcept override + { + record_account_access(addr); + recorded_selfdestructs.push_back({addr, beneficiary}); + } + + /// Call/create other contract (EVMC host method). + result call(const evmc_message& msg) noexcept override + { + record_account_access(msg.destination); + + if (recorded_calls.empty()) + { + recorded_calls.reserve(max_recorded_calls); + m_recorded_calls_inputs.reserve(max_recorded_calls); // Iterators will not invalidate. + } + + if (recorded_calls.size() < max_recorded_calls) + { + recorded_calls.emplace_back(msg); + auto& call_msg = recorded_calls.back(); + if (call_msg.input_size > 0) + { + m_recorded_calls_inputs.emplace_back(call_msg.input_data, call_msg.input_size); + const auto& input_copy = m_recorded_calls_inputs.back(); + call_msg.input_data = input_copy.data(); + } + } + return result{call_result}; + } + + /// Get transaction context (EVMC host method). + evmc_tx_context get_tx_context() const noexcept override { return tx_context; } + + /// Get the block header hash (EVMC host method). + bytes32 get_block_hash(int64_t block_number) const noexcept override + { + recorded_blockhashes.emplace_back(block_number); + return block_hash; + } + + /// Emit LOG (EVMC host method). + void emit_log(const address& addr, + const uint8_t* data, + size_t data_size, + const bytes32 topics[], + size_t topics_count) noexcept override + { + recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}}); + } +}; +} // namespace evmc From c4012eee3398aac753ca6d27332a57a77074393b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 29 Nov 2019 15:08:53 +0100 Subject: [PATCH 094/130] Update EVMHost to EVMC 7.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Bylica --- test/EVMHost.cpp | 4 ++-- test/EVMHost.h | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 779e9e000..32ba0cc4a 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -229,7 +229,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept return result; } -evmc_tx_context EVMHost::get_tx_context() noexcept +evmc_tx_context EVMHost::get_tx_context() const noexcept { evmc_tx_context ctx = {}; ctx.block_timestamp = m_state.timestamp; @@ -245,7 +245,7 @@ evmc_tx_context EVMHost::get_tx_context() noexcept return ctx; } -evmc::bytes32 EVMHost::get_block_hash(int64_t _number) noexcept +evmc::bytes32 EVMHost::get_block_hash(int64_t _number) const noexcept { return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); } diff --git a/test/EVMHost.h b/test/EVMHost.h index 4cbb045f0..0cfedacec 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -68,6 +68,12 @@ public: std::vector logs; }; + Account const* account(evmc::address const& _address) const + { + auto it = m_state.accounts.find(_address); + return it == m_state.accounts.end() ? nullptr : &it->second; + } + Account* account(evmc::address const& _address) { auto it = m_state.accounts.find(_address); @@ -82,15 +88,19 @@ public: m_state.logs.clear(); } - bool account_exists(evmc::address const& _addr) noexcept final + bool account_exists(evmc::address const& _addr) const noexcept final { return account(_addr) != nullptr; } - evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) noexcept final + evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) const noexcept final { - if (Account* acc = account(_addr)) - return acc->storage[_key]; + if (auto* acc = account(_addr)) + { + auto it = acc->storage.find(_key); + if (it != acc->storage.end()) + return it->second; + } return {}; } @@ -100,21 +110,21 @@ public: evmc::bytes32 const& _value ) noexcept; - evmc::uint256be get_balance(evmc::address const& _addr) noexcept final + evmc::uint256be get_balance(evmc::address const& _addr) const noexcept final { if (Account const* acc = account(_addr)) return acc->balance; return {}; } - size_t get_code_size(evmc::address const& _addr) noexcept final + size_t get_code_size(evmc::address const& _addr) const noexcept final { if (Account const* acc = account(_addr)) return acc->code.size(); return 0; } - evmc::bytes32 get_code_hash(evmc::address const& _addr) noexcept final + evmc::bytes32 get_code_hash(evmc::address const& _addr) const noexcept final { if (Account const* acc = account(_addr)) return acc->codeHash; @@ -126,7 +136,7 @@ public: size_t _codeOffset, uint8_t* _bufferData, size_t _bufferSize - ) noexcept final + ) const noexcept final { size_t i = 0; if (Account const* acc = account(_addr)) @@ -139,9 +149,9 @@ public: evmc::result call(evmc_message const& _message) noexcept; - evmc_tx_context get_tx_context() noexcept; + evmc_tx_context get_tx_context() const noexcept; - evmc::bytes32 get_block_hash(int64_t number) noexcept; + evmc::bytes32 get_block_hash(int64_t number) const noexcept; void emit_log( evmc::address const& _addr, From 17182502485261106d7607b77ed447784417abc3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 21 Nov 2019 22:19:37 +0000 Subject: [PATCH 095/130] EVMHost: use evmc::MockedHost --- test/EVMHost.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/EVMHost.h b/test/EVMHost.h index 0cfedacec..ec0412062 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include @@ -34,7 +35,7 @@ namespace test { using Address = h160; -class EVMHost: public evmc::Host +class EVMHost: public evmc::MockedHost { public: /// Tries to dynamically load libevmone. @returns nullptr on failure. From 2683c83ad299863a59eeabd74c18632d9dcabbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 11:38:47 +0100 Subject: [PATCH 096/130] EVMHost: Use recorded_logs from MockedHost --- test/EVMHost.cpp | 17 ----------------- test/EVMHost.h | 18 +----------------- test/ExecutionFramework.cpp | 15 +++++++++------ test/ExecutionFramework.h | 2 +- 4 files changed, 11 insertions(+), 41 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 32ba0cc4a..26d495ace 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -250,23 +250,6 @@ evmc::bytes32 EVMHost::get_block_hash(int64_t _number) const noexcept return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); } -void EVMHost::emit_log( - evmc::address const& _addr, - uint8_t const* _data, - size_t _dataSize, - evmc::bytes32 const _topics[], - size_t _topicsCount -) noexcept -{ - LogEntry entry; - entry.address = convertFromEVMC(_addr); - for (size_t i = 0; i < _topicsCount; ++i) - entry.topics.emplace_back(convertFromEVMC(_topics[i])); - entry.data = bytes(_data, _data + _dataSize); - m_state.logs.emplace_back(std::move(entry)); -} - - Address EVMHost::convertFromEVMC(evmc::address const& _addr) { return Address(bytes(begin(_addr.bytes), end(_addr.bytes))); diff --git a/test/EVMHost.h b/test/EVMHost.h index ec0412062..60ac85ed2 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -54,19 +54,11 @@ public: std::map storage; }; - struct LogEntry - { - Address address; - std::vector topics; - bytes data; - }; - struct State { size_t blockNumber; uint64_t timestamp; std::map accounts; - std::vector logs; }; Account const* account(evmc::address const& _address) const @@ -86,7 +78,7 @@ public: { m_state.blockNumber++; m_state.timestamp += 15; - m_state.logs.clear(); + recorded_logs.clear(); } bool account_exists(evmc::address const& _addr) const noexcept final @@ -154,14 +146,6 @@ public: evmc::bytes32 get_block_hash(int64_t number) const noexcept; - void emit_log( - evmc::address const& _addr, - uint8_t const* _data, - size_t _dataSize, - evmc::bytes32 const _topics[], - size_t _topicsCount - ) noexcept; - static Address convertFromEVMC(evmc::address const& _addr); static evmc::address convertToEVMC(Address const& _addr); static h256 convertFromEVMC(evmc::bytes32 const& _data); diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 9c08394ef..dea735ac1 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -201,27 +201,30 @@ bool ExecutionFramework::addressHasCode(Address const& _addr) size_t ExecutionFramework::numLogs() const { - return m_evmHost->m_state.logs.size(); + return m_evmHost->recorded_logs.size(); } size_t ExecutionFramework::numLogTopics(size_t _logIdx) const { - return m_evmHost->m_state.logs.at(_logIdx).topics.size(); + return m_evmHost->recorded_logs.at(_logIdx).topics.size(); } h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const { - return m_evmHost->m_state.logs.at(_logIdx).topics.at(_topicIdx); + return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); } Address ExecutionFramework::logAddress(size_t _logIdx) const { - return m_evmHost->m_state.logs.at(_logIdx).address; + return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).creator); } -bytes const& ExecutionFramework::logData(size_t _logIdx) const +bytes ExecutionFramework::logData(size_t _logIdx) const { - return m_evmHost->m_state.logs.at(_logIdx).data; + const auto& data = m_evmHost->recorded_logs.at(_logIdx).data; + // TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(), + // but reference type like string_view would be preferable. + return {data.begin(), data.end()}; } u256 ExecutionFramework::balanceAt(Address const& _addr) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 5019c0fbc..912b987dd 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -266,7 +266,7 @@ protected: size_t numLogTopics(size_t _logIdx) const; h256 logTopic(size_t _logIdx, size_t _topicIdx) const; Address logAddress(size_t _logIdx) const; - bytes const& logData(size_t _logIdx) const; + bytes logData(size_t _logIdx) const; langutil::EVMVersion m_evmVersion; solidity::OptimiserSettings m_optimiserSettings = solidity::OptimiserSettings::minimal(); From 635e2fc9d3a5db5552b479c1b012cdc1bf67095c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 11:54:57 +0100 Subject: [PATCH 097/130] EVMHost: Reuse tx_context from MockedHost --- test/EVMHost.cpp | 25 ++++++++--------------- test/EVMHost.h | 9 ++------ test/ExecutionFramework.cpp | 8 ++++---- test/contracts/AuctionRegistrar.cpp | 14 ++++++------- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- 5 files changed, 22 insertions(+), 36 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 26d495ace..863bd2fdf 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -100,6 +100,15 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): // 1wei m_state.accounts[address].balance.bytes[31] = 1; } + + // TODO: support short literals in EVMC and use them here + tx_context.block_difficulty = convertToEVMC(u256("200000000")); + tx_context.block_gas_limit = 20000000; + tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address; + tx_context.tx_gas_price = convertToEVMC(u256("3000000000")); + tx_context.tx_origin = 0x9292929292929292929292929292929292929292_address; + // Mainnet according to EIP-155 + tx_context.chain_id = convertToEVMC(u256(1)); } evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept @@ -229,22 +238,6 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept return result; } -evmc_tx_context EVMHost::get_tx_context() const noexcept -{ - evmc_tx_context ctx = {}; - ctx.block_timestamp = m_state.timestamp; - ctx.block_number = m_state.blockNumber; - ctx.block_coinbase = m_coinbase; - // TODO: support short literals in EVMC and use them here - ctx.block_difficulty = convertToEVMC(u256("200000000")); - ctx.block_gas_limit = 20000000; - ctx.tx_gas_price = convertToEVMC(u256("3000000000")); - ctx.tx_origin = 0x9292929292929292929292929292929292929292_address; - // Mainnet according to EIP-155 - ctx.chain_id = convertToEVMC(u256(1)); - return ctx; -} - evmc::bytes32 EVMHost::get_block_hash(int64_t _number) const noexcept { return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); diff --git a/test/EVMHost.h b/test/EVMHost.h index 60ac85ed2..14d3869f9 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -56,8 +56,6 @@ public: struct State { - size_t blockNumber; - uint64_t timestamp; std::map accounts; }; @@ -76,8 +74,8 @@ public: void reset() { m_state = State{}; m_currentAddress = {}; } void newBlock() { - m_state.blockNumber++; - m_state.timestamp += 15; + tx_context.block_number++; + tx_context.block_timestamp += 15; recorded_logs.clear(); } @@ -142,8 +140,6 @@ public: evmc::result call(evmc_message const& _message) noexcept; - evmc_tx_context get_tx_context() const noexcept; - evmc::bytes32 get_block_hash(int64_t number) const noexcept; static Address convertFromEVMC(evmc::address const& _addr); @@ -154,7 +150,6 @@ public: State m_state; evmc::address m_currentAddress = {}; - evmc::address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878")); private: static evmc::result precompileECRecover(evmc_message const& _message) noexcept; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index dea735ac1..3be6418dd 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -89,12 +89,12 @@ std::pair ExecutionFramework::compareAndCreateMessage( u256 ExecutionFramework::gasLimit() const { - return {m_evmHost->get_tx_context().block_gas_limit}; + return {m_evmHost->tx_context.block_gas_limit}; } u256 ExecutionFramework::gasPrice() const { - return {EVMHost::convertFromEVMC(m_evmHost->get_tx_context().tx_gas_price)}; + return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const @@ -104,7 +104,7 @@ u256 ExecutionFramework::blockHash(u256 const& _number) const u256 ExecutionFramework::blockNumber() const { - return m_evmHost->m_state.blockNumber; + return m_evmHost->tx_context.block_number; } void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) @@ -178,7 +178,7 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) size_t ExecutionFramework::currentTimestamp() { - return m_evmHost->get_tx_context().block_timestamp; + return m_evmHost->tx_context.block_timestamp; } size_t ExecutionFramework::blockTimestamp(u256 _block) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 183cbdf98..df6721de5 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -27,7 +27,6 @@ #include #include -#include using namespace std; using namespace dev::test; @@ -280,8 +279,7 @@ protected: } }; - size_t const m_biddingTime = size_t(7 * 24 * 3600); - size_t const m_renewalInterval = size_t(365 * 24 * 3600); + int64_t const m_biddingTime = 7 * 24 * 3600; }; } @@ -417,7 +415,7 @@ BOOST_AUTO_TEST_CASE(auction_simple) BOOST_CHECK_EQUAL(registrar.owner(name), 0); // "wait" until auction end - m_evmHost->m_state.timestamp += m_biddingTime + 10; + m_evmHost->tx_context.block_timestamp += m_biddingTime + 10; // trigger auction again registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); @@ -429,7 +427,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding) string name = "x"; unsigned startTime = 0x776347e2; - m_evmHost->m_state.timestamp = startTime; + m_evmHost->tx_context.block_timestamp = startTime; RegistrarInterface registrar(*this); // initiate auction @@ -437,19 +435,19 @@ BOOST_AUTO_TEST_CASE(auction_bidding) registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), 0); // overbid self - m_evmHost->m_state.timestamp = startTime + m_biddingTime - 10; + m_evmHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; registrar.setNextValue(12); registrar.reserve(name); // another bid by someone else sendEther(account(1), 10 * ether); m_sender = account(1); - m_evmHost->m_state.timestamp = startTime + 2 * m_biddingTime - 50; + m_evmHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; registrar.setNextValue(13); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), 0); // end auction by first bidder (which is not highest) trying to overbid again (too late) m_sender = account(0); - m_evmHost->m_state.timestamp = startTime + 4 * m_biddingTime; + m_evmHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; registrar.setNextValue(20); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(1)); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ce6ef619e..bc03c2ef8 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1135,7 +1135,7 @@ BOOST_AUTO_TEST_CASE(blockchain) } } )"; - m_evmHost->m_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); + m_evmHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); m_evmHost->newBlock(); m_evmHost->newBlock(); m_evmHost->newBlock(); From 7a0f31103258b48a6ab1d6392d803ba842c2b227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 12:54:17 +0100 Subject: [PATCH 098/130] EVMHost: Use evmc::MockedAccount --- test/EVMHost.cpp | 18 +++++++++--------- test/EVMHost.h | 27 +++++++++------------------ test/ExecutionFramework.cpp | 4 ++-- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 863bd2fdf..205351f0f 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -98,7 +98,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): evmc::address address{}; address.bytes[19] = precompiledAddress; // 1wei - m_state.accounts[address].balance.bytes[31] = 1; + accounts[address].balance.bytes[31] = 1; } // TODO: support short literals in EVMC and use them here @@ -113,8 +113,8 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept { - evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key]; - m_state.accounts[_addr].storage[_key] = _value; + evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key].value; + m_state.accounts[_addr].storage[_key].value = _value; // TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used if (previousValue == _value) @@ -158,9 +158,9 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept State stateBackup = m_state; u256 value{convertFromEVMC(_message.value)}; - Account& sender = m_state.accounts[_message.sender]; + auto& sender = m_state.accounts[_message.sender]; - bytes code; + evmc::bytes code; evmc_message message = _message; if (message.depth == 0) @@ -186,7 +186,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept asBytes(to_string(sender.nonce++)) )); message.destination = convertToEVMC(createAddress); - code = bytes(message.input_data, message.input_data + message.input_size); + code = evmc::bytes(message.input_data, message.input_data + message.input_size); } else if (message.kind == EVMC_DELEGATECALL) { @@ -202,7 +202,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept code = m_state.accounts[message.destination].code; //TODO CREATE2 - Account& destination = m_state.accounts[message.destination]; + auto& destination = m_state.accounts[message.destination]; if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) { @@ -227,8 +227,8 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept else { result.create_address = message.destination; - destination.code = bytes(result.output_data, result.output_data + result.output_size); - destination.codeHash = convertToEVMC(keccak256(destination.code)); + destination.code = evmc::bytes(result.output_data, result.output_data + result.output_size); + destination.codehash = convertToEVMC(keccak256({result.output_data, result.output_size})); } } diff --git a/test/EVMHost.h b/test/EVMHost.h index 14d3869f9..a673360dc 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -45,27 +45,18 @@ public: explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); - struct Account - { - evmc::uint256be balance = {}; - size_t nonce = 0; - bytes code; - evmc::bytes32 codeHash = {}; - std::map storage; - }; - struct State { - std::map accounts; + std::map accounts; }; - Account const* account(evmc::address const& _address) const + evmc::MockedAccount const* account(evmc::address const& _address) const { auto it = m_state.accounts.find(_address); return it == m_state.accounts.end() ? nullptr : &it->second; } - Account* account(evmc::address const& _address) + evmc::MockedAccount* account(evmc::address const& _address) { auto it = m_state.accounts.find(_address); return it == m_state.accounts.end() ? nullptr : &it->second; @@ -90,7 +81,7 @@ public: { auto it = acc->storage.find(_key); if (it != acc->storage.end()) - return it->second; + return it->second.value; } return {}; } @@ -103,22 +94,22 @@ public: evmc::uint256be get_balance(evmc::address const& _addr) const noexcept final { - if (Account const* acc = account(_addr)) + if (auto const* acc = account(_addr)) return acc->balance; return {}; } size_t get_code_size(evmc::address const& _addr) const noexcept final { - if (Account const* acc = account(_addr)) + if (auto const* acc = account(_addr)) return acc->code.size(); return 0; } evmc::bytes32 get_code_hash(evmc::address const& _addr) const noexcept final { - if (Account const* acc = account(_addr)) - return acc->codeHash; + if (auto const* acc = account(_addr)) + return acc->codehash; return {}; } @@ -130,7 +121,7 @@ public: ) const noexcept final { size_t i = 0; - if (Account const* acc = account(_addr)) + if (auto const* acc = account(_addr)) for (; i < _bufferSize && _codeOffset + i < acc->code.size(); i++) _bufferData[i] = acc->code[_codeOffset + i]; return i; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 3be6418dd..f049da70d 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -234,10 +234,10 @@ u256 ExecutionFramework::balanceAt(Address const& _addr) bool ExecutionFramework::storageEmpty(Address const& _addr) { - if (EVMHost::Account const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr))) + if (auto const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr))) { for (auto const& entry: acc->storage) - if (!(entry.second == evmc::bytes32{})) + if (!(entry.second.value == evmc::bytes32{})) return false; } return true; From eacc1753fd6673996afa1c435feefd5d7ba0ca44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 13:03:38 +0100 Subject: [PATCH 099/130] EVMHost: Remove unnecessary State struct --- test/EVMHost.cpp | 26 +++++++++++++------------- test/EVMHost.h | 16 +++++----------- test/ExecutionFramework.cpp | 3 +-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 205351f0f..1cfe840c2 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -113,8 +113,8 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept { - evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key].value; - m_state.accounts[_addr].storage[_key].value = _value; + evmc::bytes32 previousValue = accounts[_addr].storage[_key].value; + accounts[_addr].storage[_key].value = _value; // TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used if (previousValue == _value) @@ -131,9 +131,9 @@ evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc: void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept { // TODO actual selfdestruct is even more complicated. - evmc::uint256be balance = m_state.accounts[_addr].balance; - m_state.accounts.erase(_addr); - m_state.accounts[_beneficiary].balance = balance; + evmc::uint256be balance = accounts[_addr].balance; + accounts.erase(_addr); + accounts[_beneficiary].balance = balance; } evmc::result EVMHost::call(evmc_message const& _message) noexcept @@ -155,10 +155,10 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept else if (_message.destination == 0x0000000000000000000000000000000000000008_address && m_evmVersion >= langutil::EVMVersion::byzantium()) return precompileALTBN128PairingProduct(_message); - State stateBackup = m_state; + auto const stateBackup = accounts; u256 value{convertFromEVMC(_message.value)}; - auto& sender = m_state.accounts[_message.sender]; + auto& sender = accounts[_message.sender]; evmc::bytes code; @@ -172,7 +172,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept { evmc::result result({}); result.status_code = EVMC_OUT_OF_GAS; - m_state = stateBackup; + accounts = stateBackup; return result; } } @@ -190,19 +190,19 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept } else if (message.kind == EVMC_DELEGATECALL) { - code = m_state.accounts[message.destination].code; + code = accounts[message.destination].code; message.destination = m_currentAddress; } else if (message.kind == EVMC_CALLCODE) { - code = m_state.accounts[message.destination].code; + code = accounts[message.destination].code; message.destination = m_currentAddress; } else - code = m_state.accounts[message.destination].code; + code = accounts[message.destination].code; //TODO CREATE2 - auto& destination = m_state.accounts[message.destination]; + auto& destination = accounts[message.destination]; if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) { @@ -233,7 +233,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept } if (result.status_code != EVMC_SUCCESS) - m_state = stateBackup; + accounts = stateBackup; return result; } diff --git a/test/EVMHost.h b/test/EVMHost.h index a673360dc..453adb663 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -45,24 +45,19 @@ public: explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); - struct State - { - std::map accounts; - }; - evmc::MockedAccount const* account(evmc::address const& _address) const { - auto it = m_state.accounts.find(_address); - return it == m_state.accounts.end() ? nullptr : &it->second; + auto it = accounts.find(_address); + return it == accounts.end() ? nullptr : &it->second; } evmc::MockedAccount* account(evmc::address const& _address) { - auto it = m_state.accounts.find(_address); - return it == m_state.accounts.end() ? nullptr : &it->second; + auto it = accounts.find(_address); + return it == accounts.end() ? nullptr : &it->second; } - void reset() { m_state = State{}; m_currentAddress = {}; } + void reset() { accounts.clear(); m_currentAddress = {}; } void newBlock() { tx_context.block_number++; @@ -139,7 +134,6 @@ public: static evmc::bytes32 convertToEVMC(h256 const& _data); - State m_state; evmc::address m_currentAddress = {}; private: diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index f049da70d..df9c7e9df 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -25,7 +25,6 @@ #include #include -#include #include @@ -56,7 +55,7 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): m_evmHost->reset(); for (size_t i = 0; i < 10; i++) - m_evmHost->m_state.accounts[EVMHost::convertToEVMC(account(i))].balance = + m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance = EVMHost::convertToEVMC(u256(1) << 100); } From 19f54857a674d078255ac7633564533e89f1ce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 13:06:00 +0100 Subject: [PATCH 100/130] EVMHost: Make m_currentAddress private --- test/EVMHost.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/EVMHost.h b/test/EVMHost.h index 453adb663..34c3d4b8f 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -133,10 +133,9 @@ public: static h256 convertFromEVMC(evmc::bytes32 const& _data); static evmc::bytes32 convertToEVMC(h256 const& _data); - +private: evmc::address m_currentAddress = {}; -private: static evmc::result precompileECRecover(evmc_message const& _message) noexcept; static evmc::result precompileSha256(evmc_message const& _message) noexcept; static evmc::result precompileRipeMD160(evmc_message const& _message) noexcept; From 0bd80ed9580334efadf112c5a8aa99cfb791a912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 Nov 2019 13:07:35 +0100 Subject: [PATCH 101/130] EVMHost: Mark methods as final --- test/EVMHost.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/EVMHost.h b/test/EVMHost.h index 34c3d4b8f..7aa1e20b5 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -85,7 +85,7 @@ public: evmc::address const& _addr, evmc::bytes32 const& _key, evmc::bytes32 const& _value - ) noexcept; + ) noexcept final; evmc::uint256be get_balance(evmc::address const& _addr) const noexcept final { @@ -122,11 +122,11 @@ public: return i; } - void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept; + void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final; - evmc::result call(evmc_message const& _message) noexcept; + evmc::result call(evmc_message const& _message) noexcept final; - evmc::bytes32 get_block_hash(int64_t number) const noexcept; + evmc::bytes32 get_block_hash(int64_t number) const noexcept final; static Address convertFromEVMC(evmc::address const& _addr); static evmc::address convertToEVMC(Address const& _addr); From cdcfea73ab540c75e80eda18121069d475788629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sat, 30 Nov 2019 00:10:41 +0100 Subject: [PATCH 102/130] EVMHost: Remove unneeded methods --- test/EVMHost.cpp | 17 --------- test/EVMHost.h | 69 +++---------------------------------- test/ExecutionFramework.cpp | 5 +-- 3 files changed, 7 insertions(+), 84 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 1cfe840c2..9ea472eae 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -111,23 +111,6 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): tx_context.chain_id = convertToEVMC(u256(1)); } -evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept -{ - evmc::bytes32 previousValue = accounts[_addr].storage[_key].value; - accounts[_addr].storage[_key].value = _value; - - // TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used - if (previousValue == _value) - return EVMC_STORAGE_UNCHANGED; - else if (previousValue == evmc::bytes32{}) - return EVMC_STORAGE_ADDED; - else if (_value == evmc::bytes32{}) - return EVMC_STORAGE_DELETED; - else - return EVMC_STORAGE_MODIFIED; - -} - void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept { // TODO actual selfdestruct is even more complicated. diff --git a/test/EVMHost.h b/test/EVMHost.h index 7aa1e20b5..c3f69960c 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -38,6 +38,9 @@ using Address = h160; class EVMHost: public evmc::MockedHost { public: + using MockedHost::get_code_size; + using MockedHost::get_balance; + /// Tries to dynamically load libevmone. @returns nullptr on failure. /// The path has to be provided for the first successful run and will be ignored /// afterwards. @@ -45,18 +48,6 @@ public: explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); - evmc::MockedAccount const* account(evmc::address const& _address) const - { - auto it = accounts.find(_address); - return it == accounts.end() ? nullptr : &it->second; - } - - evmc::MockedAccount* account(evmc::address const& _address) - { - auto it = accounts.find(_address); - return it == accounts.end() ? nullptr : &it->second; - } - void reset() { accounts.clear(); m_currentAddress = {}; } void newBlock() { @@ -67,59 +58,7 @@ public: bool account_exists(evmc::address const& _addr) const noexcept final { - return account(_addr) != nullptr; - } - - evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) const noexcept final - { - if (auto* acc = account(_addr)) - { - auto it = acc->storage.find(_key); - if (it != acc->storage.end()) - return it->second.value; - } - return {}; - } - - evmc_storage_status set_storage( - evmc::address const& _addr, - evmc::bytes32 const& _key, - evmc::bytes32 const& _value - ) noexcept final; - - evmc::uint256be get_balance(evmc::address const& _addr) const noexcept final - { - if (auto const* acc = account(_addr)) - return acc->balance; - return {}; - } - - size_t get_code_size(evmc::address const& _addr) const noexcept final - { - if (auto const* acc = account(_addr)) - return acc->code.size(); - return 0; - } - - evmc::bytes32 get_code_hash(evmc::address const& _addr) const noexcept final - { - if (auto const* acc = account(_addr)) - return acc->codehash; - return {}; - } - - size_t copy_code( - evmc::address const& _addr, - size_t _codeOffset, - uint8_t* _bufferData, - size_t _bufferSize - ) const noexcept final - { - size_t i = 0; - if (auto const* acc = account(_addr)) - for (; i < _bufferSize && _codeOffset + i < acc->code.size(); i++) - _bufferData[i] = acc->code[_codeOffset + i]; - return i; + return evmc::MockedHost::account_exists(_addr); } void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept final; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index df9c7e9df..4f5c8badd 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -233,9 +233,10 @@ u256 ExecutionFramework::balanceAt(Address const& _addr) bool ExecutionFramework::storageEmpty(Address const& _addr) { - if (auto const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr))) + const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr)); + if (it != m_evmHost->accounts.end()) { - for (auto const& entry: acc->storage) + for (auto const& entry: it->second.storage) if (!(entry.second.value == evmc::bytes32{})) return false; } From b1577f5e461ad8a04bf1077f36872d266b780599 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 3 Dec 2019 00:00:13 +0100 Subject: [PATCH 103/130] [SMTChecker] Fix ICE in array of structs type --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 10 ++++---- .../special/abi_decode_memory_v2.sol | 1 - .../types/no_effect_statements.sol | 24 +++++++++++++++++++ .../types/type_expression_array_2d.sol | 1 - .../types/type_expression_array_3d.sol | 1 - .../types/type_expression_tuple_array_2d.sol | 1 - .../types/type_expression_tuple_array_3d.sol | 1 - 8 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/types/no_effect_statements.sol diff --git a/Changelog.md b/Changelog.md index d50b9f590..af170ed01 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,7 @@ Build System: Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. * SMTChecker: Fix internal error when using arrays or mappings of functions. + * SMTChecker: Fix internal error in array of structs type. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 643e88df1..0f04f412b 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -869,8 +869,11 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) { createExpr(_indexAccess); + if (_indexAccess.annotation().type->category() == Type::Category::TypeType) + return; + shared_ptr array; - if (auto const& id = dynamic_cast(&_indexAccess.baseExpression())) + if (auto const* id = dynamic_cast(&_indexAccess.baseExpression())) { auto varDecl = identifierToVariable(*id); solAssert(varDecl, ""); @@ -885,7 +888,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) return; } } - else if (auto const& innerAccess = dynamic_cast(&_indexAccess.baseExpression())) + else if (auto const* innerAccess = dynamic_cast(&_indexAccess.baseExpression())) { solAssert(m_context.knownExpression(*innerAccess), ""); array = m_context.expression(*innerAccess); @@ -899,9 +902,6 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) return; } - if (_indexAccess.annotation().type->category() == Type::Category::TypeType) - return; - solAssert(array, ""); defineExpr(_indexAccess, smt::Expression::select( array->currentValue(), diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol index eb82932c3..154939913 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -13,6 +13,5 @@ contract C { // Warning: (206-209): Assertion checker does not yet implement type abi // Warning: (225-226): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (235-241): Assertion checker does not yet implement type type(uint256[] memory) -// Warning: (235-241): Assertion checker does not yet implement this expression. // Warning: (235-244): Assertion checker does not yet implement type type(uint256[] memory[2] memory) // Warning: (206-246): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol b/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol new file mode 100644 index 000000000..f4d1299e9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract test { + struct s { uint a; uint b;} + function f() pure public returns (byte) { + s; + s(1,2); + s[7]; + uint; + uint[7]; + } +} +// ---- +// Warning: (125-126): Statement has no effect. +// Warning: (130-136): Statement has no effect. +// Warning: (140-144): Statement has no effect. +// Warning: (148-152): Statement has no effect. +// Warning: (156-163): Statement has no effect. +// Warning: (125-126): Assertion checker does not yet implement type type(struct test.s storage pointer) +// Warning: (130-131): Assertion checker does not yet implement type type(struct test.s storage pointer) +// Warning: (130-136): Assertion checker does not yet implement type struct test.s memory +// Warning: (130-136): Assertion checker does not yet implement this expression. +// Warning: (140-141): Assertion checker does not yet implement type type(struct test.s storage pointer) +// Warning: (140-144): Assertion checker does not yet implement type type(struct test.s memory[7] memory) +// Warning: (156-163): Assertion checker does not yet implement type type(uint256[7] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol index f63e6aef3..2bd64030d 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol @@ -7,5 +7,4 @@ function f() public pure { int[][]; } // ---- // Warning: (73-80): Statement has no effect. // Warning: (73-78): Assertion checker does not yet implement type type(int256[] memory) -// Warning: (73-78): Assertion checker does not yet implement this expression. // Warning: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol index 2588ee98d..3f0967caf 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol @@ -7,6 +7,5 @@ function f() public pure { int[][][]; } // ---- // Warning: (73-82): Statement has no effect. // Warning: (73-78): Assertion checker does not yet implement type type(int256[] memory) -// Warning: (73-78): Assertion checker does not yet implement this expression. // Warning: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol index 02c2ac056..e648b5d6d 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol @@ -7,6 +7,5 @@ function f() public pure { (int[][]); } // ---- // Warning: (73-82): Statement has no effect. // Warning: (74-79): Assertion checker does not yet implement type type(int256[] memory) -// Warning: (74-79): Assertion checker does not yet implement this expression. // Warning: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol index 893f30581..2d66a3bb2 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol @@ -7,7 +7,6 @@ function f() public pure { (int[][][]); } // ---- // Warning: (73-84): Statement has no effect. // Warning: (74-79): Assertion checker does not yet implement type type(int256[] memory) -// Warning: (74-79): Assertion checker does not yet implement this expression. // Warning: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning: (74-83): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) // Warning: (73-84): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) From 2340d830ced668555b3699d9c50edae1166d17f6 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Tue, 3 Dec 2019 10:02:55 +0100 Subject: [PATCH 104/130] Add Italian translation --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index d1ac873de..15dea172d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -66,6 +66,7 @@ They have varying degrees of completeness and up-to-dateness. The English version stands as a reference. * `French `_ (in progress) +* `Italian `_ (in progress) * `Japanese `_ * `Korean `_ (in progress) * `Russian `_ (rather outdated) From e04d8955ab8d33bd0f2eb13770156229852d009d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Dec 2019 14:41:02 +0100 Subject: [PATCH 105/130] Update to Z3 4.8.7 and cherry-pick build fix commit --- scripts/deps-ppa/static_z3.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index 9f39a79c9..dc955810c 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -25,7 +25,9 @@ set -ev keyid=70D110489D66E2F6 email=builds@ethereum.org packagename=libz3-static-dev -version=4.8.6 +# On the next version the git cherry-pick below should be removed and the patch suffix removed from the version string. +version=4.8.7 +version_patchsuffix=-1 DISTRIBUTIONS="bionic disco eoan" @@ -40,9 +42,12 @@ pparepo=cpp-build-deps ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files # Fetch source -git clone --depth 1 --branch z3-${version} https://github.com/Z3Prover/z3.git +git clone --branch z3-${version} https://github.com/Z3Prover/z3.git cd z3 -debversion="$version" +# Patch build failure. +git cherry-pick e212159f4e + +debversion="${version}${version_patchsuffix}" CMAKE_OPTIONS="-DBUILD_LIBZ3_SHARED=OFF -DCMAKE_BUILD_TYPE=Release" @@ -176,7 +181,7 @@ This program is free software: you can redistribute it and/or modify Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". EOF cat < debian/changelog -libz3-static-dev (0.0.1-0ubuntu1) saucy; urgency=low +libz3-static-dev (0.0.1-1ubuntu0) saucy; urgency=low * Initial release. @@ -186,7 +191,7 @@ mkdir debian/source echo "3.0 (quilt)" > debian/source/format chmod +x debian/rules -versionsuffix=0ubuntu1~${distribution} +versionsuffix=1ubuntu0~${distribution} EMAIL="$email" dch -v 1:${debversion}-${versionsuffix} "build of ${version}" # build source package From 7ace66ad59a03d2c36e015e1bd1e61130534a36d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Dec 2019 14:44:56 +0100 Subject: [PATCH 106/130] Remove last boost/variant include --- libyul/AsmAnalysisInfo.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libyul/AsmAnalysisInfo.h b/libyul/AsmAnalysisInfo.h index 08a35ade0..1201399a2 100644 --- a/libyul/AsmAnalysisInfo.h +++ b/libyul/AsmAnalysisInfo.h @@ -22,8 +22,6 @@ #include -#include - #include #include #include From c9be3bdf7e8120b32a33b71807da002f4eab759a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Dec 2019 15:08:06 +0100 Subject: [PATCH 107/130] Rename cmake option --- scripts/deps-ppa/static_z3.sh | 6 +++--- scripts/install_static_z3.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index dc955810c..b38af9e9c 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -49,7 +49,7 @@ git cherry-pick e212159f4e debversion="${version}${version_patchsuffix}" -CMAKE_OPTIONS="-DBUILD_LIBZ3_SHARED=OFF -DCMAKE_BUILD_TYPE=Release" +CMAKE_OPTIONS="-DZ3_BUILD_LIBZ3_SHARED=OFF -DCMAKE_BUILD_TYPE=Release" # gzip will create different tars all the time and we are not allowed # to upload the same file twice with different contents, so we only @@ -181,7 +181,7 @@ This program is free software: you can redistribute it and/or modify Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". EOF cat < debian/changelog -libz3-static-dev (0.0.1-1ubuntu0) saucy; urgency=low +libz3-static-dev (0.0.1-2ubuntu0) saucy; urgency=low * Initial release. @@ -191,7 +191,7 @@ mkdir debian/source echo "3.0 (quilt)" > debian/source/format chmod +x debian/rules -versionsuffix=1ubuntu0~${distribution} +versionsuffix=2ubuntu0~${distribution} EMAIL="$email" dch -v 1:${debversion}-${versionsuffix} "build of ${version}" # build source package diff --git a/scripts/install_static_z3.sh b/scripts/install_static_z3.sh index 7748fb1f3..6fba203d8 100644 --- a/scripts/install_static_z3.sh +++ b/scripts/install_static_z3.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -git clone --depth 1 --branch z3-4.8.1 https://github.com/Z3Prover/z3.git +git clone --depth 1 --branch z3-4.8.7 https://github.com/Z3Prover/z3.git cd z3 mkdir build cd build -LDFLAGS="-static" cmake -DBUILD_LIBZ3_SHARED=OFF .. +LDFLAGS="-static" cmake -DZ3_BUILD_LIBZ3_SHARED=OFF .. make -j 4 make install \ No newline at end of file From 7156c908be3a0278ef2574cb38a8910004ddd6ff Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Dec 2019 15:13:52 +0100 Subject: [PATCH 108/130] Drop non-strict version of jsonParse* and always use jsonParseStrict --- libdevcore/JSON.cpp | 12 -------- libdevcore/JSON.h | 14 --------- test/libdevcore/JSON.cpp | 41 ------------------------- test/libsolidity/Metadata.cpp | 2 +- test/libsolidity/SMTCheckerJSONTest.cpp | 5 +-- 5 files changed, 4 insertions(+), 70 deletions(-) diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp index 80c116bbe..5d1ea831d 100644 --- a/libdevcore/JSON.cpp +++ b/libdevcore/JSON.cpp @@ -111,16 +111,4 @@ bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* return parse(readerBuilder, _input, _json, _errs); } -bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = nullptr */) -{ - static Json::CharReaderBuilder readerBuilder; - return parse(readerBuilder, _input, _json, _errs); -} - -bool jsonParseFile(string const& _fileName, Json::Value& _json, string *_errs /* = nullptr */) -{ - return jsonParse(readFileAsString(_fileName), _json, _errs); -} - - } // namespace dev diff --git a/libdevcore/JSON.h b/libdevcore/JSON.h index ecb934673..2390d55f2 100644 --- a/libdevcore/JSON.h +++ b/libdevcore/JSON.h @@ -41,18 +41,4 @@ std::string jsonCompactPrint(Json::Value const& _input); /// \return \c true if the document was successfully parsed, \c false if an error occurred. bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); -/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json) -/// \param _input JSON input string -/// \param _json [out] resulting JSON object -/// \param _errs [out] Formatted error messages -/// \return \c true if the document was successfully parsed, \c false if an error occurred. -bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); - -/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json) -/// \param _input file containing JSON input -/// \param _json [out] resulting JSON object -/// \param _errs [out] Formatted error messages -/// \return \c true if the document was successfully parsed, \c false if an error occurred. -bool jsonParseFile(std::string const& _fileName, Json::Value& _json, std::string* _errs = nullptr); - } diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp index a3e081be6..684fd92b2 100644 --- a/test/libdevcore/JSON.cpp +++ b/test/libdevcore/JSON.cpp @@ -69,47 +69,6 @@ BOOST_AUTO_TEST_CASE(json_compact_print) BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}" == jsonCompactPrint(json)); } -BOOST_AUTO_TEST_CASE(parse_json_not_strict) -{ - Json::Value json; - std::string errors; - - // just parse a valid json input - BOOST_CHECK(jsonParse("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); - BOOST_CHECK(json["1"] == 1); - BOOST_CHECK(json["2"] == "2"); - BOOST_CHECK(json["3"]["3.1"] == "3.1"); - BOOST_CHECK(json["3"]["3.2"] == 2); - - // trailing garbage is allowed here - BOOST_CHECK(jsonParse("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); - BOOST_CHECK(json["1"] == 2); - BOOST_CHECK(json["2"] == "2"); - BOOST_CHECK(json["3"]["3.1"] == "3.1"); - BOOST_CHECK(json["3"]["3.2"] == 3); - - // comments are allowed - BOOST_CHECK(jsonParse( - "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":4}}", json, &errors - )); - BOOST_CHECK(json["1"] == 3); - BOOST_CHECK(json["2"] == "2"); - BOOST_CHECK(json["3"]["3.1"] == "3.1"); - BOOST_CHECK(json["3"]["3.2"] == 4); - - // root element other than object or array is allowed - BOOST_CHECK(jsonParse("[]", json, &errors)); - BOOST_CHECK(jsonParse("{}", json, &errors)); - BOOST_CHECK(jsonParse("1", json, &errors)); - BOOST_CHECK(json == 1); - BOOST_CHECK(jsonParse("\"hello\"", json, &errors)); - BOOST_CHECK(json == "hello"); - - // non-UTF-8 escapes allowed - BOOST_CHECK(jsonParse("[ \"\x80\xec\x80\" ]", json, &errors)); - BOOST_CHECK(json[0] == "\x80\xec\x80"); -} - BOOST_AUTO_TEST_CASE(parse_json_strict) { Json::Value json; diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 2526dd8b6..f0af8b544 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); string metadata_str = compilerStack.metadata("test"); Json::Value metadata; - jsonParse(metadata_str, metadata); + jsonParseStrict(metadata_str, metadata); BOOST_CHECK(dev::test::isValidMetadata(metadata_str)); BOOST_CHECK(metadata.isMember("settings")); if (_literal) diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index a2ce4e26b..19bdcaacc 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +44,7 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _ev string jsonFilename = _filename.substr(0, _filename.size() - 4) + ".json"; if ( - !jsonParseFile(jsonFilename, m_smtResponses) || + !jsonParseStrict(readFileAsString(jsonFilename), m_smtResponses) || !m_smtResponses.isObject() ) BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); @@ -158,7 +159,7 @@ Json::Value SMTCheckerTest::buildJson(string const& _extra) sources += "}"; string input = "{" + language + ", " + sources + "}"; Json::Value source; - if (!jsonParse(input, source)) + if (!jsonParseStrict(input, source)) BOOST_THROW_EXCEPTION(runtime_error("Could not build JSON from string: " + input)); return source; } From cab00755e8662e1f0b13f85d16d3bca3e30bd7b4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 26 Nov 2019 22:49:26 +0100 Subject: [PATCH 109/130] Actually throw YulAssertion in yulAssert --- libyul/Exceptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 3e1e4155b..7ce1a8066 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -33,6 +33,6 @@ struct YulAssertion: virtual YulException {}; /// Assertion that throws an YulAssertion containing the given description if it is not met. #define yulAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::yul::YulException, DESCRIPTION) + assertThrow(CONDITION, ::yul::YulAssertion, DESCRIPTION) } From 7e8f0a17bce55b63b3e7ed00394baf6e1a60daa5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 26 Nov 2019 22:52:09 +0100 Subject: [PATCH 110/130] Replace all solAsserts with yulAsserts in libyul --- libyul/AsmAnalysis.cpp | 46 ++++++------ libyul/AsmParser.cpp | 15 ++-- libyul/AsmPrinter.cpp | 36 +++++----- libyul/AsmScopeFiller.cpp | 4 +- libyul/AssemblyStack.cpp | 44 ++++++------ libyul/CompilabilityChecker.cpp | 4 +- libyul/ObjectParser.cpp | 2 +- libyul/backends/evm/AsmCodeGen.cpp | 12 ++-- libyul/backends/evm/EVMAssembly.cpp | 43 ++++++----- libyul/backends/evm/EVMCodeTransform.cpp | 92 ++++++++++++------------ libyul/backends/evm/NoOutputAssembly.cpp | 23 +++--- 11 files changed, 160 insertions(+), 161 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 80a39f386..9ea6cf2bc 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -60,12 +60,12 @@ bool AsmAnalyzer::analyze(Block const& _block) success = (*this)(_block); if (!success) - solAssert(m_errorReporter.hasErrors(), "No success but no error."); + yulAssert(m_errorReporter.hasErrors(), "No success but no error."); } catch (FatalError const&) { // This FatalError con occur if the errorReporter has too many errors. - solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); + yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); } return success && !m_errorReporter.hasErrors(); } @@ -83,13 +83,13 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, {}, _object.dataNames() ).analyze(*_object.code); - solAssert(success && errorList.empty(), "Invalid assembly/yul code."); + yulAssert(success && errorList.empty(), "Invalid assembly/yul code."); return analysisInfo; } bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(!_label.name.empty(), ""); + yulAssert(!_label.name.empty(), ""); checkLooseFeature( _label.location, "The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead." @@ -134,8 +134,8 @@ bool AsmAnalyzer::operator()(Literal const& _literal) } else if (_literal.kind == LiteralKind::Boolean) { - solAssert(m_dialect.flavour == AsmFlavour::Yul, ""); - solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); + yulAssert(m_dialect.flavour == AsmFlavour::Yul, ""); + yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; @@ -143,7 +143,7 @@ bool AsmAnalyzer::operator()(Literal const& _literal) bool AsmAnalyzer::operator()(Identifier const& _identifier) { - solAssert(!_identifier.name.empty(), ""); + yulAssert(!_identifier.name.empty(), ""); size_t numErrorsBefore = m_errorReporter.errors().size(); bool success = true; if (m_currentScope->lookup(_identifier.name, GenericVisitor{ @@ -197,14 +197,14 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); + yulAssert(m_dialect.flavour != AsmFlavour::Yul, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) success = false; // Parser already checks that the number of arguments is correct. auto const& info = instructionInfo(_instr.instruction); - solAssert(info.args == int(_instr.arguments.size()), ""); + yulAssert(info.args == int(_instr.arguments.size()), ""); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instr] = m_stackHeight; warnOnInstructions(_instr.instruction, _instr.location); @@ -245,9 +245,9 @@ bool AsmAnalyzer::operator()(StackAssignment const& _assignment) bool AsmAnalyzer::operator()(Assignment const& _assignment) { - solAssert(_assignment.value, ""); + yulAssert(_assignment.value, ""); int const expectedItems = _assignment.variableNames.size(); - solAssert(expectedItems >= 1, ""); + yulAssert(expectedItems >= 1, ""); int const stackHeight = m_stackHeight; bool success = std::visit(*this, *_assignment.value); if ((m_stackHeight - stackHeight) != expectedItems) @@ -306,9 +306,9 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) { - solAssert(!_funDef.name.empty(), ""); + yulAssert(!_funDef.name.empty(), ""); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); - solAssert(virtualBlock, ""); + yulAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { @@ -328,7 +328,7 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) bool AsmAnalyzer::operator()(FunctionCall const& _funCall) { - solAssert(!_funCall.functionName.name.empty(), ""); + yulAssert(!_funCall.functionName.name.empty(), ""); bool success = true; size_t parameters = 0; size_t returns = 0; @@ -426,7 +426,7 @@ bool AsmAnalyzer::operator()(If const& _if) bool AsmAnalyzer::operator()(Switch const& _switch) { - solAssert(_switch.expression, ""); + yulAssert(_switch.expression, ""); bool success = true; @@ -498,7 +498,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) bool AsmAnalyzer::operator()(ForLoop const& _for) { - solAssert(_for.condition, ""); + yulAssert(_for.condition, ""); Scope* outerScope = m_currentScope; @@ -609,7 +609,7 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize) { - solAssert(!_variable.name.empty(), ""); + yulAssert(!_variable.name.empty(), ""); bool success = true; size_t numErrorsBefore = m_errorReporter.errors().size(); size_t variableSize(-1); @@ -665,9 +665,9 @@ bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize Scope& AsmAnalyzer::scope(Block const* _block) { - solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present."); + yulAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present."); auto scopePtr = m_info.scopes.at(_block); - solAssert(scopePtr, "Scope requested but not present."); + yulAssert(scopePtr, "Scope requested but not present."); return *scopePtr; } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) @@ -686,10 +686,10 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio { // We assume that returndatacopy, returndatasize and staticcall are either all available // or all not available. - solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + yulAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); // Similarly we assume bitwise shifting and create2 go together. - solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); + yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); + yulAssert(m_dialect.flavour != AsmFlavour::Yul, ""); auto errorForVM = [=](string const& vmKindMessage) { m_errorReporter.typeError( @@ -768,7 +768,7 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) { if (m_dialect.flavour != AsmFlavour::Loose) - solAssert(false, _description); + yulAssert(false, _description); else if (m_errorTypeForLoose) m_errorReporter.error(*m_errorTypeForLoose, _location, _description); } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index de98f7d11..5de7417d2 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -52,7 +53,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ } catch (FatalError const&) { - solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); + yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); } return nullptr; @@ -257,7 +258,7 @@ Statement Parser::parseStatement() } else { - solAssert(holds_alternative(elementary), "Invalid elementary operation."); + yulAssert(holds_alternative(elementary), "Invalid elementary operation."); return std::get(elementary); } } @@ -277,7 +278,7 @@ Case Parser::parseCase() _case.value = make_unique(std::get(std::move(literal))); } else - solAssert(false, "Case or default case expected."); + yulAssert(false, "Case or default case expected."); _case.body = parseBlock(); _case.location.end = _case.body.location.end; return _case; @@ -315,7 +316,7 @@ Expression Parser::parseExpression() return parseCall(std::move(operation)); else if (holds_alternative(operation)) { - solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); + yulAssert(m_dialect.flavour == AsmFlavour::Loose, ""); Instruction const& instr = std::get(operation); // Disallow instructions returning multiple values (and DUP/SWAP) as expression. if ( @@ -348,7 +349,7 @@ Expression Parser::parseExpression() else if (holds_alternative(operation)) { // Instructions not taking arguments are allowed as expressions. - solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); + yulAssert(m_dialect.flavour == AsmFlavour::Loose, ""); Instruction& instr = std::get(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } @@ -356,7 +357,7 @@ Expression Parser::parseExpression() return std::get(operation); else { - solAssert(holds_alternative(operation), ""); + yulAssert(holds_alternative(operation), ""); return std::get(operation); } } @@ -539,7 +540,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) RecursionGuard recursionGuard(*this); if (holds_alternative(_initialOp)) { - solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); + yulAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); Instruction& instruction = std::get(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index 7814100aa..50e354f0a 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include @@ -41,8 +41,8 @@ using namespace yul; string AsmPrinter::operator()(yul::Instruction const& _instruction) const { - solAssert(!m_yul, ""); - solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); + yulAssert(!m_yul, ""); + yulAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_instruction.instruction).name); } @@ -51,10 +51,10 @@ string AsmPrinter::operator()(Literal const& _literal) const switch (_literal.kind) { case LiteralKind::Number: - solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); + yulAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); return _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: - solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal."); + yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal."); return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type); case LiteralKind::String: break; @@ -91,14 +91,14 @@ string AsmPrinter::operator()(Literal const& _literal) const string AsmPrinter::operator()(Identifier const& _identifier) const { - solAssert(!_identifier.name.empty(), "Invalid identifier."); + yulAssert(!_identifier.name.empty(), "Invalid identifier."); return _identifier.name.str(); } string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstruction) const { - solAssert(!m_yul, ""); - solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); + yulAssert(!m_yul, ""); + yulAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + @@ -116,21 +116,21 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) const string AsmPrinter::operator()(Label const& _label) const { - solAssert(!m_yul, ""); - solAssert(!_label.name.empty(), "Invalid label."); + yulAssert(!m_yul, ""); + yulAssert(!_label.name.empty(), "Invalid label."); return _label.name.str() + ":"; } string AsmPrinter::operator()(StackAssignment const& _assignment) const { - solAssert(!m_yul, ""); - solAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); + yulAssert(!m_yul, ""); + yulAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); return "=: " + (*this)(_assignment.variableName); } string AsmPrinter::operator()(Assignment const& _assignment) const { - solAssert(_assignment.variableNames.size() >= 1, ""); + yulAssert(_assignment.variableNames.size() >= 1, ""); string variables = (*this)(_assignment.variableNames.front()); for (size_t i = 1; i < _assignment.variableNames.size(); ++i) variables += ", " + (*this)(_assignment.variableNames[i]); @@ -156,7 +156,7 @@ string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) c string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) const { - solAssert(!_functionDefinition.name.empty(), "Invalid function name."); + yulAssert(!_functionDefinition.name.empty(), "Invalid function name."); string out = "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | boost::adaptors::transformed( @@ -191,7 +191,7 @@ string AsmPrinter::operator()(FunctionCall const& _functionCall) const string AsmPrinter::operator()(If const& _if) const { - solAssert(_if.condition, "Invalid if condition."); + yulAssert(_if.condition, "Invalid if condition."); string body = (*this)(_if.body); char delim = '\n'; if (body.find('\n') == string::npos) @@ -201,7 +201,7 @@ string AsmPrinter::operator()(If const& _if) const string AsmPrinter::operator()(Switch const& _switch) const { - solAssert(_switch.expression, "Invalid expression pointer."); + yulAssert(_switch.expression, "Invalid expression pointer."); string out = "switch " + std::visit(*this, *_switch.expression); for (auto const& _case: _switch.cases) { @@ -216,7 +216,7 @@ string AsmPrinter::operator()(Switch const& _switch) const string AsmPrinter::operator()(ForLoop const& _forLoop) const { - solAssert(_forLoop.condition, "Invalid for loop condition."); + yulAssert(_forLoop.condition, "Invalid for loop condition."); string pre = (*this)(_forLoop.pre); string condition = std::visit(*this, *_forLoop.condition); string post = (*this)(_forLoop.post); @@ -261,7 +261,7 @@ string AsmPrinter::operator()(Block const& _block) const string AsmPrinter::formatTypedName(TypedName _variable) const { - solAssert(!_variable.name.empty(), "Invalid variable name."); + yulAssert(!_variable.name.empty(), "Invalid variable name."); return _variable.name.str() + appendTypeName(_variable.type); } diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 3c940c92f..fb23a4628 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -23,9 +23,9 @@ #include #include #include +#include #include -#include #include @@ -88,7 +88,7 @@ bool ScopeFiller::operator()(FunctionDefinition const& _funDef) if (!(*this)(_funDef.body)) success = false; - solAssert(m_currentScope == &varScope, ""); + yulAssert(m_currentScope == &varScope, ""); m_currentScope = m_currentScope->superScope; return success; diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 7851101af..5ac28f63e 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -63,7 +63,7 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _ case AssemblyStack::Language::EWasm: return WasmDialect::instance(); } - solAssert(false, ""); + yulAssert(false, ""); return Dialect::yul(); } @@ -72,7 +72,7 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _ Scanner const& AssemblyStack::scanner() const { - solAssert(m_scanner, ""); + yulAssert(m_scanner, ""); return *m_scanner; } @@ -84,8 +84,8 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; - solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); + yulAssert(m_parserResult, ""); + yulAssert(m_parserResult->code, ""); return analyzeParsed(); } @@ -95,12 +95,12 @@ void AssemblyStack::optimize() if (!m_optimiserSettings.runYulOptimiser) return; - solAssert(m_analysisSuccessful, "Analysis was not successful."); + yulAssert(m_analysisSuccessful, "Analysis was not successful."); m_analysisSuccessful = false; - solAssert(m_parserResult, ""); + yulAssert(m_parserResult, ""); optimize(*m_parserResult, true); - solAssert(analyzeParsed(), "Invalid source code after optimization."); + yulAssert(analyzeParsed(), "Invalid source code after optimization."); } void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) @@ -122,14 +122,14 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) bool AssemblyStack::analyzeParsed() { - solAssert(m_parserResult, ""); + yulAssert(m_parserResult, ""); m_analysisSuccessful = analyzeParsed(*m_parserResult); return m_analysisSuccessful; } bool AssemblyStack::analyzeParsed(Object& _object) { - solAssert(_object.code, ""); + yulAssert(_object.code, ""); _object.analysisInfo = make_shared(); AsmAnalyzer analyzer( @@ -159,15 +159,15 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _o else if (m_language == AssemblyStack::Language::Yul) dialect = &EVMDialect::yulForEVM(m_evmVersion); else - solAssert(false, "Invalid language."); + yulAssert(false, "Invalid language."); EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize); } void AssemblyStack::optimize(Object& _object, bool _isCreation) { - solAssert(_object.code, ""); - solAssert(_object.analysisInfo, ""); + yulAssert(_object.code, ""); + yulAssert(_object.analysisInfo, ""); for (auto& subNode: _object.subObjects) if (auto subObject = dynamic_cast(subNode.get())) optimize(*subObject, false); @@ -186,10 +186,10 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation) MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { - solAssert(m_analysisSuccessful, ""); - solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); - solAssert(m_parserResult->analysisInfo, ""); + yulAssert(m_analysisSuccessful, ""); + yulAssert(m_parserResult, ""); + yulAssert(m_parserResult->code, ""); + yulAssert(m_parserResult->analysisInfo, ""); switch (_machine) { @@ -214,7 +214,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const } case Machine::eWasm: { - solAssert(m_language == Language::EWasm, ""); + yulAssert(m_language == Language::EWasm, ""); Dialect const& dialect = languageToDialect(m_language, EVMVersion{}); MachineAssemblyObject object; @@ -231,15 +231,15 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const string AssemblyStack::print() const { - solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); + yulAssert(m_parserResult, ""); + yulAssert(m_parserResult->code, ""); return m_parserResult->toString(m_language == Language::Yul) + "\n"; } shared_ptr AssemblyStack::parserResult() const { - solAssert(m_analysisSuccessful, "Analysis was not successful."); - solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); + yulAssert(m_analysisSuccessful, "Analysis was not successful."); + yulAssert(m_parserResult, ""); + yulAssert(m_parserResult->code, ""); return m_parserResult; } diff --git a/libyul/CompilabilityChecker.cpp b/libyul/CompilabilityChecker.cpp index 7c6fe9d78..228515b9b 100644 --- a/libyul/CompilabilityChecker.cpp +++ b/libyul/CompilabilityChecker.cpp @@ -41,7 +41,7 @@ map CompilabilityChecker::run( if (_dialect.flavour == AsmFlavour::Yul) return {}; - solAssert(_dialect.flavour == AsmFlavour::Strict, ""); + yulAssert(_dialect.flavour == AsmFlavour::Strict, ""); if (EVMDialect const* evmDialect = dynamic_cast(&_dialect)) { @@ -69,7 +69,7 @@ map CompilabilityChecker::run( } catch (StackTooDeepError const&) { - solAssert(!transform.stackErrors().empty(), "Got stack too deep exception that was not stored."); + yulAssert(!transform.stackErrors().empty(), "Got stack too deep exception that was not stored."); } std::map functions; diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 2e201046f..683b8022b 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -112,7 +112,7 @@ shared_ptr ObjectParser::parseBlock() void ObjectParser::parseData(Object& _containingObject) { - solAssert( + yulAssert( currentToken() == Token::Identifier && currentLiteral() == "data", "parseData called on wrong input." ); diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index ca0aeb86a..9022dadad 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -118,19 +118,19 @@ void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId) void EthAssemblyAdapter::appendBeginsub(LabelID, int) { // TODO we could emulate that, though - solAssert(false, "BEGINSUB not implemented for EVM 1.0"); + yulAssert(false, "BEGINSUB not implemented for EVM 1.0"); } void EthAssemblyAdapter::appendJumpsub(LabelID, int, int) { // TODO we could emulate that, though - solAssert(false, "JUMPSUB not implemented for EVM 1.0"); + yulAssert(false, "JUMPSUB not implemented for EVM 1.0"); } void EthAssemblyAdapter::appendReturnsub(int, int) { // TODO we could emulate that, though - solAssert(false, "RETURNSUB not implemented for EVM 1.0"); + yulAssert(false, "RETURNSUB not implemented for EVM 1.0"); } void EthAssemblyAdapter::appendAssemblySize() @@ -174,7 +174,7 @@ AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data) EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(eth::AssemblyItem const& _tag) { u256 id = _tag.data(); - solAssert(id <= std::numeric_limits::max(), "Tag id too large."); + yulAssert(id <= std::numeric_limits::max(), "Tag id too large."); return LabelID(id); } @@ -207,11 +207,11 @@ void CodeGenerator::assemble( } catch (StackTooDeepError const& _e) { - solAssert( + yulAssert( false, "Stack too deep when compiling inline assembly" + (_e.comment() ? ": " + *_e.comment() : ".") ); } - solAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); + yulAssert(transform.stackErrors().empty(), "Stack errors present but not thrown."); } diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index 65d22cbd8..6b268e67d 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -19,11 +19,10 @@ */ #include +#include #include -#include - using namespace std; using namespace dev; using namespace dev::eth; @@ -65,7 +64,7 @@ void EVMAssembly::appendLabel(LabelID _labelId) void EVMAssembly::appendLabelReference(LabelID _labelId) { - solAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); + yulAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); // @TODO we now always use labelReferenceSize for all labels, it could be shortened // for some of them. appendInstruction(dev::eth::pushInstruction(labelReferenceSize)); @@ -81,7 +80,7 @@ EVMAssembly::LabelID EVMAssembly::newLabelId() AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) { - solAssert(!_name.empty(), ""); + yulAssert(!_name.empty(), ""); if (!m_namedLabels.count(_name)) m_namedLabels[_name] = newLabelId(); return m_namedLabels[_name]; @@ -89,12 +88,12 @@ AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) void EVMAssembly::appendLinkerSymbol(string const&) { - solAssert(false, "Linker symbols not yet implemented."); + yulAssert(false, "Linker symbols not yet implemented."); } void EVMAssembly::appendJump(int _stackDiffAfter) { - solAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); + yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); appendInstruction(dev::eth::Instruction::JUMP); m_stackHeight += _stackDiffAfter; } @@ -131,8 +130,8 @@ void EVMAssembly::appendJumpToIf(LabelID _labelId) void EVMAssembly::appendBeginsub(LabelID _labelId, int _arguments) { - solAssert(m_evm15, "BEGINSUB used for EVM 1.0"); - solAssert(_arguments >= 0, ""); + yulAssert(m_evm15, "BEGINSUB used for EVM 1.0"); + yulAssert(_arguments >= 0, ""); setLabelToCurrentPosition(_labelId); m_bytecode.push_back(uint8_t(dev::eth::Instruction::BEGINSUB)); m_stackHeight += _arguments; @@ -140,8 +139,8 @@ void EVMAssembly::appendBeginsub(LabelID _labelId, int _arguments) void EVMAssembly::appendJumpsub(LabelID _labelId, int _arguments, int _returns) { - solAssert(m_evm15, "JUMPSUB used for EVM 1.0"); - solAssert(_arguments >= 0 && _returns >= 0, ""); + yulAssert(m_evm15, "JUMPSUB used for EVM 1.0"); + yulAssert(_arguments >= 0 && _returns >= 0, ""); m_bytecode.push_back(uint8_t(dev::eth::Instruction::JUMPSUB)); appendLabelReferenceInternal(_labelId); m_stackHeight += _returns - _arguments; @@ -149,8 +148,8 @@ void EVMAssembly::appendJumpsub(LabelID _labelId, int _arguments, int _returns) void EVMAssembly::appendReturnsub(int _returns, int _stackDiffAfter) { - solAssert(m_evm15, "RETURNSUB used for EVM 1.0"); - solAssert(_returns >= 0, ""); + yulAssert(m_evm15, "RETURNSUB used for EVM 1.0"); + yulAssert(_returns >= 0, ""); m_bytecode.push_back(uint8_t(dev::eth::Instruction::RETURNSUB)); m_stackHeight += _stackDiffAfter - _returns; } @@ -164,9 +163,9 @@ eth::LinkerObject EVMAssembly::finalize() for (auto const& ref: m_labelReferences) { size_t referencePos = ref.first; - solAssert(m_labelPositions.count(ref.second), ""); + yulAssert(m_labelPositions.count(ref.second), ""); size_t labelPos = m_labelPositions.at(ref.second); - solAssert(labelPos != size_t(-1), "Undefined but allocated label used."); + yulAssert(labelPos != size_t(-1), "Undefined but allocated label used."); updateReference(referencePos, labelReferenceSize, u256(labelPos)); } @@ -177,8 +176,8 @@ eth::LinkerObject EVMAssembly::finalize() void EVMAssembly::setLabelToCurrentPosition(LabelID _labelId) { - solAssert(m_labelPositions.count(_labelId), "Label not found."); - solAssert(m_labelPositions[_labelId] == size_t(-1), "Label already set."); + yulAssert(m_labelPositions.count(_labelId), "Label not found."); + yulAssert(m_labelPositions[_labelId] == size_t(-1), "Label already set."); m_labelPositions[_labelId] = m_bytecode.size(); } @@ -197,29 +196,29 @@ void EVMAssembly::appendAssemblySize() pair, AbstractAssembly::SubID> EVMAssembly::createSubAssembly() { - solAssert(false, "Sub assemblies not implemented."); + yulAssert(false, "Sub assemblies not implemented."); return {}; } void EVMAssembly::appendDataOffset(AbstractAssembly::SubID) { - solAssert(false, "Data not implemented."); + yulAssert(false, "Data not implemented."); } void EVMAssembly::appendDataSize(AbstractAssembly::SubID) { - solAssert(false, "Data not implemented."); + yulAssert(false, "Data not implemented."); } AbstractAssembly::SubID EVMAssembly::appendData(bytes const&) { - solAssert(false, "Data not implemented."); + yulAssert(false, "Data not implemented."); } void EVMAssembly::updateReference(size_t pos, size_t size, u256 value) { - solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, ""); - solAssert(value < (u256(1) << (8 * size)), ""); + yulAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, ""); + yulAssert(value < (u256(1) << (8 * size)), ""); for (size_t i = 0; i < size; i++) m_bytecode[pos + i] = uint8_t((value >> (8 * (size - i - 1))) & 0xff); } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 8e04969c1..f3d76b002 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -44,9 +44,9 @@ void VariableReferenceCounter::operator()(FunctionDefinition const& _function) { Scope* originalScope = m_scope; - solAssert(m_info.virtualBlocks.at(&_function), ""); + yulAssert(m_info.virtualBlocks.at(&_function), ""); m_scope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); - solAssert(m_scope, "Variable scope does not exist."); + yulAssert(m_scope, "Variable scope does not exist."); for (auto const& v: _function.returnVariables) increaseRefIfFound(v.name); @@ -131,7 +131,7 @@ void CodeTransform::decreaseReference(YulString, Scope::Variable const& _var) return; unsigned& ref = m_context->variableReferences.at(&_var); - solAssert(ref >= 1, ""); + yulAssert(ref >= 1, ""); --ref; if (ref == 0) m_variablesScheduledForDeletion.insert(&_var); @@ -157,7 +157,7 @@ void CodeTransform::freeUnusedVariables() while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1)) { - solAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), ""); + yulAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), ""); m_assembly.appendInstruction(dev::eth::Instruction::POP); --m_stackAdjustment; } @@ -165,8 +165,8 @@ void CodeTransform::freeUnusedVariables() void CodeTransform::deleteVariable(Scope::Variable const& _var) { - solAssert(m_allowStackOpt, ""); - solAssert(m_context->variableStackHeights.count(&_var) > 0, ""); + yulAssert(m_allowStackOpt, ""); + yulAssert(m_context->variableStackHeights.count(&_var) > 0, ""); m_unusedStackSlots.insert(m_context->variableStackHeights[&_var]); m_context->variableStackHeights.erase(&_var); m_context->variableReferences.erase(&_var); @@ -175,7 +175,7 @@ void CodeTransform::deleteVariable(Scope::Variable const& _var) void CodeTransform::operator()(VariableDeclaration const& _varDecl) { - solAssert(m_scope, ""); + yulAssert(m_scope, ""); int const numVariables = _varDecl.variables.size(); int height = m_assembly.stackHeight(); @@ -254,7 +254,7 @@ void CodeTransform::operator()(Assignment const& _assignment) void CodeTransform::operator()(StackAssignment const& _assignment) { - solAssert(!m_allowStackOpt, ""); + yulAssert(!m_allowStackOpt, ""); m_assembly.setSourceLocation(_assignment.location); generateAssignment(_assignment.variableName); checkStackHeight(&_assignment); @@ -269,10 +269,10 @@ void CodeTransform::operator()(ExpressionStatement const& _statement) void CodeTransform::operator()(Label const& _label) { - solAssert(!m_allowStackOpt, ""); + yulAssert(!m_allowStackOpt, ""); m_assembly.setSourceLocation(_label.location); - solAssert(m_scope, ""); - solAssert(m_scope->identifiers.count(_label.name), ""); + yulAssert(m_scope, ""); + yulAssert(m_scope->identifiers.count(_label.name), ""); Scope::Label& label = std::get(m_scope->identifiers.at(_label.name)); m_assembly.appendLabel(labelID(label)); checkStackHeight(&_label); @@ -280,7 +280,7 @@ void CodeTransform::operator()(Label const& _label) void CodeTransform::operator()(FunctionCall const& _call) { - solAssert(m_scope, ""); + yulAssert(m_scope, ""); if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) { @@ -302,13 +302,13 @@ void CodeTransform::operator()(FunctionCall const& _call) } Scope::Function* function = nullptr; - solAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{ - [=](Scope::Variable&) { solAssert(false, "Expected function name."); }, - [=](Scope::Label&) { solAssert(false, "Expected function name."); }, + yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{ + [=](Scope::Variable&) { yulAssert(false, "Expected function name."); }, + [=](Scope::Label&) { yulAssert(false, "Expected function name."); }, [&](Scope::Function& _function) { function = &_function; } }), "Function name not found."); - solAssert(function, ""); - solAssert(function->arguments.size() == _call.arguments.size(), ""); + yulAssert(function, ""); + yulAssert(function->arguments.size() == _call.arguments.size(), ""); for (auto const& arg: _call.arguments | boost::adaptors::reversed) visitExpression(arg); m_assembly.setSourceLocation(_call.location); @@ -334,12 +334,12 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) bool const isJumpI = _instruction.instruction == dev::eth::Instruction::JUMPI; if (isJumpI) { - solAssert(_instruction.arguments.size() == 2, ""); + yulAssert(_instruction.arguments.size() == 2, ""); visitExpression(_instruction.arguments.at(1)); } else { - solAssert(_instruction.arguments.size() == 1, ""); + yulAssert(_instruction.arguments.size() == 1, ""); } m_assembly.setSourceLocation(_instruction.location); auto label = labelFromIdentifier(std::get(_instruction.arguments.at(0))); @@ -362,7 +362,7 @@ void CodeTransform::operator()(Identifier const& _identifier) { m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. - solAssert(m_scope, ""); + yulAssert(m_scope, ""); if (m_scope->lookup(_identifier.name, GenericVisitor{ [=](Scope::Variable& _var) { @@ -381,13 +381,13 @@ void CodeTransform::operator()(Identifier const& _identifier) }, [=](Scope::Function&) { - solAssert(false, "Function not removed during desugaring."); + yulAssert(false, "Function not removed during desugaring."); } })) { return; } - solAssert( + yulAssert( m_identifierAccess.generateCode, "Identifier not found and no external access available." ); @@ -405,9 +405,9 @@ void CodeTransform::operator()(Literal const& _literal) void CodeTransform::operator()(yul::Instruction const& _instruction) { - solAssert(!m_allowStackOpt, ""); - solAssert(!m_evm15 || _instruction.instruction != dev::eth::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); - solAssert(!m_evm15 || _instruction.instruction != dev::eth::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); + yulAssert(!m_allowStackOpt, ""); + yulAssert(!m_evm15 || _instruction.instruction != dev::eth::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); + yulAssert(!m_evm15 || _instruction.instruction != dev::eth::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); m_assembly.setSourceLocation(_instruction.location); m_assembly.appendInstruction(_instruction.instruction); checkStackHeight(&_instruction); @@ -442,7 +442,7 @@ void CodeTransform::operator()(Switch const& _switch) m_assembly.setSourceLocation(c.location); AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); caseBodies[&c] = bodyLabel; - solAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); + yulAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); m_assembly.appendInstruction(dev::eth::dupInstruction(2)); m_assembly.appendInstruction(dev::eth::Instruction::EQ); m_assembly.appendJumpToIf(bodyLabel); @@ -476,15 +476,15 @@ void CodeTransform::operator()(Switch const& _switch) void CodeTransform::operator()(FunctionDefinition const& _function) { - solAssert(m_scope, ""); - solAssert(m_scope->identifiers.count(_function.name), ""); + yulAssert(m_scope, ""); + yulAssert(m_scope->identifiers.count(_function.name), ""); Scope::Function& function = std::get(m_scope->identifiers.at(_function.name)); int const localStackAdjustment = m_evm15 ? 0 : 1; int height = localStackAdjustment; - solAssert(m_info.scopes.at(&_function.body), ""); + yulAssert(m_info.scopes.at(&_function.body), ""); Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); - solAssert(varScope, ""); + yulAssert(varScope, ""); for (auto const& v: _function.parameters | boost::adaptors::reversed) { auto& var = std::get(varScope->identifiers.at(v.name)); @@ -582,7 +582,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) swap(stackLayout[stackLayout.back()], stackLayout.back()); } for (int i = 0; size_t(i) < stackLayout.size(); ++i) - solAssert(i == stackLayout[i], "Error reshuffling stack."); + yulAssert(i == stackLayout[i], "Error reshuffling stack."); } } if (m_evm15) @@ -683,15 +683,15 @@ AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _ { AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); if (!m_scope->lookup(_identifier.name, GenericVisitor{ - [=](Scope::Variable&) { solAssert(false, "Expected label"); }, + [=](Scope::Variable&) { yulAssert(false, "Expected label"); }, [&](Scope::Label& _label) { label = labelID(_label); }, - [=](Scope::Function&) { solAssert(false, "Expected label"); } + [=](Scope::Function&) { yulAssert(false, "Expected label"); } })) { - solAssert(false, "Identifier not found."); + yulAssert(false, "Identifier not found."); } return label; } @@ -759,15 +759,15 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight freeUnusedVariables(); // pop variables - solAssert(m_info.scopes.at(&_block).get() == m_scope, ""); + yulAssert(m_info.scopes.at(&_block).get() == m_scope, ""); for (auto const& id: m_scope->identifiers) if (holds_alternative(id.second)) { Scope::Variable const& var = std::get(id.second); if (m_allowStackOpt) { - solAssert(!m_context->variableStackHeights.count(&var), ""); - solAssert(!m_context->variableReferences.count(&var), ""); + yulAssert(!m_context->variableStackHeights.count(&var), ""); + yulAssert(!m_context->variableReferences.count(&var), ""); m_stackAdjustment++; } else @@ -775,20 +775,20 @@ void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight } int deposit = m_assembly.stackHeight() - blockStartStackHeight; - solAssert(deposit == 0, "Invalid stack height at end of block: " + to_string(deposit)); + yulAssert(deposit == 0, "Invalid stack height at end of block: " + to_string(deposit)); checkStackHeight(&_block); } void CodeTransform::generateMultiAssignment(vector const& _variableNames) { - solAssert(m_scope, ""); + yulAssert(m_scope, ""); for (auto const& variableName: _variableNames | boost::adaptors::reversed) generateAssignment(variableName); } void CodeTransform::generateAssignment(Identifier const& _variableName) { - solAssert(m_scope, ""); + yulAssert(m_scope, ""); if (auto var = m_scope->lookup(_variableName.name)) { Scope::Variable const& _var = std::get(*var); @@ -799,7 +799,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } else { - solAssert( + yulAssert( m_identifierAccess.generateCode, "Identifier not found and no external access available." ); @@ -809,9 +809,9 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) int CodeTransform::variableHeightDiff(Scope::Variable const& _var, YulString _varName, bool _forSwap) { - solAssert(m_context->variableStackHeights.count(&_var), ""); + yulAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; - solAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable."); + yulAssert(heightDiff > (_forSwap ? 1 : 0), "Negative stack difference for variable."); int limit = _forSwap ? 17 : 16; if (heightDiff > limit) { @@ -830,15 +830,15 @@ int CodeTransform::variableHeightDiff(Scope::Variable const& _var, YulString _va void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const { - solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); + yulAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } void CodeTransform::checkStackHeight(void const* _astElement) const { - solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); + yulAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); int stackHeightInAnalysis = m_info.stackHeightInfo.at(_astElement); int stackHeightInCodegen = m_assembly.stackHeight() - m_stackAdjustment; - solAssert( + yulAssert( stackHeightInAnalysis == stackHeightInCodegen, "Stack height mismatch between analysis and code generation phase: Analysis: " + to_string(stackHeightInAnalysis) + diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index 4618d4e54..e2a34ef20 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -19,11 +19,10 @@ */ #include +#include #include -#include - using namespace std; using namespace dev; using namespace langutil; @@ -47,7 +46,7 @@ void NoOutputAssembly::appendLabel(LabelID) void NoOutputAssembly::appendLabelReference(LabelID) { - solAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); + yulAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); appendInstruction(dev::eth::pushInstruction(1)); } @@ -63,12 +62,12 @@ AbstractAssembly::LabelID NoOutputAssembly::namedLabel(string const&) void NoOutputAssembly::appendLinkerSymbol(string const&) { - solAssert(false, "Linker symbols not yet implemented."); + yulAssert(false, "Linker symbols not yet implemented."); } void NoOutputAssembly::appendJump(int _stackDiffAfter) { - solAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); + yulAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); appendInstruction(dev::eth::Instruction::JUMP); m_stackHeight += _stackDiffAfter; } @@ -97,22 +96,22 @@ void NoOutputAssembly::appendJumpToIf(LabelID _labelId) void NoOutputAssembly::appendBeginsub(LabelID, int _arguments) { - solAssert(m_evm15, "BEGINSUB used for EVM 1.0"); - solAssert(_arguments >= 0, ""); + yulAssert(m_evm15, "BEGINSUB used for EVM 1.0"); + yulAssert(_arguments >= 0, ""); m_stackHeight += _arguments; } void NoOutputAssembly::appendJumpsub(LabelID, int _arguments, int _returns) { - solAssert(m_evm15, "JUMPSUB used for EVM 1.0"); - solAssert(_arguments >= 0 && _returns >= 0, ""); + yulAssert(m_evm15, "JUMPSUB used for EVM 1.0"); + yulAssert(_arguments >= 0 && _returns >= 0, ""); m_stackHeight += _returns - _arguments; } void NoOutputAssembly::appendReturnsub(int _returns, int _stackDiffAfter) { - solAssert(m_evm15, "RETURNSUB used for EVM 1.0"); - solAssert(_returns >= 0, ""); + yulAssert(m_evm15, "RETURNSUB used for EVM 1.0"); + yulAssert(_returns >= 0, ""); m_stackHeight += _stackDiffAfter - _returns; } @@ -123,7 +122,7 @@ void NoOutputAssembly::appendAssemblySize() pair, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly() { - solAssert(false, "Sub assemblies not implemented."); + yulAssert(false, "Sub assemblies not implemented."); return {}; } From 9140a7d64e7bf5ca145dab6a19be1e0c55c41c7c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Dec 2019 19:05:58 +0100 Subject: [PATCH 111/130] Support catching YulException nicely in StandardCompiler --- libsolidity/interface/StandardCompiler.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 921e90231..8e226a7df 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -817,6 +818,16 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting "Unimplemented feature (" + _exception.lineInfo() + ")" )); } + catch (yul::YulException const& _exception) + { + errors.append(formatErrorWithException( + _exception, + false, + "YulException", + "general", + "Yul exception" + )); + } catch (Exception const& _exception) { errors.append(formatError( From c644f43cde2def03b7e4ebb2f9d941c1561a241a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Dec 2019 19:06:04 +0100 Subject: [PATCH 112/130] Update test expection in use_stack_optimization with YulException --- test/libsolidity/StandardCompiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index e9d8613a4..f737ac99d 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -1199,7 +1199,9 @@ BOOST_AUTO_TEST_CASE(use_stack_optimization) result = compiler.compile(parsedInput); BOOST_REQUIRE(result["errors"].isArray()); BOOST_CHECK(result["errors"][0]["severity"] == "error"); - BOOST_CHECK(result["errors"][0]["type"] == "InternalCompilerError"); + BOOST_REQUIRE(result["errors"][0]["message"].isString()); + BOOST_CHECK(result["errors"][0]["message"].asString().find("Stack too deep when compiling inline assembly") != std::string::npos); + BOOST_CHECK(result["errors"][0]["type"] == "YulException"); } BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard) From 5337f587673ae7b53c9dd7a840bce78d09ec3c66 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 3 Dec 2019 00:24:04 +0100 Subject: [PATCH 113/130] Update to Z3 4.8.7 --- .circleci/config.yml | 2 +- .circleci/docker/Dockerfile.clang.ubuntu1904 | 2 +- .circleci/osx_install_dependencies.sh | 14 +++++----- .../smtCheckerTests/invariants/loop_array.sol | 19 -------------- ..._loop_array_assignment_storage_storage.sol | 26 +++++++++++++++++++ 5 files changed, 35 insertions(+), 28 deletions(-) delete mode 100644 test/libsolidity/smtCheckerTests/invariants/loop_array.sol create mode 100644 test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol diff --git a/.circleci/config.yml b/.circleci/config.yml index 87236581a..2139fa94d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ version: 2.1 parameters: docker-image-rev: type: string - default: "2" + default: "3" defaults: diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904 index 3678afbed..0306dcf68 100644 --- a/.circleci/docker/Dockerfile.clang.ubuntu1904 +++ b/.circleci/docker/Dockerfile.clang.ubuntu1904 @@ -62,7 +62,7 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git rm -rf /usr/src/boost # Z3 -RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \ +RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \ /usr/src/z3; \ cd /usr/src/z3; \ python scripts/mk_make.py --prefix=/usr ; \ diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index fadff605e..59e7c2234 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -43,13 +43,13 @@ then ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 - wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-osx-10.14.6.zip - unzip z3-4.8.6-x64-osx-10.14.6.zip - rm -f z3-4.8.6-x64-osx-10.14.6.zip - cp z3-4.8.6-x64-osx-10.14.6/bin/libz3.a /usr/local/lib - cp z3-4.8.6-x64-osx-10.14.6/bin/z3 /usr/local/bin - cp z3-4.8.6-x64-osx-10.14.6/include/* /usr/local/include - rm -rf z3-4.8.6-x64-osx-10.14.6 + wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.7/z3-4.8.7-x64-osx-10.14.6.zip + unzip z3-4.8.7-x64-osx-10.14.6.zip + rm -f z3-4.8.7-x64-osx-10.14.6.zip + cp z3-4.8.7-x64-osx-10.14.6/bin/libz3.a /usr/local/lib + cp z3-4.8.7-x64-osx-10.14.6/bin/z3 /usr/local/bin + cp z3-4.8.7-x64-osx-10.14.6/include/* /usr/local/include + rm -rf z3-4.8.7-x64-osx-10.14.6 # evmone wget https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_array.sol b/test/libsolidity/smtCheckerTests/invariants/loop_array.sol deleted file mode 100644 index 0f6937d36..000000000 --- a/test/libsolidity/smtCheckerTests/invariants/loop_array.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma experimental SMTChecker; - -contract Simple { - uint[] a; - function f(uint n) public { - uint i; - while (i < n) - { - a[i] = i; - ++i; - } - require(n > 1); - // Assertion is safe but current solver version cannot solve it. - // Keep test for next solver release. - assert(a[n-1] > a[n-2]); - } -} -// ---- -// Warning: (273-296): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol new file mode 100644 index 000000000..92d1ded3e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract LoopFor2 { + uint[] b; + uint[] c; + + function testUnboundedForLoop(uint n) public { + b[0] = 900; + uint[] storage a = b; + require(n > 0 && n < 100); + uint i; + while (i < n) { + b[i] = i + 1; + c[i] = b[i]; + ++i; + } + // Fails as false positive. + assert(b[0] == c[0]); + assert(a[0] == 900); + assert(b[0] == 900); + } +} +// ---- +// Warning: (296-316): Assertion violation happens here +// Warning: (320-339): Assertion violation happens here +// Warning: (343-362): Assertion violation happens here From bbeb093abf5048f134aca8d90b16c4a9d7f39679 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 2 Dec 2019 12:33:24 +0100 Subject: [PATCH 114/130] Detect loops in call graph generator. --- Changelog.md | 1 + libyul/optimiser/CallGraphGenerator.cpp | 17 ++++--- libyul/optimiser/CallGraphGenerator.h | 13 +++++- libyul/optimiser/Semantics.cpp | 45 +++++++++++++++++-- libyul/optimiser/Semantics.h | 3 +- .../functionSideEffects/cyclic_graph.yul | 6 +-- .../doubly_recursive_function.yul | 4 +- .../recursive_function.yul | 2 +- test/libyul/functionSideEffects/with_loop.yul | 9 ++++ .../fullSuite/abi_example1.yul | 19 ++++---- .../fullSuite/no_move_loop_orig.yul | 24 ++++++++++ .../loopInvariantCodeMotion/no_move_loop.yul | 29 ++++++++++++ .../no_move_recursive_function.yul | 26 +++++++++++ 13 files changed, 169 insertions(+), 29 deletions(-) create mode 100644 test/libyul/functionSideEffects/with_loop.yul create mode 100644 test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul diff --git a/Changelog.md b/Changelog.md index af170ed01..b95736856 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. * SMTChecker: Fix internal error when using arrays or mappings of functions. * SMTChecker: Fix internal error in array of structs type. + * Yul: Consider infinite loops and recursion to be not removable. diff --git a/libyul/optimiser/CallGraphGenerator.cpp b/libyul/optimiser/CallGraphGenerator.cpp index 0c11464ee..b1dc62f97 100644 --- a/libyul/optimiser/CallGraphGenerator.cpp +++ b/libyul/optimiser/CallGraphGenerator.cpp @@ -29,7 +29,7 @@ using namespace std; using namespace dev; using namespace yul; -map> CallGraphGenerator::callGraph(Block const& _ast) +CallGraph CallGraphGenerator::callGraph(Block const& _ast) { CallGraphGenerator gen; gen(_ast); @@ -40,28 +40,33 @@ void CallGraphGenerator::operator()(FunctionalInstruction const& _functionalInst { string name = dev::eth::instructionInfo(_functionalInstruction.instruction).name; std::transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); - m_callGraph[m_currentFunction].insert(YulString{name}); + m_callGraph.functionCalls[m_currentFunction].insert(YulString{name}); ASTWalker::operator()(_functionalInstruction); } void CallGraphGenerator::operator()(FunctionCall const& _functionCall) { - m_callGraph[m_currentFunction].insert(_functionCall.functionName.name); + m_callGraph.functionCalls[m_currentFunction].insert(_functionCall.functionName.name); ASTWalker::operator()(_functionCall); } +void CallGraphGenerator::operator()(ForLoop const&) +{ + m_callGraph.functionsWithLoops.insert(m_currentFunction); +} + void CallGraphGenerator::operator()(FunctionDefinition const& _functionDefinition) { YulString previousFunction = m_currentFunction; m_currentFunction = _functionDefinition.name; - yulAssert(m_callGraph.count(m_currentFunction) == 0, ""); - m_callGraph[m_currentFunction] = {}; + yulAssert(m_callGraph.functionCalls.count(m_currentFunction) == 0, ""); + m_callGraph.functionCalls[m_currentFunction] = {}; ASTWalker::operator()(_functionDefinition); m_currentFunction = previousFunction; } CallGraphGenerator::CallGraphGenerator() { - m_callGraph[YulString{}] = {}; + m_callGraph.functionCalls[YulString{}] = {}; } diff --git a/libyul/optimiser/CallGraphGenerator.h b/libyul/optimiser/CallGraphGenerator.h index 4dc420900..fb7e8d52c 100644 --- a/libyul/optimiser/CallGraphGenerator.h +++ b/libyul/optimiser/CallGraphGenerator.h @@ -31,25 +31,34 @@ namespace yul { +struct CallGraph +{ + std::map> functionCalls; + std::set functionsWithLoops; +}; + /** * Specific AST walker that generates the call graph. * + * It also generates information about which functions contain for loops. + * * The outermost (non-function) context is denoted by the empty string. */ class CallGraphGenerator: public ASTWalker { public: - static std::map> callGraph(Block const& _ast); + static CallGraph callGraph(Block const& _ast); using ASTWalker::operator(); void operator()(FunctionalInstruction const& _functionalInstruction) override; void operator()(FunctionCall const& _functionCall) override; + void operator()(ForLoop const& _forLoop) override; void operator()(FunctionDefinition const& _functionDefinition) override; private: CallGraphGenerator(); - std::map> m_callGraph; + CallGraph m_callGraph; /// The name of the function we are currently visiting during traversal. YulString m_currentFunction; }; diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 62aaa0e0f..0d79e09c5 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -108,11 +108,44 @@ void MSizeFinder::operator()(FunctionCall const& _functionCall) map SideEffectsPropagator::sideEffects( Dialect const& _dialect, - map> const& _directCallGraph + CallGraph const& _directCallGraph ) { + // Any loop currently makes a function non-movable, because + // it could be a non-terminating loop. + // The same is true for any function part of a call cycle. + // In the future, we should refine that, because the property + // is actually a bit different from "not movable". + map ret; - for (auto const& call: _directCallGraph) + for (auto const& function: _directCallGraph.functionsWithLoops) + { + ret[function].movable = false; + ret[function].sideEffectFree = false; + ret[function].sideEffectFreeIfNoMSize = false; + } + + // Detect recursive functions. + for (auto const& call: _directCallGraph.functionCalls) + { + // TODO we could shortcut the search as soon as we find a + // function that has as bad side-effects as we can + // ever achieve via recursion. + auto search = [&](YulString const& _functionName, CycleDetector& _cycleDetector, size_t) { + for (auto const& callee: _directCallGraph.functionCalls.at(_functionName)) + if (!_dialect.builtin(callee)) + if (_cycleDetector.run(callee)) + return; + }; + if (CycleDetector(search).run(call.first)) + { + ret[call.first].movable = false; + ret[call.first].sideEffectFree = false; + ret[call.first].sideEffectFreeIfNoMSize = false; + } + } + + for (auto const& call: _directCallGraph.functionCalls) { YulString funName = call.first; SideEffects sideEffects; @@ -123,11 +156,15 @@ map SideEffectsPropagator::sideEffects( if (BuiltinFunction const* f = _dialect.builtin(_function)) sideEffects += f->sideEffects; else - for (YulString callee: _directCallGraph.at(_function)) + { + if (ret.count(_function)) + sideEffects += ret[_function]; + for (YulString callee: _directCallGraph.functionCalls.at(_function)) _addChild(callee); + } } ); - ret[funName] = sideEffects; + ret[funName] += sideEffects; } return ret; } diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 8953ab321..8c266910a 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -86,7 +87,7 @@ class SideEffectsPropagator public: static std::map sideEffects( Dialect const& _dialect, - std::map> const& _directCallGraph + CallGraph const& _directCallGraph ); }; diff --git a/test/libyul/functionSideEffects/cyclic_graph.yul b/test/libyul/functionSideEffects/cyclic_graph.yul index 742d74087..0b5bb6d7f 100644 --- a/test/libyul/functionSideEffects/cyclic_graph.yul +++ b/test/libyul/functionSideEffects/cyclic_graph.yul @@ -5,6 +5,6 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize -// b: movable, sideEffectFree, sideEffectFreeIfNoMSize -// c: movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: +// b: +// c: diff --git a/test/libyul/functionSideEffects/doubly_recursive_function.yul b/test/libyul/functionSideEffects/doubly_recursive_function.yul index cbc594f38..82a543b81 100644 --- a/test/libyul/functionSideEffects/doubly_recursive_function.yul +++ b/test/libyul/functionSideEffects/doubly_recursive_function.yul @@ -4,5 +4,5 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize -// b: movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: +// b: diff --git a/test/libyul/functionSideEffects/recursive_function.yul b/test/libyul/functionSideEffects/recursive_function.yul index cacefefb4..05e474eb0 100644 --- a/test/libyul/functionSideEffects/recursive_function.yul +++ b/test/libyul/functionSideEffects/recursive_function.yul @@ -3,4 +3,4 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize +// a: diff --git a/test/libyul/functionSideEffects/with_loop.yul b/test/libyul/functionSideEffects/with_loop.yul new file mode 100644 index 000000000..a1deb26f5 --- /dev/null +++ b/test/libyul/functionSideEffects/with_loop.yul @@ -0,0 +1,9 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + pop(f()) +} +// ---- +// : +// f: +// g: diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 0d37cf1b9..ab1ebb10d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -477,16 +477,15 @@ // pos := add(pos, 0x60) // } // let _3 := mload(64) -// let _4 := mload(0x20) -// if slt(sub(_3, _4), 128) { revert(_1, _1) } -// let offset := calldataload(add(_4, 64)) -// let _5 := 0xffffffffffffffff -// if gt(offset, _5) { revert(_1, _1) } -// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_4, offset), _3) -// let offset_1 := calldataload(add(_4, 0x60)) -// if gt(offset_1, _5) { revert(_1, _1) } -// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3) -// sstore(calldataload(_4), calldataload(add(_4, 0x20))) +// if slt(sub(_3, length), 128) { revert(_1, _1) } +// let offset := calldataload(add(length, 64)) +// let _4 := 0xffffffffffffffff +// if gt(offset, _4) { revert(_1, _1) } +// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(length, offset), _3) +// let offset_1 := calldataload(add(length, 0x60)) +// if gt(offset_1, _4) { revert(_1, _1) } +// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(length, offset_1), _3) +// sstore(calldataload(length), calldataload(add(length, 0x20))) // sstore(value2, value3) // sstore(_1, pos) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul new file mode 100644 index 000000000..5562bd85e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul @@ -0,0 +1,24 @@ +{ + for {} msize() { + function foo_s_0() -> x_1 { for {} caller() {} {} } + // x_3 used to be a movable loop invariant because `foo_s_0()` used to be movable + let x_3 := foo_s_0() + mstore(192, x_3) + } + {} +} +// ==== +// step: fullSuite +// ---- +// { +// { +// for { } +// 1 +// { +// for { } iszero(iszero(caller())) { } +// { } +// mstore(192, 0) +// } +// { if iszero(msize()) { break } } +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul new file mode 100644 index 000000000..7f842bbdd --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := f() + let q := g() + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := f() +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul new file mode 100644 index 000000000..5f3eeb7ac --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul @@ -0,0 +1,26 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := g() } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := f() + let q := g() + } +} +// ==== +// step: loopInvariantCodeMotion +// ---- +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := g() } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := f() +// let q := g() +// } +// } From f713b7e158bc54ab86fa66f7ceeeb6348e82d67e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 3 Dec 2019 08:54:46 +0100 Subject: [PATCH 115/130] Remove trailing whitespace in test. --- test/libyul/FunctionSideEffects.cpp | 2 +- test/libyul/functionSideEffects/cyclic_graph.yul | 6 +++--- .../functionSideEffects/doubly_recursive_function.yul | 4 ++-- test/libyul/functionSideEffects/recursive_function.yul | 2 +- test/libyul/functionSideEffects/with_loop.yul | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 6ae6df8ab..ba0a17599 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -87,7 +87,7 @@ TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _l m_obtainedResult.clear(); for (auto const& fun: functionSideEffectsStr) - m_obtainedResult += fun.first + ": " + fun.second + "\n"; + m_obtainedResult += fun.first + ":" + (fun.second.empty() ? "" : " ") + fun.second + "\n"; if (m_expectation != m_obtainedResult) { diff --git a/test/libyul/functionSideEffects/cyclic_graph.yul b/test/libyul/functionSideEffects/cyclic_graph.yul index 0b5bb6d7f..58eba0acf 100644 --- a/test/libyul/functionSideEffects/cyclic_graph.yul +++ b/test/libyul/functionSideEffects/cyclic_graph.yul @@ -5,6 +5,6 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: -// c: +// a: +// b: +// c: diff --git a/test/libyul/functionSideEffects/doubly_recursive_function.yul b/test/libyul/functionSideEffects/doubly_recursive_function.yul index 82a543b81..9afcfb0af 100644 --- a/test/libyul/functionSideEffects/doubly_recursive_function.yul +++ b/test/libyul/functionSideEffects/doubly_recursive_function.yul @@ -4,5 +4,5 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: +// a: +// b: diff --git a/test/libyul/functionSideEffects/recursive_function.yul b/test/libyul/functionSideEffects/recursive_function.yul index 05e474eb0..875339cba 100644 --- a/test/libyul/functionSideEffects/recursive_function.yul +++ b/test/libyul/functionSideEffects/recursive_function.yul @@ -3,4 +3,4 @@ } // ---- // : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: +// a: diff --git a/test/libyul/functionSideEffects/with_loop.yul b/test/libyul/functionSideEffects/with_loop.yul index a1deb26f5..d68aa1fec 100644 --- a/test/libyul/functionSideEffects/with_loop.yul +++ b/test/libyul/functionSideEffects/with_loop.yul @@ -4,6 +4,6 @@ pop(f()) } // ---- -// : -// f: -// g: +// : +// f: +// g: From dc792748712fd6b652fd8f92a52359b4fa24860d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Dec 2019 10:42:14 +0100 Subject: [PATCH 116/130] Set default EVM version to Istanbul. --- .circleci/soltest_all.sh | 2 +- Changelog.md | 1 + docs/using-the-compiler.rst | 4 ++-- liblangutil/EVMVersion.h | 2 +- solc/CommandLineInterface.cpp | 3 ++- test/libsolidity/StandardCompiler.cpp | 4 +++- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 3a2acd1ac..74b3f1197 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -34,4 +34,4 @@ for OPTIMIZE in 0 1; do done done -EVM=constantinople OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh \ No newline at end of file +EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh diff --git a/Changelog.md b/Changelog.md index b95736856..f3aa6d0fb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: + * Set the default EVM version to "Istanbul". * Commandline Interface: Allow translation from yul / strict assembly to EWasm using ``solc --yul --yul-dialect evm --machine eWasm`` * SMTChecker: Add support to constructors including constructor inheritance. * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 2a02322cf..fe2d316ba 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -120,9 +120,9 @@ at each version. Backward compatibility is not guaranteed between each version. - ``constantinople`` - Opcodes ``create2`, ``extcodehash``, ``shl``, ``shr`` and ``sar`` are available in assembly. - Shifting operators use shifting opcodes and thus need less gas. -- ``petersburg`` (**default**) +- ``petersburg`` - The compiler behaves the same way as with constantinople. -- ``istanbul`` +- ``istanbul`` (**default**) - Opcodes ``chainid`` and ``selfbalance`` are available in assembly. - ``berlin`` (**experimental**) diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 95a9eb089..5151d85a9 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -98,7 +98,7 @@ private: EVMVersion(Version _version): m_version(_version) {} - Version m_version = Version::Petersburg; + Version m_version = Version::Istanbul; }; } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 48f04d564..05b021379 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -656,7 +656,8 @@ Allowed options)", ( g_strEVMVersion.c_str(), po::value()->value_name("version"), - "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg (default), istanbul or berlin." + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " + "byzantium, constantinople, petersburg, istanbul (default) or berlin." ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index f737ac99d..17b39b2e6 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -900,9 +900,11 @@ BOOST_AUTO_TEST_CASE(evm_version) BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"constantinople\"") != string::npos); result = compile(inputForVersion("\"evmVersion\": \"petersburg\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"petersburg\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"istanbul\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"istanbul\"") != string::npos); // test default result = compile(inputForVersion("")); - BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"petersburg\"") != string::npos); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"istanbul\"") != string::npos); // test invalid result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); From af482470f92464fc2b6b1ce5f74c041cfff337ba Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Dec 2019 11:41:42 +0100 Subject: [PATCH 117/130] Update gas costs. --- test/libsolidity/gasTests/abiv2.sol | 2 +- test/libsolidity/gasTests/abiv2_optimised.sol | 4 +- test/libsolidity/gasTests/dispatch_large.sol | 40 +++++++++---------- .../gasTests/dispatch_large_optimised.sol | 40 +++++++++---------- test/libsolidity/gasTests/dispatch_medium.sol | 16 ++++---- .../gasTests/dispatch_medium_optimised.sol | 16 ++++---- test/libsolidity/gasTests/dispatch_small.sol | 6 +-- .../gasTests/dispatch_small_optimised.sol | 6 +-- 8 files changed, 65 insertions(+), 65 deletions(-) diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index 2ee698008..390811a0b 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -18,7 +18,7 @@ contract C { // executionCost: 1167 // totalCost: 1123767 // external: -// a(): 530 +// a(): 1130 // b(uint256): infinite // f1(uint256): infinite // f2(uint256[],string[],uint16,address): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index 83d07911f..35b9b8057 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -21,8 +21,8 @@ contract C { // executionCost: 645 // totalCost: 609045 // external: -// a(): 429 -// b(uint256): 884 +// a(): 1029 +// b(uint256): 2084 // f1(uint256): 351 // f2(uint256[],string[],uint16,address): infinite // f3(uint16[],string[],uint16,address): infinite diff --git a/test/libsolidity/gasTests/dispatch_large.sol b/test/libsolidity/gasTests/dispatch_large.sol index 7fc28339c..014eb1a9a 100644 --- a/test/libsolidity/gasTests/dispatch_large.sol +++ b/test/libsolidity/gasTests/dispatch_large.sol @@ -28,25 +28,25 @@ contract Large { // executionCost: 670 // totalCost: 637470 // external: -// a(): 451 -// b(uint256): 846 +// a(): 1051 +// b(uint256): 2046 // f0(uint256): 427 -// f1(uint256): 40752 -// f2(uint256): 20693 -// f3(uint256): 20781 -// f4(uint256): 20759 -// f5(uint256): 20737 -// f6(uint256): 20760 -// f7(uint256): 20672 -// f8(uint256): 20672 -// f9(uint256): 20694 +// f1(uint256): 41352 +// f2(uint256): 21293 +// f3(uint256): 21381 +// f4(uint256): 21359 +// f5(uint256): 21337 +// f6(uint256): 21360 +// f7(uint256): 21272 +// f8(uint256): 21272 +// f9(uint256): 21294 // g0(uint256): 313 -// g1(uint256): 40707 -// g2(uint256): 20670 -// g3(uint256): 20758 -// g4(uint256): 20736 -// g5(uint256): 20692 -// g6(uint256): 20715 -// g7(uint256): 20714 -// g8(uint256): 20692 -// g9(uint256): 20649 +// g1(uint256): 41307 +// g2(uint256): 21270 +// g3(uint256): 21358 +// g4(uint256): 21336 +// g5(uint256): 21292 +// g6(uint256): 21315 +// g7(uint256): 21314 +// g8(uint256): 21292 +// g9(uint256): 21249 diff --git a/test/libsolidity/gasTests/dispatch_large_optimised.sol b/test/libsolidity/gasTests/dispatch_large_optimised.sol index cee223b4c..bb80d1e8e 100644 --- a/test/libsolidity/gasTests/dispatch_large_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_large_optimised.sol @@ -31,25 +31,25 @@ contract Large { // executionCost: 300 // totalCost: 260700 // external: -// a(): 398 -// b(uint256): 1105 +// a(): 998 +// b(uint256): 2305 // f0(uint256): 334 -// f1(uint256): 40874 -// f2(uint256): 20940 -// f3(uint256): 21028 -// f4(uint256): 21006 -// f5(uint256): 20984 -// f6(uint256): 20896 -// f7(uint256): 20676 -// f8(uint256): 20808 -// f9(uint256): 20830 +// f1(uint256): 41474 +// f2(uint256): 21540 +// f3(uint256): 21628 +// f4(uint256): 21606 +// f5(uint256): 21584 +// f6(uint256): 21496 +// f7(uint256): 21276 +// f8(uint256): 21408 +// f9(uint256): 21430 // g0(uint256): 574 -// g1(uint256): 40586 -// g2(uint256): 20674 -// g3(uint256): 20762 -// g4(uint256): 20740 -// g5(uint256): 20828 -// g6(uint256): 20608 -// g7(uint256): 20718 -// g8(uint256): 20696 -// g9(uint256): 20542 +// g1(uint256): 41186 +// g2(uint256): 21274 +// g3(uint256): 21362 +// g4(uint256): 21340 +// g5(uint256): 21428 +// g6(uint256): 21208 +// g7(uint256): 21318 +// g8(uint256): 21296 +// g9(uint256): 21142 diff --git a/test/libsolidity/gasTests/dispatch_medium.sol b/test/libsolidity/gasTests/dispatch_medium.sol index 0c1d80126..c1bc1176e 100644 --- a/test/libsolidity/gasTests/dispatch_medium.sol +++ b/test/libsolidity/gasTests/dispatch_medium.sol @@ -15,12 +15,12 @@ contract Medium { // executionCost: 294 // totalCost: 253294 // external: -// a(): 428 -// b(uint256): 846 -// f1(uint256): 40663 -// f2(uint256): 20693 -// f3(uint256): 20737 +// a(): 1028 +// b(uint256): 2046 +// f1(uint256): 41263 +// f2(uint256): 21293 +// f3(uint256): 21337 // g0(uint256): 313 -// g7(uint256): 20692 -// g8(uint256): 20670 -// g9(uint256): 20626 +// g7(uint256): 21292 +// g8(uint256): 21270 +// g9(uint256): 21226 diff --git a/test/libsolidity/gasTests/dispatch_medium_optimised.sol b/test/libsolidity/gasTests/dispatch_medium_optimised.sol index b2c76140a..db4686a84 100644 --- a/test/libsolidity/gasTests/dispatch_medium_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_medium_optimised.sol @@ -18,12 +18,12 @@ contract Medium { // executionCost: 183 // totalCost: 140983 // external: -// a(): 398 -// b(uint256): 863 -// f1(uint256): 40654 -// f2(uint256): 20698 -// f3(uint256): 20742 +// a(): 998 +// b(uint256): 2063 +// f1(uint256): 41254 +// f2(uint256): 21298 +// f3(uint256): 21342 // g0(uint256): 332 -// g7(uint256): 20608 -// g8(uint256): 20586 -// g9(uint256): 20542 +// g7(uint256): 21208 +// g8(uint256): 21186 +// g9(uint256): 21142 diff --git a/test/libsolidity/gasTests/dispatch_small.sol b/test/libsolidity/gasTests/dispatch_small.sol index cb163d1ba..3d48a4543 100644 --- a/test/libsolidity/gasTests/dispatch_small.sol +++ b/test/libsolidity/gasTests/dispatch_small.sol @@ -11,6 +11,6 @@ contract Small { // totalCost: 83735 // external: // fallback: 118 -// a(): 383 -// b(uint256): 802 -// f1(uint256): 40663 +// a(): 983 +// b(uint256): 2002 +// f1(uint256): 41263 diff --git a/test/libsolidity/gasTests/dispatch_small_optimised.sol b/test/libsolidity/gasTests/dispatch_small_optimised.sol index 7153a784c..9012dbd43 100644 --- a/test/libsolidity/gasTests/dispatch_small_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_small_optimised.sol @@ -14,6 +14,6 @@ contract Small { // totalCost: 60511 // external: // fallback: 118 -// a(): 376 -// b(uint256): 753 -// f1(uint256): 40588 +// a(): 976 +// b(uint256): 1953 +// f1(uint256): 41188 From 67d82fc8a7283d3dcb64c58ecb302eee11207aab Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 3 Dec 2019 01:11:21 +0100 Subject: [PATCH 118/130] [SMTChecker] Use rlimit instead of tlimit for SMT queries --- libsolidity/formal/CVC4Interface.cpp | 2 +- libsolidity/formal/CVC4Interface.h | 6 ++++++ libsolidity/formal/SolverInterface.h | 4 ---- libsolidity/formal/Z3CHCInterface.cpp | 5 ++--- libsolidity/formal/Z3CHCInterface.h | 3 --- libsolidity/formal/Z3Interface.cpp | 5 ++--- libsolidity/formal/Z3Interface.h | 6 ++++++ 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp index 377df32d2..03c12cf08 100644 --- a/libsolidity/formal/CVC4Interface.cpp +++ b/libsolidity/formal/CVC4Interface.cpp @@ -35,7 +35,7 @@ void CVC4Interface::reset() m_variables.clear(); m_solver.reset(); m_solver.setOption("produce-models", true); - m_solver.setTimeLimit(queryTimeout); + m_solver.setResourceLimit(resourceLimit); } void CVC4Interface::push() diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index 28596bdcf..76318fdee 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -63,6 +63,12 @@ private: CVC4::ExprManager m_context; CVC4::SmtEngine m_solver; std::map m_variables; + + // CVC4 "basic resources" limit. + // This is used to make the runs more deterministic and platform/machine independent. + // The tests start failing for CVC4 with less than 6000, + // so using double that. + static int const resourceLimit = 12000; }; } diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 2889af25a..a39e699af 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -359,10 +359,6 @@ public: /// @returns how many SMT solvers this interface has. virtual unsigned solvers() { return 1; } - -protected: - // SMT query timeout in milliseconds. - static int const queryTimeout = 10000; }; } diff --git a/libsolidity/formal/Z3CHCInterface.cpp b/libsolidity/formal/Z3CHCInterface.cpp index 87d0db0c4..d4c53f850 100644 --- a/libsolidity/formal/Z3CHCInterface.cpp +++ b/libsolidity/formal/Z3CHCInterface.cpp @@ -29,10 +29,9 @@ Z3CHCInterface::Z3CHCInterface(): m_context(m_z3Interface->context()), m_solver(*m_context) { - // This needs to be set globally. + // These need to be set globally. z3::set_param("rewriter.pull_cheap_ite", true); - // This needs to be set in the context. - m_context->set("timeout", queryTimeout); + z3::set_param("rlimit", Z3Interface::resourceLimit); // Spacer options. // These needs to be set in the solver. diff --git a/libsolidity/formal/Z3CHCInterface.h b/libsolidity/formal/Z3CHCInterface.h index 3acab97c3..858e4f40f 100644 --- a/libsolidity/formal/Z3CHCInterface.h +++ b/libsolidity/formal/Z3CHCInterface.h @@ -54,9 +54,6 @@ private: z3::context* m_context; // Horn solver. z3::fixedpoint m_solver; - - // SMT query timeout in milliseconds. - static int const queryTimeout = 10000; }; } diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index aa20eb533..74b3cfe2b 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -27,10 +27,9 @@ using namespace dev::solidity::smt; Z3Interface::Z3Interface(): m_solver(m_context) { - // This needs to be set globally. + // These need to be set globally. z3::set_param("rewriter.pull_cheap_ite", true); - // This needs to be set in the context. - m_context.set("timeout", queryTimeout); + z3::set_param("rlimit", resourceLimit); } void Z3Interface::reset() diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index 38734dd00..df5db8d26 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -50,6 +50,12 @@ public: z3::context* context() { return &m_context; } + // Z3 "basic resources" limit. + // This is used to make the runs more deterministic and platform/machine independent. + // The tests start failing for Z3 with less than 20000000, + // so using double that. + static int const resourceLimit = 40000000; + private: void declareFunction(std::string const& _name, Sort const& _sort); From 48c3a5c2256ac30274c251f69d2e5eb1f4bacd9e Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 3 Dec 2019 14:39:47 +0100 Subject: [PATCH 119/130] [SMTChecker] Create options to choose SMT solver in runtime --- libsolidity/formal/BMC.cpp | 24 +++++++++++++++--------- libsolidity/formal/BMC.h | 7 ++++++- libsolidity/formal/CHC.cpp | 10 ++++++++-- libsolidity/formal/CHC.h | 3 ++- libsolidity/formal/ModelChecker.cpp | 10 +++++++--- libsolidity/formal/ModelChecker.h | 7 ++++++- libsolidity/formal/SMTPortfolio.cpp | 12 +++++++++--- libsolidity/formal/SMTPortfolio.h | 5 ++++- libsolidity/formal/SolverInterface.h | 15 +++++++++++++++ 9 files changed, 72 insertions(+), 21 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index b852485f8..cea8e3d4f 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -27,19 +27,25 @@ using namespace dev; using namespace langutil; using namespace dev::solidity; -BMC::BMC(smt::EncodingContext& _context, ErrorReporter& _errorReporter, map const& _smtlib2Responses): +BMC::BMC( + smt::EncodingContext& _context, + ErrorReporter& _errorReporter, + map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers +): SMTEncoder(_context), m_outerErrorReporter(_errorReporter), - m_interface(make_shared(_smtlib2Responses)) + m_interface(make_shared(_smtlib2Responses, _enabledSolvers)) { #if defined (HAVE_Z3) || defined (HAVE_CVC4) - if (!_smtlib2Responses.empty()) - m_errorReporter.warning( - "SMT-LIB2 query responses were given in the auxiliary input, " - "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." - "These responses will be ignored." - "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." - ); + if (_enabledSolvers.some()) + if (!_smtlib2Responses.empty()) + m_errorReporter.warning( + "SMT-LIB2 query responses were given in the auxiliary input, " + "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." + "These responses will be ignored." + "Consider disabling Z3/CVC4 at compilation time in order to use SMT-LIB2 responses." + ); #endif } diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 432050e78..01ef36369 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -53,7 +53,12 @@ namespace solidity class BMC: public SMTEncoder { public: - BMC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses); + BMC( + smt::EncodingContext& _context, + langutil::ErrorReporter& _errorReporter, + std::map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers + ); void analyze(SourceUnit const& _sources, std::set _safeAssertions); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 0bc6a1adc..5e9495192 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -35,17 +35,23 @@ using namespace dev::solidity; CHC::CHC( smt::EncodingContext& _context, ErrorReporter& _errorReporter, - map const& _smtlib2Responses + map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers ): SMTEncoder(_context), #ifdef HAVE_Z3 - m_interface(make_shared()), + m_interface( + _enabledSolvers.z3 ? + dynamic_pointer_cast(make_shared()) : + dynamic_pointer_cast(make_shared(_smtlib2Responses)) + ), #else m_interface(make_shared(_smtlib2Responses)), #endif m_outerErrorReporter(_errorReporter) { (void)_smtlib2Responses; + (void)_enabledSolvers; } void CHC::analyze(SourceUnit const& _source) diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 9aa5871d5..a5ab9e06b 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -47,7 +47,8 @@ public: CHC( smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter, - std::map const& _smtlib2Responses + std::map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers ); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index c061c8416..508509805 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -22,9 +22,13 @@ using namespace dev; using namespace langutil; using namespace dev::solidity; -ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map const& _smtlib2Responses): - m_bmc(m_context, _errorReporter, _smtlib2Responses), - m_chc(m_context, _errorReporter, _smtlib2Responses), +ModelChecker::ModelChecker( + ErrorReporter& _errorReporter, + map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers +): + m_bmc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers), + m_chc(m_context, _errorReporter, _smtlib2Responses, _enabledSolvers), m_context() { } diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 349ed76d4..be0d5c38e 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,11 @@ namespace solidity class ModelChecker { public: - ModelChecker(langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses); + ModelChecker( + langutil::ErrorReporter& _errorReporter, + std::map const& _smtlib2Responses, + smt::SMTSolverChoice _enabledSolvers = smt::SMTSolverChoice::All() + ); void analyze(SourceUnit const& _sources); diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp index d47c01f31..9b9795e9a 100644 --- a/libsolidity/formal/SMTPortfolio.cpp +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -30,15 +30,21 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::smt; -SMTPortfolio::SMTPortfolio(map const& _smtlib2Responses) +SMTPortfolio::SMTPortfolio( + map const& _smtlib2Responses, + SMTSolverChoice _enabledSolvers +) { m_solvers.emplace_back(make_unique(_smtlib2Responses)); #ifdef HAVE_Z3 - m_solvers.emplace_back(make_unique()); + if (_enabledSolvers.z3) + m_solvers.emplace_back(make_unique()); #endif #ifdef HAVE_CVC4 - m_solvers.emplace_back(make_unique()); + if (_enabledSolvers.cvc4) + m_solvers.emplace_back(make_unique()); #endif + (void)_enabledSolvers; } void SMTPortfolio::reset() diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h index d922affd2..41472c187 100644 --- a/libsolidity/formal/SMTPortfolio.h +++ b/libsolidity/formal/SMTPortfolio.h @@ -42,7 +42,10 @@ namespace smt class SMTPortfolio: public SolverInterface, public boost::noncopyable { public: - SMTPortfolio(std::map const& _smtlib2Responses); + SMTPortfolio( + std::map const& _smtlib2Responses, + SMTSolverChoice _enabledSolvers + ); void reset() override; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index a39e699af..81f6ea497 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -36,6 +36,21 @@ namespace solidity namespace smt { +struct SMTSolverChoice +{ + bool cvc4 = false; + bool z3 = false; + + static constexpr SMTSolverChoice All() { return {true, true}; } + static constexpr SMTSolverChoice CVC4() { return {true, false}; } + static constexpr SMTSolverChoice Z3() { return {false, true}; } + static constexpr SMTSolverChoice None() { return {false, false}; } + + bool none() { return !some(); } + bool some() { return cvc4 || z3; } + bool all() { return cvc4 && z3; } +}; + enum class CheckResult { SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR From 7be6b54fc72d8d7f82a2714f98f0b99a5a456f9b Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 4 Dec 2019 17:31:39 +0100 Subject: [PATCH 120/130] Add comment --- libsolidity/formal/ModelChecker.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index be0d5c38e..d7ec7c1b3 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -44,6 +44,8 @@ namespace solidity class ModelChecker { public: + /// @param _enabledSolvers represents a runtime choice of which SMT solvers + /// should be used, even if all are available. The default choice is to use all. ModelChecker( langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, From 628d219c94f08b52dbe43b7121d7df4fb1665bb7 Mon Sep 17 00:00:00 2001 From: Henry Lee Date: Tue, 29 Oct 2019 11:46:04 +1100 Subject: [PATCH 121/130] Version Checker: 0.5.x-prerelease will match `pragma solidity ^0.5` Basically, the pre-release rule is only applied when all remaining version numbers equal to 0. --- Changelog.md | 2 +- liblangutil/SemVerHandler.cpp | 11 +++++++++++ test/libsolidity/SemVerMatcher.cpp | 10 ++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index f3aa6d0fb..2b1972989 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,7 +22,7 @@ Bugfixes: * SMTChecker: Fix internal error when using arrays or mappings of functions. * SMTChecker: Fix internal error in array of structs type. * Yul: Consider infinite loops and recursion to be not removable. - + * Version Checker: 0.5.x-prerelease will match `pragma solidity ^0.5`. ### 0.5.13 (2019-11-14) diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index 378923420..611f058ae 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -105,8 +105,19 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio didCompare = true; cmp = _version.numbers[i] - version.numbers[i]; } + if (cmp == 0 && !_version.prerelease.empty() && didCompare) + { cmp = -1; + for (unsigned i = levelsPresent; i < 3; i++) + { + if (_version.numbers[i] > 0) + { + cmp = 0; + break; + } + } + } switch (prefix) { diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 4a6c123ef..7dae613a8 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -70,6 +70,8 @@ BOOST_AUTO_TEST_CASE(positive_range) {"*", "1.2.3-foo"}, {"1.0.0 - 2.0.0", "1.2.3"}, {"1.0.0", "1.0.0"}, + {"1.0", "1.0.0"}, + {"1", "1.0.0"}, {">=*", "0.2.4"}, {"*", "1.2.3"}, {">=1.0.0", "1.0.0"}, @@ -82,6 +84,8 @@ BOOST_AUTO_TEST_CASE(positive_range) {"<=2.0.0", "0.2.9"}, {"<2.0.0", "1.9999.9999"}, {"<2.0.0", "0.2.9"}, + {"<1.0", "1.0.0-pre"}, + {"<1", "1.0.0-pre"}, {">= 1.0.0", "1.0.0"}, {">= 1.0.0", "1.0.1"}, {">= 1.0.0", "1.1.0"}, @@ -137,6 +141,10 @@ BOOST_AUTO_TEST_CASE(positive_range) {"^0.1.2", "0.1.2"}, {"^0.1", "0.1.2"}, {"^1.2", "1.4.2"}, + {"^1.2", "1.2.1-pre"}, + {"^1.2", "1.2.0"}, + {"^1", "1.2.0-pre"}, + {"^1", "1.2.0"}, {"<=1.2.3", "1.2.3-beta"}, {">1.2", "1.3.0-beta"}, {"<1.2.3", "1.2.3-beta"}, @@ -158,6 +166,8 @@ BOOST_AUTO_TEST_CASE(negative_range) // Positive range tests vector> tests = { {"1.0.0 - 2.0.0", "2.2.3"}, + {"1.0", "1.0.0-pre"}, + {"1", "1.0.0-pre"}, {"^1.2.3", "1.2.3-pre"}, {"^1.2", "1.2.0-pre"}, {"^1.2.3", "1.2.3-beta"}, From 4d90180e24382ff324d43c78cdc0c2025ce0c62d Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 3 Dec 2019 03:02:33 +0300 Subject: [PATCH 122/130] Support referencing other constants in inline assembly --- Changelog.md | 1 + libsolidity/CMakeLists.txt | 2 + libsolidity/analysis/TypeChecker.cpp | 5 ++- libsolidity/ast/ASTUtils.cpp | 42 +++++++++++++++++++ libsolidity/ast/ASTUtils.h | 30 +++++++++++++ libsolidity/codegen/ContractCompiler.cpp | 2 + .../constant_access_referencing.sol | 26 ++++++++++++ .../inlineAssembly/constant_array.sol | 2 +- .../inlineAssembly/constant_bytes_ref.sol | 11 +++++ .../inlineAssembly/constant_ref.sol | 1 - 10 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 libsolidity/ast/ASTUtils.cpp create mode 100644 libsolidity/ast/ASTUtils.h create mode 100644 test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol diff --git a/Changelog.md b/Changelog.md index f3aa6d0fb..c8a91b238 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Language Features: * Allow to obtain the selector of public or external library functions via a member ``.selector``. * Parser: Allow splitting string and hexadecimal string literals into multiple parts. + * Inline Assembly: Support referencing other constants. Compiler Features: diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index ded239559..3de6b68ec 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -41,6 +41,8 @@ set(sources ast/ASTJsonConverter.h ast/ASTPrinter.cpp ast/ASTPrinter.h + ast/ASTUtils.cpp + ast/ASTUtils.h ast/ASTVisitor.h ast/ExperimentalFeatures.h ast/Types.cpp diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 262db9d1d..4982ee158 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -641,6 +642,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(var->type(), "Expected variable type!"); if (var->isConstant()) { + var = rootVariableDeclaration(*var); + if (!var->value()) { m_errorReporter.typeError(_identifier.location, "Constant has no value."); @@ -651,7 +654,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) type(*var->value())->category() != Type::Category::RationalNumber )) { - m_errorReporter.typeError(_identifier.location, "Only direct number constants are supported by inline assembly."); + m_errorReporter.typeError(_identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); return size_t(-1); } else if (_context == yul::IdentifierContext::LValue) diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp new file mode 100644 index 000000000..991083dee --- /dev/null +++ b/libsolidity/ast/ASTUtils.cpp @@ -0,0 +1,42 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include + +namespace dev +{ +namespace solidity +{ + +VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl) +{ + solAssert(_varDecl.isConstant(), "Constant variable expected"); + + VariableDeclaration const* rootDecl = &_varDecl; + Identifier const* identifier; + while ((identifier = dynamic_cast(rootDecl->value().get()))) + { + auto referencedVarDecl = dynamic_cast(identifier->annotation().referencedDeclaration); + solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration"); + rootDecl = referencedVarDecl; + } + return rootDecl; +} + +} +} diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h new file mode 100644 index 000000000..67841f1ce --- /dev/null +++ b/libsolidity/ast/ASTUtils.h @@ -0,0 +1,30 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +namespace dev +{ +namespace solidity +{ + +/// Find the topmost referenced variable declaration when the given variable +/// declaration value is an identifier. Works only for constant variable declarations. +VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl); + +} +} diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a9c3990f8..4ad432bf8 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -631,6 +632,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { if (variable->isConstant()) { + variable = rootVariableDeclaration(*variable); u256 value; if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) { diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol new file mode 100644 index 000000000..d2e24a70e --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol @@ -0,0 +1,26 @@ +contract C { + uint constant a = 2; + uint constant aa = a; + uint constant aaa = aa; + bytes2 constant b = 0xabcd; + bytes2 constant bb = b; + bytes3 constant c = "abc"; + bytes3 constant cc = c; + bytes3 constant ccc = cc; + bytes3 constant cccc = ccc; + bool constant d = true; + bool constant dd = d; + address payable constant e = 0x1212121212121212121212121212121212121212; + address payable constant ee = e; + function f() public pure returns (uint w, bytes2 x, bytes3 y, bool z, address t) { + assembly { + w := aaa + x := bb + y := cccc + z := dd + t := ee + } + } +} +// ---- +// f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol index 04cb37421..0711c5368 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_array.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// TypeError: (115-116): Only direct number constants are supported by inline assembly. +// TypeError: (115-116): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol new file mode 100644 index 000000000..dba00a588 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_bytes_ref.sol @@ -0,0 +1,11 @@ +contract C { + bytes32 constant x = keccak256("abc"); + bytes32 constant y = x; + function f() public pure returns (uint t) { + assembly { + t := y + } + } +} +// ---- +// TypeError: (168-169): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol b/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol index eba70edce..dbde5cfa3 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/constant_ref.sol @@ -8,4 +8,3 @@ contract C { } } // ---- -// TypeError: (134-135): Only direct number constants are supported by inline assembly. From 1ab5dcaa2225542722e570fa929e5802e5948cbd Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 5 Dec 2019 14:07:09 +0100 Subject: [PATCH 123/130] Docs: Add missing ` --- docs/contributing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 6220d7d7f..1b50ccaaa 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -86,7 +86,7 @@ but for quicker feedback, you might want to run specific tests. Solidity includes different types of tests, most of them bundled into the `Boost C++ Test Framework `_ application ``soltest``. -Running ``build/test/soltest` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. +Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. Some tests require the ``evmone`` library, others require ``libz3``. @@ -113,7 +113,7 @@ See especially: If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``. To run a subset of tests, you can use filters: -``./scripts/soltest.sh -t TestSuite/TestName, +``./scripts/soltest.sh -t TestSuite/TestName``, where ``TestName`` can be a wildcard ``*``. For example, here is an example test you might run; From 48dd1e398d6e954198a3732775027bcfad77db0b Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 6 Dec 2019 12:33:05 +0100 Subject: [PATCH 124/130] test: Improve handling invalid EVMVersion syntax in test cases. --- test/TestCase.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/TestCase.cpp b/test/TestCase.cpp index c264da100..a9f4e6252 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -186,7 +186,7 @@ bool EVMVersionRestrictedTestCase::validateSettings(langutil::EVMVersion _evmVer versionString = versionString.substr(versionBegin); std::optional version = langutil::EVMVersion::fromString(versionString); if (!version) - throw runtime_error("Invalid EVM version: \"" + versionString + "\""); + BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM version: \"" + versionString + "\""}); if (comparator == ">") return _evmVersion > version; @@ -201,6 +201,5 @@ bool EVMVersionRestrictedTestCase::validateSettings(langutil::EVMVersion _evmVer else if (comparator == "!") return !(_evmVersion == version); else - throw runtime_error("Invalid EVM comparator: \"" + comparator + "\""); - return false; // not reached + BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM comparator: \"" + comparator + "\""}); } From ea4b9e2f989a630f8f81a154760110f01a8ee214 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Dec 2019 11:57:44 +0100 Subject: [PATCH 125/130] Changelog wording. --- Changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index a691203c2..ef537243f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,9 @@ -### 0.5.14 (unreleased) +### 0.5.14 (2019-12-09) Language Features: * Allow to obtain the selector of public or external library functions via a member ``.selector``. - * Parser: Allow splitting string and hexadecimal string literals into multiple parts. - * Inline Assembly: Support referencing other constants. + * Parser: Allow splitting hexadecimal and regular string literals into multiple parts. + * Inline Assembly: Support constants that reference other constants. Compiler Features: From 9d2fc7ec8bb4bb710ca3562ccbcac4a25986431b Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Dec 2019 12:34:21 +0100 Subject: [PATCH 126/130] Sort changelog. --- Changelog.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index ef537243f..d491e6f72 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,28 +2,28 @@ Language Features: * Allow to obtain the selector of public or external library functions via a member ``.selector``. - * Parser: Allow splitting hexadecimal and regular string literals into multiple parts. * Inline Assembly: Support constants that reference other constants. + * Parser: Allow splitting hexadecimal and regular string literals into multiple parts. Compiler Features: - * Set the default EVM version to "Istanbul". * Commandline Interface: Allow translation from yul / strict assembly to EWasm using ``solc --yul --yul-dialect evm --machine eWasm`` + * Set the default EVM version to "Istanbul". * SMTChecker: Add support to constructors including constructor inheritance. * Yul: When compiling via Yul, string literals from the Solidity code are kept as string literals if every character is safely printable. * Yul Optimizer: Perform loop-invariant code motion. -Build System: - * Update to emscripten version 1.39.3. - - Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. * SMTChecker: Fix internal error when using arrays or mappings of functions. * SMTChecker: Fix internal error in array of structs type. - * Yul: Consider infinite loops and recursion to be not removable. * Version Checker: 0.5.x-prerelease will match `pragma solidity ^0.5`. + * Yul: Consider infinite loops and recursion to be not removable. + + +Build System: + * Update to emscripten version 1.39.3. ### 0.5.13 (2019-11-14) From 63962f835dc4b85b6c700fdb63e22896853165be Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Dec 2019 12:52:56 +0100 Subject: [PATCH 127/130] Update bug list. --- docs/bugs_by_version.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 402997af1..c412be830 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -758,6 +758,10 @@ "bugs": [], "released": "2019-11-14" }, + "0.5.14": { + "bugs": [], + "released": "2019-12-09" + }, "0.5.2": { "bugs": [ "SignedArrayStorageCopy", From ee3a83416511ce252469a2f0e738f310f7acee61 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 6 Dec 2019 19:04:29 +0100 Subject: [PATCH 128/130] Add backwards compatibility to the generated soljson.js for solc-js <0.5.14 Reverts part of edd80a9 --- scripts/travis-emscripten/build_emscripten.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 8011205db..c2c8760ef 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -94,6 +94,9 @@ make -j 4 cd .. mkdir -p upload +# Patch soljson.js to provide backwards-compatibility with older emscripten versions +# TODO: remove in 0.6.0! +echo -n ";/* backwards compatibility */ Module['Runtime'] = Module; Module['Pointer_stringify'] = Module['UTF8ToString'];" >> $BUILD_DIR/libsolc/soljson.js cp $BUILD_DIR/libsolc/soljson.js upload/ cp $BUILD_DIR/libsolc/soljson.js ./ From b4a260d6e3ca4288593880cc2c4770303f92125d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 6 Dec 2019 19:05:57 +0100 Subject: [PATCH 129/130] Test solc-js with v0.5.0 Reverts part of edd80a98 --- test/externalTests/common.sh | 4 ++-- test/solcjsTests.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index e5a2dd3e7..dd9aa77aa 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -37,7 +37,7 @@ function setup_solcjs cd "$dir" printLog "Setting up solc-js..." - git clone --depth 1 -b master https://github.com/ethereum/solc-js.git solc + git clone --depth 1 -b v0.5.0 https://github.com/ethereum/solc-js.git solc cd solc npm install @@ -107,7 +107,7 @@ function force_solc_truffle_modules if [ -d "$d" ]; then cd $d rm -rf solc - git clone --depth 1 -b master https://github.com/ethereum/solc-js.git solc + git clone --depth 1 -b v0.5.0 https://github.com/ethereum/solc-js.git solc cp "$1" solc/soljson.js fi ) diff --git a/test/solcjsTests.sh b/test/solcjsTests.sh index df2554c21..d525a3f21 100755 --- a/test/solcjsTests.sh +++ b/test/solcjsTests.sh @@ -40,7 +40,7 @@ VERSION="$2" DIR=$(mktemp -d) ( echo "Preparing solc-js (master)..." - git clone --depth 1 --branch master https://github.com/ethereum/solc-js "$DIR" + git clone --depth 1 --branch v0.5.0 https://github.com/ethereum/solc-js "$DIR" cd "$DIR" # disable "prepublish" script which downloads the latest version # (we will replace it anyway and it is often incorrectly cached From 9c3503834dd4d5560f08e50376ba8a1abef8ef6c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 9 Dec 2019 11:58:27 +0100 Subject: [PATCH 130/130] Fix semver matcher differently. --- Changelog.md | 2 +- liblangutil/SemVerHandler.cpp | 12 +----------- test/libsolidity/SemVerMatcher.cpp | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Changelog.md b/Changelog.md index d491e6f72..0247137b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,7 +18,7 @@ Bugfixes: * SMTChecker: Fix internal error when using ``abi.decode``. * SMTChecker: Fix internal error when using arrays or mappings of functions. * SMTChecker: Fix internal error in array of structs type. - * Version Checker: 0.5.x-prerelease will match `pragma solidity ^0.5`. + * Version Checker: ``^0`` should match ``0.5.0``, but no prerelease. * Yul: Consider infinite loops and recursion to be not removable. diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index 611f058ae..cc414b1d7 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -88,7 +88,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio if (!comp.matches(_version)) return false; - if (comp.version.numbers[0] == 0) + if (comp.version.numbers[0] == 0 && comp.levelsPresent != 1) comp.levelsPresent = 2; else comp.levelsPresent = 1; @@ -107,17 +107,7 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio } if (cmp == 0 && !_version.prerelease.empty() && didCompare) - { cmp = -1; - for (unsigned i = levelsPresent; i < 3; i++) - { - if (_version.numbers[i] > 0) - { - cmp = 0; - break; - } - } - } switch (prefix) { diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 7dae613a8..e0e643968 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -141,14 +141,14 @@ BOOST_AUTO_TEST_CASE(positive_range) {"^0.1.2", "0.1.2"}, {"^0.1", "0.1.2"}, {"^1.2", "1.4.2"}, - {"^1.2", "1.2.1-pre"}, {"^1.2", "1.2.0"}, - {"^1", "1.2.0-pre"}, {"^1", "1.2.0"}, {"<=1.2.3", "1.2.3-beta"}, {">1.2", "1.3.0-beta"}, {"<1.2.3", "1.2.3-beta"}, - {"^1.2 ^1", "1.4.2"} + {"^1.2 ^1", "1.4.2"}, + {"^0", "0.5.1"}, + {"^0", "0.1.1"}, }; for (auto const& t: tests) { @@ -163,13 +163,14 @@ BOOST_AUTO_TEST_CASE(positive_range) BOOST_AUTO_TEST_CASE(negative_range) { - // Positive range tests + // Negative range tests vector> tests = { {"1.0.0 - 2.0.0", "2.2.3"}, {"1.0", "1.0.0-pre"}, {"1", "1.0.0-pre"}, {"^1.2.3", "1.2.3-pre"}, {"^1.2", "1.2.0-pre"}, + {"^1.2", "1.2.1-pre"}, {"^1.2.3", "1.2.3-beta"}, {"=0.7.x", "0.7.0-asdf"}, {">=0.7.x", "0.7.0-asdf"}, @@ -212,8 +213,16 @@ BOOST_AUTO_TEST_CASE(negative_range) {"=1.2.3", "1.2.3-beta"}, {">1.2", "1.2.8"}, {"^1.2.3", "2.0.0-alpha"}, + {"^0.6", "0.6.2-alpha"}, + {"^0.6", "0.6.0-alpha"}, + {"^1.2", "1.2.1-pre"}, {"^1.2.3", "1.2.2"}, - {"^1.2", "1.1.9"} + {"^1", "1.2.0-pre"}, + {"^1", "1.2.0-pre"}, + {"^1.2", "1.1.9"}, + {"^0", "0.5.1-pre"}, + {"^0", "0.0.0-pre"}, + {"^0", "1.0.0"}, }; for (auto const& t: tests) {