From f7b29d55da95e97fd969e1ccd0f531a2c44748ce Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 21 Feb 2022 15:35:38 -0500 Subject: [PATCH 01/51] cmake/EthCompilerSettings.cmake: generator expression fix for ninja. --- cmake/EthCompilerSettings.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index ffa825446..0ddeb452a 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -54,9 +54,10 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA add_compile_options(-Wsign-conversion) add_compile_options(-Wconversion) - eth_add_cxx_compiler_flag_if_supported( - $<$:-Wextra-semi> - ) + check_cxx_compiler_flag(-Wextra-semi WEXTRA_SEMI) + if(WEXTRA_SEMI) + add_compile_options($<$:-Wextra-semi>) + endif() eth_add_cxx_compiler_flag_if_supported(-Wfinal-dtor-non-final-class) eth_add_cxx_compiler_flag_if_supported(-Wnewline-eof) eth_add_cxx_compiler_flag_if_supported(-Wsuggest-destructor-override) From 75abe92eb2033bba82d1f0fe9948253c65106f18 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Mar 2022 18:10:52 +0100 Subject: [PATCH 02/51] Refactor stack compressor. --- libyul/optimiser/NameCollector.cpp | 20 +- libyul/optimiser/NameCollector.h | 2 +- libyul/optimiser/Rematerialiser.cpp | 23 --- libyul/optimiser/Rematerialiser.h | 12 -- libyul/optimiser/StackCompressor.cpp | 171 +++++++++--------- .../stackCompressor/inlineInFunction.yul | 1 - 6 files changed, 99 insertions(+), 130 deletions(-) diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index eca509f43..c2a82d494 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -30,18 +30,22 @@ using namespace solidity::util; void NameCollector::operator()(VariableDeclaration const& _varDecl) { - for (auto const& var: _varDecl.variables) - m_names.emplace(var.name); + if (m_collectWhat != OnlyFunctions) + for (auto const& var: _varDecl.variables) + m_names.emplace(var.name); } -void NameCollector::operator ()(FunctionDefinition const& _funDef) +void NameCollector::operator()(FunctionDefinition const& _funDef) { - if (m_collectWhat == VariablesAndFunctions) + if (m_collectWhat != OnlyVariables) m_names.emplace(_funDef.name); - for (auto const& arg: _funDef.parameters) - m_names.emplace(arg.name); - for (auto const& ret: _funDef.returnVariables) - m_names.emplace(ret.name); + if (m_collectWhat != OnlyFunctions) + { + for (auto const& arg: _funDef.parameters) + m_names.emplace(arg.name); + for (auto const& ret: _funDef.returnVariables) + m_names.emplace(ret.name); + } ASTWalker::operator ()(_funDef); } diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 8c9dc14ff..34dd5f9b3 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -35,7 +35,7 @@ namespace solidity::yul class NameCollector: public ASTWalker { public: - enum CollectWhat { VariablesAndFunctions, OnlyVariables }; + enum CollectWhat { VariablesAndFunctions, OnlyVariables, OnlyFunctions }; explicit NameCollector( Block const& _block, diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 6e00fcd8a..7dff1df80 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -37,16 +37,6 @@ void Rematerialiser::run(Dialect const& _dialect, Block& _ast, set _v Rematerialiser{_dialect, _ast, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_ast); } -void Rematerialiser::run( - Dialect const& _dialect, - FunctionDefinition& _function, - set _varsToAlwaysRematerialize, - bool _onlySelectedVariables -) -{ - Rematerialiser{_dialect, _function, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_function); -} - Rematerialiser::Rematerialiser( Dialect const& _dialect, Block& _ast, @@ -60,19 +50,6 @@ Rematerialiser::Rematerialiser( { } -Rematerialiser::Rematerialiser( - Dialect const& _dialect, - FunctionDefinition& _function, - set _varsToAlwaysRematerialize, - bool _onlySelectedVariables -): - DataFlowAnalyzer(_dialect), - m_referenceCounts(ReferencesCounter::countReferences(_function)), - m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)), - m_onlySelectedVariables(_onlySelectedVariables) -{ -} - void Rematerialiser::visit(Expression& _e) { if (holds_alternative(_e)) diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index d1ebb1595..592f79d1b 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -53,12 +53,6 @@ public: std::set _varsToAlwaysRematerialize = {}, bool _onlySelectedVariables = false ); - static void run( - Dialect const& _dialect, - FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {}, - bool _onlySelectedVariables = false - ); protected: Rematerialiser( @@ -67,12 +61,6 @@ protected: std::set _varsToAlwaysRematerialize = {}, bool _onlySelectedVariables = false ); - Rematerialiser( - Dialect const& _dialect, - FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {}, - bool _onlySelectedVariables = false - ); using DataFlowAnalyzer::operator(); diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 8b06d6625..eb0629258 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -50,31 +50,41 @@ namespace /** * Class that discovers all variables that can be fully eliminated by rematerialization, * and the corresponding approximate costs. + * + * Prerequisite: Disambiguator, Function Grouper */ class RematCandidateSelector: public DataFlowAnalyzer { public: explicit RematCandidateSelector(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} - /// @returns a map from rematerialisation costs to a vector of variables to rematerialise + /// @returns a map from function name to rematerialisation costs to a vector of variables to rematerialise /// and variables that occur in their expression. /// While the map is sorted by cost, the contained vectors are sorted by the order of occurrence. - map>>> candidates() + map>>>> candidates() { - map>>> cand; - for (auto const& candidate: m_candidates) + map>>>> cand; + for (auto const& [functionName, candidate]: m_candidates) { if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate)) { size_t numRef = m_numReferences[candidate]; set const* ref = references(candidate); - cand[*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set{}); + cand[functionName][*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set{}); } } return cand; } using DataFlowAnalyzer::operator(); + void operator()(FunctionDefinition& _function) override + { + yulAssert(m_currentFunctionName.empty()); + m_currentFunctionName = _function.name; + DataFlowAnalyzer::operator()(_function); + m_currentFunctionName = {}; + } + void operator()(VariableDeclaration& _varDecl) override { DataFlowAnalyzer::operator()(_varDecl); @@ -84,7 +94,7 @@ public: if (AssignedValue const* value = variableValue(varName)) { yulAssert(!m_expressionCodeCost.count(varName), ""); - m_candidates.emplace_back(varName); + m_candidates.emplace_back(m_currentFunctionName, varName); m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *value->value); } } @@ -122,8 +132,10 @@ public: m_expressionCodeCost.erase(_variable); } - /// All candidate variables in order of occurrence. - vector m_candidates; + YulString m_currentFunctionName = {}; + + /// All candidate variables by function name, in order of occurrence. + vector> m_candidates; /// Candidate variables and the code cost of their value. map m_expressionCodeCost; /// Number of references to each candidate variable. @@ -156,62 +168,80 @@ set chooseVarsToEliminate( return varsToEliminate; } -template void eliminateVariables( Dialect const& _dialect, - ASTNode& _node, - size_t _numVariables, + Block& _ast, + map const& _numVariables, bool _allowMSizeOptimization ) { RematCandidateSelector selector{_dialect}; - selector(_node); - Rematerialiser::run(_dialect, _node, chooseVarsToEliminate(selector.candidates(), _numVariables)); - UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization); + selector(_ast); + map>>>> candidates = selector.candidates(); + + set varsToEliminate; + for (auto const& [functionName, numVariables]: _numVariables) + { + yulAssert(numVariables > 0); + varsToEliminate += chooseVarsToEliminate(candidates[functionName], static_cast(numVariables)); + } + + Rematerialiser::run(_dialect, _ast, move(varsToEliminate)); + // Do not remove functions. + set allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names(); + UnusedPruner::runUntilStabilised(_dialect, _ast, _allowMSizeOptimization, nullptr, allFunctions); } -void eliminateVariables( +void eliminateVariablesOptimizedCodegen( Dialect const& _dialect, - Block& _block, - vector const& _unreachables, + Block& _ast, + map> const& _unreachables, bool _allowMSizeOptimization ) { + if (std::all_of(_unreachables.begin(), _unreachables.end(), [](auto const& _item) { return _item.second.empty(); })) + return; + RematCandidateSelector selector{_dialect}; - selector(_block); - std::map candidates; - for (auto [cost, candidatesWithCost]: selector.candidates()) - for (auto candidate: candidatesWithCost) - candidates[get<0>(candidate)] = cost; + selector(_ast); + + map candidates; + for (auto const& [functionName, candidatesInFunction]: selector.candidates()) + for (auto [cost, candidatesWithCost]: candidatesInFunction) + for (auto candidate: candidatesWithCost) + candidates[get<0>(candidate)] = cost; set varsToEliminate; // TODO: this currently ignores the fact that variables may reference other variables we want to eliminate. - for (auto const& unreachable: _unreachables) - { - map> suitableCandidates; - size_t neededSlots = unreachable.deficit; - for (auto varName: unreachable.variableChoices) + for (auto const& [functionName, unreachables]: _unreachables) + for (auto const& unreachable: unreachables) { - if (varsToEliminate.count(varName)) - --neededSlots; - else if (size_t* cost = util::valueOrNullptr(candidates, varName)) - if (!util::contains(suitableCandidates[*cost], varName)) - suitableCandidates[*cost].emplace_back(varName); - } - for (auto candidatesByCost: suitableCandidates) - { - for (auto candidate: candidatesByCost.second) - if (neededSlots--) - varsToEliminate.emplace(candidate); - else + map> suitableCandidates; + size_t neededSlots = unreachable.deficit; + for (auto varName: unreachable.variableChoices) + { + if (varsToEliminate.count(varName)) + --neededSlots; + else if (size_t* cost = util::valueOrNullptr(candidates, varName)) + if (!util::contains(suitableCandidates[*cost], varName)) + suitableCandidates[*cost].emplace_back(varName); + } + for (auto candidatesByCost: suitableCandidates) + { + for (auto candidate: candidatesByCost.second) + if (neededSlots--) + varsToEliminate.emplace(candidate); + else + break; + if (!neededSlots) break; - if (!neededSlots) - break; + } } - } - Rematerialiser::run(_dialect, _block, std::move(varsToEliminate), true); - UnusedPruner::runUntilStabilised(_dialect, _block, _allowMSizeOptimization); + Rematerialiser::run(_dialect, _ast, std::move(varsToEliminate), true); + // Do not remove functions. + set allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names(); + UnusedPruner::runUntilStabilised(_dialect, _ast, _allowMSizeOptimization, nullptr, allFunctions); } } @@ -239,21 +269,12 @@ bool StackCompressor::run( { yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); unique_ptr cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code); - Block& mainBlock = std::get(_object.code->statements.at(0)); - if ( - auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, YulString{}); - !stackTooDeepErrors.empty() - ) - eliminateVariables(_dialect, mainBlock, stackTooDeepErrors, allowMSizeOptimzation); - for (size_t i = 1; i < _object.code->statements.size(); ++i) - { - auto& fun = std::get(_object.code->statements[i]); - if ( - auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, fun.name); - !stackTooDeepErrors.empty() - ) - eliminateVariables(_dialect, fun.body, stackTooDeepErrors, allowMSizeOptimzation); - } + eliminateVariablesOptimizedCodegen( + _dialect, + *_object.code, + StackLayoutGenerator::reportStackTooDeep(*cfg), + allowMSizeOptimzation + ); } else for (size_t iterations = 0; iterations < _maxIterations; iterations++) @@ -261,32 +282,12 @@ bool StackCompressor::run( map stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit; if (stackSurplus.empty()) return true; - - if (stackSurplus.count(YulString{})) - { - yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value."); - eliminateVariables( - _dialect, - std::get(_object.code->statements.at(0)), - static_cast(stackSurplus.at({})), - allowMSizeOptimzation - ); - } - - for (size_t i = 1; i < _object.code->statements.size(); ++i) - { - auto& fun = std::get(_object.code->statements[i]); - if (!stackSurplus.count(fun.name)) - continue; - - yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value."); - eliminateVariables( - _dialect, - fun, - static_cast(stackSurplus.at(fun.name)), - allowMSizeOptimzation - ); - } + eliminateVariables( + _dialect, + *_object.code, + stackSurplus, + allowMSizeOptimzation + ); } return false; } diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul index 35dfa5343..a51c39f4f 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul @@ -11,7 +11,6 @@ // step: stackCompressor // // { -// { let x := 8 } // function f() // { // mstore(calldataload(calldataload(9)), add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(calldataload(calldataload(9)), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) From 54ab09fee883c6d054db85c80eb4d9459afe004a Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 15 Mar 2022 17:25:22 +0100 Subject: [PATCH 03/51] Additional peephole optimizer rules for removing side-effect free instructions before simple terminations. --- Changelog.md | 1 + libevmasm/PeepholeOptimiser.cpp | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index b178baf57..32d76774a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Peephole Optimizer: Remove operations without side effects before simple terminations. Bugfixes: diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 4e4a2c9d7..334903c92 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -146,6 +146,35 @@ struct OpStop: SimplePeepholeOptimizerMethod } }; +struct OpReturnRevert: SimplePeepholeOptimizerMethod +{ + static bool applySimple( + AssemblyItem const& _op, + AssemblyItem const& _push, + AssemblyItem const& _pushOrDup, + AssemblyItem const& _returnRevert, + std::back_insert_iterator _out + ) + { + if ( + (_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) && + _push.type() == Push && + (_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1)) + ) + if ( + (_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) || + _op.type() == Push + ) + { + *_out = _push; + *_out = _pushOrDup; + *_out = _returnRevert; + return true; + } + return false; + } +}; + struct DoubleSwap: SimplePeepholeOptimizerMethod { static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator) @@ -459,7 +488,7 @@ bool PeepholeOptimiser::optimise() while (state.i < m_items.size()) applyMethods( state, - PushPop(), OpPop(), OpStop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), + PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(), TagConjunctions(), TruthyAnd(), Identity() ); From 4b9c01752d506a02b2f4080f260c14a8d0a98158 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 15 Mar 2022 17:25:35 +0100 Subject: [PATCH 04/51] Test updates. --- test/cmdlineTests/function_debug_info_via_yul/output | 2 +- test/cmdlineTests/viair_subobject_optimization/output | 1 - .../abi_encode_v2_in_function_inherited_in_v1_contract.sol | 2 +- .../semanticTests/array/constant_var_as_array_length.sol | 2 +- .../semanticTests/array/fixed_arrays_as_return_type.sol | 2 +- .../semanticTests/array/function_array_cross_calls.sol | 2 +- test/libsolidity/semanticTests/array/reusing_memory.sol | 2 +- .../constructor/bytes_in_constructors_packer.sol | 2 +- .../constructor/bytes_in_constructors_unpacker.sol | 2 +- .../constructor_with_params_diamond_inheritance.sol | 2 +- .../semanticTests/events/event_emit_from_other_contract.sol | 2 +- .../semanticTests/externalContracts/deposit_contract.sol | 2 +- .../semanticTests/externalContracts/prbmath_unsigned.sol | 2 +- .../semanticTests/externalContracts/ramanujan_pi.sol | 2 +- .../functionCall/creation_function_call_with_args.sol | 2 +- .../functionCall/creation_function_call_with_salt.sol | 2 +- .../functionCall/external_call_to_nonexisting.sol | 2 +- test/libsolidity/semanticTests/functionCall/failed_create.sol | 2 +- .../semanticTests/functionCall/gas_and_value_basic.sol | 2 +- .../semanticTests/functionCall/gas_and_value_brace_syntax.sol | 2 +- .../semanticTests/functionCall/send_zero_ether.sol | 2 +- test/libsolidity/semanticTests/immutable/multi_creation.sol | 2 +- test/libsolidity/semanticTests/immutable/use_scratch.sol | 2 +- .../inherited_function_calldata_memory_interface.sol | 2 +- .../semanticTests/inheritance/value_for_constructor.sol | 2 +- .../semanticTests/isoltestTesting/balance_other_contract.sol | 2 +- .../semanticTests/salted_create/salted_create_with_value.sol | 2 +- test/libsolidity/semanticTests/smoke/constructor.sol | 2 +- test/libsolidity/semanticTests/state/blockhash_basic.sol | 2 +- test/libsolidity/semanticTests/various/address_code.sol | 2 +- .../semanticTests/various/contract_binary_dependencies.sol | 2 +- test/libsolidity/semanticTests/various/senders_balance.sol | 2 +- .../semanticTests/various/staticcall_for_view_and_pure.sol | 4 ++-- test/libsolidity/semanticTests/various/value_complex.sol | 2 +- test/libsolidity/semanticTests/various/value_insane.sol | 2 +- 35 files changed, 35 insertions(+), 36 deletions(-) diff --git a/test/cmdlineTests/function_debug_info_via_yul/output b/test/cmdlineTests/function_debug_info_via_yul/output index 4e9c732b4..97125e4b9 100644 --- a/test/cmdlineTests/function_debug_info_via_yul/output +++ b/test/cmdlineTests/function_debug_info_via_yul/output @@ -13,7 +13,7 @@ }, "calldata_array_index_access_uint256_dyn_calldata": { - "entryPoint": 145, + "entryPoint": 144, "parameterSlots": 2, "returnSlots": 1 } diff --git a/test/cmdlineTests/viair_subobject_optimization/output b/test/cmdlineTests/viair_subobject_optimization/output index 1686e3ef7..5d4e89480 100644 --- a/test/cmdlineTests/viair_subobject_optimization/output +++ b/test/cmdlineTests/viair_subobject_optimization/output @@ -125,7 +125,6 @@ sub_0: assembly { eq tag_4 jumpi - pop 0x00 dup1 revert diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol index 1a72e0888..b7d917dbf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -32,6 +32,6 @@ contract C is B { // compileViaYul: also // ---- // test() -> 77 -// gas irOptimized: 119911 +// gas irOptimized: 119711 // gas legacy: 155093 // gas legacyOptimized: 111550 diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol index 2aa3d43f8..42d14ba24 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -11,7 +11,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 1, 2, 3 -> -// gas irOptimized: 142856 +// gas irOptimized: 142640 // gas legacy: 183490 // gas legacyOptimized: 151938 // a(uint256): 0 -> 1 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index a19475f7b..3ce5f1a8d 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -21,6 +21,6 @@ contract B { // compileViaYul: also // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 -// gas irOptimized: 128110 +// gas irOptimized: 127910 // gas legacy: 234719 // gas legacyOptimized: 132639 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index 66e385812..087ecf0a8 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 292702 +// gas irOptimized: 292502 // gas legacy: 452136 // gas legacyOptimized: 284945 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index d2d09645d..455d6f948 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -26,6 +26,6 @@ contract Main { // compileViaYul: also // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 -// gas irOptimized: 113398 +// gas irOptimized: 113198 // gas legacy: 126596 // gas legacyOptimized: 113823 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 2e0835c25..bdd5c6eb5 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 295403 +// gas irOptimized: 293203 // gas legacy: 428711 // gas legacyOptimized: 297922 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 31212c5e8..d0b3f64cb 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -10,7 +10,7 @@ contract Test { // compileViaYul: also // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 286205 +// gas irOptimized: 283829 // gas legacy: 309607 // gas legacyOptimized: 260566 // m_x() -> 7 diff --git a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol index b8f4d42bb..3e9ae1ab2 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol @@ -23,7 +23,7 @@ contract D is B, C { // compileViaYul: also // ---- // constructor(): 2, 0 -> -// gas irOptimized: 158225 +// gas irOptimized: 156071 // gas legacy: 170665 // gas legacyOptimized: 145396 // i() -> 2 diff --git a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol index 36e5ee73c..0f685b783 100644 --- a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol +++ b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 173316 +// gas irOptimized: 173094 // gas legacy: 250376 // gas legacyOptimized: 174522 // deposit(bytes32), 18 wei: 0x1234 -> diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index bcbf816ae..9e3211ecb 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -178,7 +178,7 @@ contract DepositContract is IDepositContract, ERC165 { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1532125 +// gas irOptimized: 1529797 // gas legacy: 2435803 // gas legacyOptimized: 1775425 // supportsInterface(bytes4): 0x0 -> 0 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index c3fb5183b..c37fe52b5 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -50,7 +50,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1783505 +// gas irOptimized: 1780841 // gas legacy: 2248594 // gas legacyOptimized: 1749096 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 2152a38a0..b1c4c0169 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -35,7 +35,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 456094 +// gas irOptimized: 455866 // gas legacy: 671453 // gas legacyOptimized: 480242 // prb_pi() -> 3141592656369545286 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol index 99167ee8e..0f1b63523 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol @@ -17,7 +17,7 @@ contract D { // compileViaYul: also // ---- // constructor(): 2 -> -// gas irOptimized: 200649 +// gas irOptimized: 200217 // gas legacy: 245842 // gas legacyOptimized: 195676 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol index 7a44bf1b9..462e9ad76 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol @@ -18,7 +18,7 @@ contract D { // compileViaYul: also // ---- // constructor(): 2 -> -// gas irOptimized: 200812 +// gas irOptimized: 200380 // gas legacy: 246202 // gas legacyOptimized: 195914 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol index 7a29cc254..79c3adccb 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol @@ -25,7 +25,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 1 ether -> -// gas irOptimized: 304151 +// gas irOptimized: 303935 // gas legacy: 464030 // gas legacyOptimized: 304049 // f(uint256): 0 -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/failed_create.sol b/test/libsolidity/semanticTests/functionCall/failed_create.sol index 0a8b5a922..19fd10bf6 100644 --- a/test/libsolidity/semanticTests/functionCall/failed_create.sol +++ b/test/libsolidity/semanticTests/functionCall/failed_create.sol @@ -18,7 +18,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 20 wei -// gas irOptimized: 214971 +// gas irOptimized: 212583 // gas legacy: 294335 // gas legacyOptimized: 174279 // f(uint256): 20 -> 1370859564726510389319704988634906228201275401179 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol index ca2cf152e..40de1b509 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -41,7 +41,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 275142 +// gas irOptimized: 270609 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol index a08d75557..6b3f99dee 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -40,7 +40,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 275142 +// gas irOptimized: 270609 // gas legacy: 402654 // gas legacyOptimized: 274470 // sendAmount(uint256): 5 -> 5 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index 188052e75..a3d93043d 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -19,7 +19,7 @@ contract Main { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 100480 +// gas irOptimized: 100264 // gas legacy: 116691 // gas legacyOptimized: 100361 // s() -> true diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index c50acf2be..a66c86027 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -29,7 +29,7 @@ contract C { // compileViaYul: also // ---- // f() -> 3, 7, 5 -// gas irOptimized: 126536 +// gas irOptimized: 126136 // gas legacy: 151334 // gas legacyOptimized: 125166 // x() -> 7 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol index 4f3e8a3bd..9dec40935 100644 --- a/test/libsolidity/semanticTests/immutable/use_scratch.sol +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor(): 3 -> -// gas irOptimized: 129602 +// gas irOptimized: 127454 // gas legacy: 209361 // gas legacyOptimized: 139324 // f() -> 84, 23 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index 165ae909d..2ce71c31f 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -25,6 +25,6 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 102144 +// gas irOptimized: 101944 // gas legacy: 185053 // gas legacyOptimized: 114598 diff --git a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol index 95dd7c3d9..b38e9a6ad 100644 --- a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol +++ b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol @@ -42,7 +42,7 @@ contract Main { // compileViaYul: also // ---- // constructor(), 22 wei -> -// gas irOptimized: 280056 +// gas irOptimized: 277680 // gas legacy: 402045 // gas legacyOptimized: 266772 // getFlag() -> true diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol index 3b1a485c1..b02e48804 100644 --- a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol @@ -18,7 +18,7 @@ contract ClientReceipt { // compileViaYul: also // ---- // constructor(), 2000 wei -> -// gas irOptimized: 183976 +// gas irOptimized: 183544 // gas legacy: 235195 // gas legacyOptimized: 176766 // balance -> 1500 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index aab46a9a4..6613e471b 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -22,6 +22,6 @@ contract A { // compileViaYul: also // ---- // f(), 10 ether -> 3007, 3008, 3009 -// gas irOptimized: 272431 +// gas irOptimized: 271831 // gas legacy: 422501 // gas legacyOptimized: 287472 diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index 4e5a07e95..3003c5d8d 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -14,7 +14,7 @@ contract C { // compileViaYul: also // ---- // constructor(), 2 wei: 3 -> -// gas irOptimized: 109775 +// gas irOptimized: 107627 // gas legacy: 151416 // gas legacyOptimized: 108388 // state() -> 3 diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol index 558e1ca90..829a8695d 100644 --- a/test/libsolidity/semanticTests/state/blockhash_basic.sol +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -14,7 +14,7 @@ contract C { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 113738 +// gas irOptimized: 111584 // gas legacy: 155081 // gas legacyOptimized: 107997 // genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 6f1fbe7a9..2adf31716 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -17,7 +17,7 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 194717 +// gas irOptimized: 192317 // gas legacy: 240889 // gas legacyOptimized: 155314 // initCode() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol index 3d5a480e4..9ff4fe5e2 100644 --- a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -21,4 +21,4 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 101495 +// gas irOptimized: 101063 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol index 344daf8d8..0e0e59996 100644 --- a/test/libsolidity/semanticTests/various/senders_balance.sol +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -19,7 +19,7 @@ contract D { // compileViaYul: also // ---- // constructor(), 27 wei -> -// gas irOptimized: 175589 +// gas irOptimized: 175157 // gas legacy: 222977 // gas legacyOptimized: 169779 // f() -> 27 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol index d2c520545..90dd3a6f7 100644 --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -38,10 +38,10 @@ contract D { // f() -> 0x1 # This should work, next should throw # // gas legacy: 103716 // fview() -> FAILURE -// gas irOptimized: 98438622 +// gas irOptimized: 98438619 // gas legacy: 98438801 // gas legacyOptimized: 98438594 // fpure() -> FAILURE -// gas irOptimized: 98438622 +// gas irOptimized: 98438619 // gas legacy: 98438801 // gas legacyOptimized: 98438595 diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol index 879e9d23c..c868652a0 100644 --- a/test/libsolidity/semanticTests/various/value_complex.sol +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -22,7 +22,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 190491 +// gas irOptimized: 190275 // gas legacy: 265006 // gas legacyOptimized: 182842 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol index 0499c1b06..c304732bb 100644 --- a/test/libsolidity/semanticTests/various/value_insane.sol +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -21,7 +21,7 @@ contract test { // compileViaYul: also // ---- // constructor(), 20 wei -> -// gas irOptimized: 192213 +// gas irOptimized: 191991 // gas legacy: 266728 // gas legacyOptimized: 184762 // sendAmount(uint256): 5 -> 8 From a054285f31fbf332de2230e727a844b86090dc21 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Mar 2022 16:43:21 +0100 Subject: [PATCH 05/51] Simplify rematerialization candidates. --- libyul/optimiser/StackCompressor.cpp | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index eb0629258..b3402e4d7 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -61,16 +61,15 @@ public: /// @returns a map from function name to rematerialisation costs to a vector of variables to rematerialise /// and variables that occur in their expression. /// While the map is sorted by cost, the contained vectors are sorted by the order of occurrence. - map>>>> candidates() + map>> candidates() { - map>>>> cand; + map>> cand; for (auto const& [functionName, candidate]: m_candidates) { if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate)) { size_t numRef = m_numReferences[candidate]; - set const* ref = references(candidate); - cand[functionName][*cost * numRef].emplace_back(candidate, ref ? move(*ref) : set{}); + cand[functionName][*cost * numRef].emplace_back(candidate); } } return cand; @@ -144,25 +143,16 @@ public: /// Selects at most @a _numVariables among @a _candidates. set chooseVarsToEliminate( - map>>> const& _candidates, + map> const& _candidates, size_t _numVariables ) { set varsToEliminate; for (auto&& [cost, candidates]: _candidates) - for (auto&& [candidate, references]: candidates) + for (auto&& candidate: candidates) { if (varsToEliminate.size() >= _numVariables) return varsToEliminate; - // If a variable we would like to eliminate references another one - // we already selected for elimination, then stop selecting - // candidates. If we would add that variable, then the cost calculation - // for the previous variable would be off. Furthermore, we - // do not skip the variable because it would be better to properly re-compute - // the costs of all other variables instead. - for (YulString const& referencedVar: references) - if (varsToEliminate.count(referencedVar)) - return varsToEliminate; varsToEliminate.insert(candidate); } return varsToEliminate; @@ -177,7 +167,7 @@ void eliminateVariables( { RematCandidateSelector selector{_dialect}; selector(_ast); - map>>>> candidates = selector.candidates(); + map>> candidates = selector.candidates(); set varsToEliminate; for (auto const& [functionName, numVariables]: _numVariables) @@ -209,7 +199,7 @@ void eliminateVariablesOptimizedCodegen( for (auto const& [functionName, candidatesInFunction]: selector.candidates()) for (auto [cost, candidatesWithCost]: candidatesInFunction) for (auto candidate: candidatesWithCost) - candidates[get<0>(candidate)] = cost; + candidates[candidate] = cost; set varsToEliminate; From 040c444a2a406b687fe23e3dd7928177f87255a4 Mon Sep 17 00:00:00 2001 From: Hector Roussille Date: Wed, 16 Mar 2022 18:38:55 +0100 Subject: [PATCH 06/51] Fixed ambiguity in the create2 address computation doc specify that arguments are encoded using abi.encode in the example code --- 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 0ce7d21e3..de3b58485 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -283,7 +283,7 @@ which only need to be created if there is a dispute. salt, keccak256(abi.encodePacked( type(D).creationCode, - arg + abi.encode(arg) )) ))))); From 0bf674b4423bce87f0d54c5264e318342e6b3552 Mon Sep 17 00:00:00 2001 From: Marenz Date: Tue, 1 Mar 2022 18:56:17 +0100 Subject: [PATCH 07/51] LSP test: Minor refactoring and better exception reporting --- test/libsolidity/lsp/didOpen_with_import.sol | 1 + test/libsolidity/lsp/lib.sol | 3 + .../libsolidity/lsp/publish_diagnostics_1.sol | 3 + .../libsolidity/lsp/publish_diagnostics_2.sol | 3 + .../libsolidity/lsp/publish_diagnostics_3.sol | 2 + test/lsp.py | 183 +++++++++++++----- 6 files changed, 143 insertions(+), 52 deletions(-) diff --git a/test/libsolidity/lsp/didOpen_with_import.sol b/test/libsolidity/lsp/didOpen_with_import.sol index f505ca6e5..a335df759 100644 --- a/test/libsolidity/lsp/didOpen_with_import.sol +++ b/test/libsolidity/lsp/didOpen_with_import.sol @@ -8,5 +8,6 @@ contract C function f(uint a, uint b) public pure returns (uint) { return Lib.add(2 * a, b); + // ^^^^^^^ @diagnostics } } diff --git a/test/libsolidity/lsp/lib.sol b/test/libsolidity/lsp/lib.sol index 22efe6ca2..031cf19ad 100644 --- a/test/libsolidity/lsp/lib.sol +++ b/test/libsolidity/lsp/lib.sol @@ -23,13 +23,16 @@ enum Color { library Lib { function add(uint a, uint b) public pure returns (uint result) +// ^( @addFunction { result = a + b; } +// ^) @addFunction function warningWithUnused() public pure { uint unused; + // ^^^^^^^^^^^ @diagnostics } } diff --git a/test/libsolidity/lsp/publish_diagnostics_1.sol b/test/libsolidity/lsp/publish_diagnostics_1.sol index e66718512..be15a4090 100644 --- a/test/libsolidity/lsp/publish_diagnostics_1.sol +++ b/test/libsolidity/lsp/publish_diagnostics_1.sol @@ -6,13 +6,16 @@ contract MyContract constructor() { uint unused; // [Warning 2072] Unused local variable. + // ^^^^^^^^^^^ @unusedVariable } } contract D { function main() public payable returns (uint) + // ^^^^ @unusedReturnVariable { MyContract c = new MyContract(); + // ^^^^^^^^^^^^ @unusedContractVariable } } diff --git a/test/libsolidity/lsp/publish_diagnostics_2.sol b/test/libsolidity/lsp/publish_diagnostics_2.sol index 968618955..65b4df585 100644 --- a/test/libsolidity/lsp/publish_diagnostics_2.sol +++ b/test/libsolidity/lsp/publish_diagnostics_2.sol @@ -6,7 +6,9 @@ contract C function makeSomeError() public pure returns (uint res) { uint x = "hi"; + // ^^^^^^^^^^^^^ @conversionError return; + // ^^^^^^^ @argumentsRequired res = 2; } } @@ -17,5 +19,6 @@ contract D { C c = new C(); return c.makeSomeError(2, 3); + // ^^^^^^^^^^^^^^^^^^^^^ @wrongArgumentsCount } } diff --git a/test/libsolidity/lsp/publish_diagnostics_3.sol b/test/libsolidity/lsp/publish_diagnostics_3.sol index bb8998a6a..45b418a90 100644 --- a/test/libsolidity/lsp/publish_diagnostics_3.sol +++ b/test/libsolidity/lsp/publish_diagnostics_3.sol @@ -6,5 +6,7 @@ abstract contract A { } contract B is A +// ^( @notAbstract { } +// ^) @notAbstract diff --git a/test/lsp.py b/test/lsp.py index a20c28c3c..e094292ba 100755 --- a/test/lsp.py +++ b/test/lsp.py @@ -7,8 +7,10 @@ import os import subprocess import sys import traceback +import re from typing import Any, List, Optional, Tuple, Union +from enum import Enum, auto import colorama # Enables the use of SGR & CUP terminal VT sequences on Windows. from deepdiff import DeepDiff @@ -18,6 +20,7 @@ class BadHeader(Exception): def __init__(self, msg: str): super().__init__("Bad header: " + msg) + class JsonRpcProcess: exe_path: str exe_args: List[str] @@ -116,13 +119,14 @@ SGR_STATUS_FAIL = '\033[1;31m' class ExpectationFailed(Exception): def __init__(self, actual, expected): - self.actual = actual - self.expected = expected - diff = DeepDiff(actual, expected) + self.actual = json.dumps(actual, sort_keys=True) + self.expected = json.dumps(expected, sort_keys=True) + diff = json.dumps(DeepDiff(actual, expected), indent=4) super().__init__( - f"Expectation failed.\n\tExpected {expected}\n\tbut got {actual}.\n\t{diff}" + f"\n\tExpected {self.expected}\n\tbut got {self.actual}.\n\t{diff}" ) + def create_cli_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Solidity LSP Test suite") parser.set_defaults(trace_io=False) @@ -168,12 +172,25 @@ class Counter: passed: int = 0 failed: int = 0 + +class Marker(Enum): + SimpleRange = auto() + MultilineRange = auto() + + +# Returns the given marker with the end extended by 'amount' +def extendEnd(marker, amount=1): + marker["end"]["character"] += amount + return marker + + class SolidityLSPTestSuite: # {{{ test_counter = Counter() assertion_counter = Counter() print_assertions: bool = False trace_io: bool = False test_pattern: str + marker_regexes: {} def __init__(self): colorama.init() @@ -184,6 +201,10 @@ class SolidityLSPTestSuite: # {{{ self.print_assertions = args.print_assertions self.trace_io = args.trace_io self.test_pattern = args.test_pattern + self.marker_regexes = { + Marker.SimpleRange: re.compile(R"(?P[\^]+) (?P@\w+)"), + Marker.MultilineRange: re.compile(R"\^(?P[()]) (?P@\w+)$") + } print(f"{SGR_NOTICE}test pattern: {self.test_pattern}{SGR_RESET}") @@ -206,9 +227,8 @@ class SolidityLSPTestSuite: # {{{ with JsonRpcProcess(self.solc_path, ["--lsp"], trace_io=self.trace_io) as solc: test_fn(solc) self.test_counter.passed += 1 - except ExpectationFailed as e: + except ExpectationFailed: self.test_counter.failed += 1 - print(e) print(traceback.format_exc()) except Exception as e: # pragma pylint: disable=broad-except self.test_counter.failed += 1 @@ -347,20 +367,26 @@ class SolidityLSPTestSuite: # {{{ self, diagnostic, code: int, - lineNo: int, - startEndColumns: Tuple[int, int] + lineNo: int = None, + startEndColumns: Tuple[int, int] = None, + marker: {} = None ): - assert len(startEndColumns) == 2 - [startColumn, endColumn] = startEndColumns self.expect_equal(diagnostic['code'], code, f'diagnostic: {code}') - self.expect_equal( - diagnostic['range'], - { - 'start': {'character': startColumn, 'line': lineNo}, - 'end': {'character': endColumn, 'line': lineNo} - }, - "diagnostic: check range" - ) + + if marker: + self.expect_equal(diagnostic['range'], marker, "diagnostic: check range") + else: + assert len(startEndColumns) == 2 + [startColumn, endColumn] = startEndColumns + self.expect_equal( + diagnostic['range'], + { + 'start': {'character': startColumn, 'line': lineNo}, + 'end': {'character': endColumn, 'line': lineNo} + }, + "diagnostic: check range" + ) + def expect_location( self, @@ -421,10 +447,12 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] + markers = self.get_file_tags(TEST_NAME) + self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=13, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo= 7, startEndColumns=( 8, 19)) - self.expect_diagnostic(diagnostics[2], code=2072, lineNo=15, startEndColumns=( 8, 20)) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"]) + self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"]) def test_publish_diagnostics_errors(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -437,10 +465,12 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] + markers = self.get_file_tags(TEST_NAME) + self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=9574, lineNo= 7, startEndColumns=( 8, 21)) - self.expect_diagnostic(diagnostics[1], code=6777, lineNo= 8, startEndColumns=( 8, 15)) - self.expect_diagnostic(diagnostics[2], code=6160, lineNo=18, startEndColumns=(15, 36)) + self.expect_diagnostic(diagnostics[0], code=9574, marker=markers["@conversionError"]) + self.expect_diagnostic(diagnostics[1], code=6777, marker=markers["@argumentsRequired"]) + self.expect_diagnostic(diagnostics[2], code=6160, marker=markers["@wrongArgumentsCount"]) def test_publish_diagnostics_errors_multiline(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -453,13 +483,13 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] - self.expect_equal(len(diagnostics), 1, "3 diagnostic messages") + self.expect_equal(len(diagnostics), 1, "1 diagnostic messages") self.expect_equal(diagnostics[0]['code'], 3656, "diagnostic: check code") self.expect_equal( diagnostics[0]['range'], { - 'end': {'character': 1, 'line': 9}, - 'start': {'character': 0, 'line': 7} + 'start': {'character': 0, 'line': 7}, + 'end': {'character': 1, 'line': 10} }, "diagnostic: check range" ) @@ -480,11 +510,58 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[1] self.expect_equal(report['uri'], self.get_test_file_uri('lib'), "Correct file URI") self.expect_equal(len(report['diagnostics']), 1, "one diagnostic") - self.expect_diagnostic(report['diagnostics'][0], code=2072, lineNo=31, startEndColumns=(8, 19)) + marker = self.get_file_tags("lib")["@diagnostics"] + self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker) + + + def get_file_tags(self, test_name: str, verbose=False): + """ + Finds all tags (e.g. @tagname) in the given test and returns them as a + dictionary having the following structure: { + "@tagname": { + "start": { "character": 3, "line": 2 }, + "end": { "character": 30, "line": 2 } + } + } + """ + content = self.get_test_file_contents(test_name) + + markers = {} + + for lineNum, line in enumerate(content.splitlines(), start=-1): + commentStart = line.find("//") + if commentStart == -1: + continue + + for kind, regex in self.marker_regexes.items(): + for match in regex.finditer(line[commentStart:]): + if kind == Marker.SimpleRange: + markers[match.group("tag")] = { + "start": { + "line": lineNum, + "character": match.start("range") + commentStart + }, + "end": { + "line": lineNum, + "character": match.end("range") + commentStart + }} + elif kind == Marker.MultilineRange: + if match.group("delimiter") == "(": + markers[match.group("tag")] = \ + { "start": { "line": lineNum, "character": 0 } } + elif match.group("delimiter") == ")": + markers[match.group("tag")]["end"] = \ + { "line": lineNum, "character": 0 } + + if verbose: + print(markers) + return markers + def test_didChange_in_A_causing_error_in_B(self, solc: JsonRpcProcess) -> None: # Reusing another test but now change some file that generates an error in the other. self.test_textDocument_didOpen_with_relative_import(solc) + marker = self.get_file_tags("lib")["@addFunction"] self.open_file_and_wait_for_diagnostics(solc, 'lib', 2) solc.send_message( 'textDocument/didChange', @@ -496,10 +573,7 @@ class SolidityLSPTestSuite: # {{{ 'contentChanges': [ { - 'range': { - 'start': { 'line': 24, 'character': 0 }, - 'end': { 'line': 29, 'character': 0 } - }, + 'range': marker, 'text': "" # deleting function `add` } ] @@ -512,8 +586,9 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[0] self.expect_equal(report['uri'], self.get_test_file_uri('didOpen_with_import')) diagnostics = report['diagnostics'] + marker = self.get_file_tags("didOpen_with_import")["@diagnostics"] self.expect_equal(len(diagnostics), 1, "now, no diagnostics") - self.expect_diagnostic(diagnostics[0], code=9582, lineNo=9, startEndColumns=(15, 22)) + self.expect_diagnostic(diagnostics[0], code=9582, marker=marker) # The modified file retains the same diagnostics. report = published_diagnostics[1] @@ -543,7 +618,10 @@ class SolidityLSPTestSuite: # {{{ report = published_diagnostics[1] self.expect_equal(report['uri'], self.get_test_file_uri('lib'), "Correct file URI") self.expect_equal(len(report['diagnostics']), 1, "one diagnostic") - self.expect_diagnostic(report['diagnostics'][0], code=2072, lineNo=31, startEndColumns=(8, 19)) + + marker = self.get_file_tags('lib')["@diagnostics"] + self.expect_diagnostic(report['diagnostics'][0], code=2072, marker=marker) + def test_textDocument_didChange_updates_diagnostics(self, solc: JsonRpcProcess) -> None: self.setup_lsp(solc) @@ -554,9 +632,10 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] self.expect_equal(len(diagnostics), 3, "3 diagnostic messages") - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=13, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo= 7, startEndColumns=( 8, 19)) - self.expect_diagnostic(diagnostics[2], code=2072, lineNo=15, startEndColumns=( 8, 20)) + markers = self.get_file_tags(TEST_NAME) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedVariable"]) + self.expect_diagnostic(diagnostics[2], code=2072, marker=markers["@unusedContractVariable"]) solc.send_message( 'textDocument/didChange', @@ -566,10 +645,7 @@ class SolidityLSPTestSuite: # {{{ }, 'contentChanges': [ { - 'range': { - 'start': { 'line': 7, 'character': 1 }, - 'end': { 'line': 8, 'character': 1 } - }, + 'range': extendEnd(markers["@unusedVariable"]), 'text': "" } ] @@ -581,13 +657,16 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(report['uri'], self.get_test_file_uri(TEST_NAME), "Correct file URI") diagnostics = report['diagnostics'] self.expect_equal(len(diagnostics), 2) - self.expect_diagnostic(diagnostics[0], code=6321, lineNo=12, startEndColumns=(44, 48)) - self.expect_diagnostic(diagnostics[1], code=2072, lineNo=14, startEndColumns=( 8, 20)) + self.expect_diagnostic(diagnostics[0], code=6321, marker=markers["@unusedReturnVariable"]) + self.expect_diagnostic(diagnostics[1], code=2072, marker=markers["@unusedContractVariable"]) def test_textDocument_didChange_delete_line_and_close(self, solc: JsonRpcProcess) -> None: # Reuse this test to prepare and ensure it is as expected self.test_textDocument_didOpen_with_relative_import(solc) self.open_file_and_wait_for_diagnostics(solc, 'lib', 2) + + marker = self.get_file_tags('lib')["@diagnostics"] + # lib.sol: Fix the unused variable message by removing it. solc.send_message( 'textDocument/didChange', @@ -599,11 +678,7 @@ class SolidityLSPTestSuite: # {{{ 'contentChanges': # delete the in-body statement: `uint unused;` [ { - 'range': - { - 'start': { 'line': 31, 'character': 1 }, - 'end': { 'line': 32, 'character': 1 } - }, + 'range': extendEnd(marker), 'text': "" } ] @@ -719,7 +794,11 @@ class SolidityLSPTestSuite: # {{{ reports = self.wait_for_diagnostics(solc, 2) self.expect_equal(len(reports), 2, '') self.expect_equal(len(reports[0]['diagnostics']), 0, "should not contain diagnostics") - self.expect_diagnostic(reports[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + + marker = self.get_file_tags("lib")["@diagnostics"] + + # unused variable in lib.sol + self.expect_diagnostic(reports[1]['diagnostics'][0], code=2072, marker=marker) # Now close the file and expect the warning for lib.sol to be removed solc.send_message( @@ -807,7 +886,7 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(len(published_diagnostics), 2, "publish diagnostics for 2 files") self.expect_equal(len(published_diagnostics[0]['diagnostics']), 0) self.expect_equal(len(published_diagnostics[1]['diagnostics']), 1) - self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 33, (8, 19)) # unused variable in lib.sol # import directive self.expect_goto_definition_location( @@ -940,7 +1019,7 @@ class SolidityLSPTestSuite: # {{{ document_uri=FILE_URI, document_position=(64, 33), # symbol `RGBColor` right hand side expression. expected_uri=LIB_URI, - expected_lineNo=35, + expected_lineNo=38, expected_startEndColumns=(7, 15), description="Struct constructor." ) @@ -962,7 +1041,7 @@ class SolidityLSPTestSuite: # {{{ self.expect_equal(len(published_diagnostics), 2, "publish diagnostics for 2 files") self.expect_equal(len(published_diagnostics[0]['diagnostics']), 0) self.expect_equal(len(published_diagnostics[1]['diagnostics']), 1) - self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 31, (8, 19)) # unused variable in lib.sol + self.expect_diagnostic(published_diagnostics[1]['diagnostics'][0], 2072, 33, (8, 19)) # unused variable in lib.sol # import directive: test symbol alias self.expect_goto_definition_location( From 07c9384b44a62cec3ddbaf6885c6035eac9182ab Mon Sep 17 00:00:00 2001 From: aathan Date: Tue, 22 Mar 2022 23:06:23 -0700 Subject: [PATCH 08/51] Update value-types.rst --- docs/types/value-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 8f1009407..cc3f17dd3 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -463,7 +463,7 @@ There is no additional semantic meaning added to a number literal containing und the underscores are ignored. Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by -using them together with anything else than a number literal expression (like boolean literals) or by explicit conversion). +using them together with anything other than a number literal expression (like boolean literals) or by explicit conversion). This means that computations do not overflow and divisions do not truncate in number literal expressions. From 13a4c79a9b02fca61940b90406edd6de0e99d3ae Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 23 Mar 2022 17:32:33 +0100 Subject: [PATCH 09/51] lsp.py: Adds --fail-fast (-f) to terminate early on first failure. --- test/lsp.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/lsp.py b/test/lsp.py index e094292ba..8b7398cf2 100755 --- a/test/lsp.py +++ b/test/lsp.py @@ -129,6 +129,13 @@ class ExpectationFailed(Exception): def create_cli_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Solidity LSP Test suite") + parser.set_defaults(fail_fast=False) + parser.add_argument( + "-f, --fail-fast", + dest="fail_fast", + action="store_true", + help="Terminates the running tests on first failure." + ) parser.set_defaults(trace_io=False) parser.add_argument( "-T, --trace-io", @@ -189,6 +196,7 @@ class SolidityLSPTestSuite: # {{{ assertion_counter = Counter() print_assertions: bool = False trace_io: bool = False + fail_fast: bool = False test_pattern: str marker_regexes: {} @@ -201,6 +209,7 @@ class SolidityLSPTestSuite: # {{{ self.print_assertions = args.print_assertions self.trace_io = args.trace_io self.test_pattern = args.test_pattern + self.fail_fast = args.fail_fast self.marker_regexes = { Marker.SimpleRange: re.compile(R"(?P[\^]+) (?P@\w+)"), Marker.MultilineRange: re.compile(R"\^(?P[()]) (?P@\w+)$") @@ -230,10 +239,14 @@ class SolidityLSPTestSuite: # {{{ except ExpectationFailed: self.test_counter.failed += 1 print(traceback.format_exc()) + if self.fail_fast: + break except Exception as e: # pragma pylint: disable=broad-except self.test_counter.failed += 1 print(f"Unhandled exception {e.__class__.__name__} caught: {e}") print(traceback.format_exc()) + if self.fail_fast: + break print( f"\n{SGR_NOTICE}Summary:{SGR_RESET}\n\n" From 1a0988e503cd551a8c31e62a30b7194fb3631d00 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 15 Mar 2022 15:25:51 -0500 Subject: [PATCH 10/51] libevmasm: refactor asm-json export & add support for source list. --- Changelog.md | 2 + libevmasm/Assembly.cpp | 150 +++++++++--------------------- libevmasm/Assembly.h | 15 +-- libevmasm/AssemblyItem.cpp | 51 ++++++++++ libevmasm/AssemblyItem.h | 7 ++ test/cmdlineTests/asm_json/output | 71 +++++++------- test/libevmasm/Assembler.cpp | 72 +++++++++++--- 7 files changed, 204 insertions(+), 164 deletions(-) diff --git a/Changelog.md b/Changelog.md index 32d76774a..33291c6bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,9 +5,11 @@ Language Features: Compiler Features: * Peephole Optimizer: Remove operations without side effects before simple terminations. + * Assembly-Json: Export: Include source list in `sourceList` field. Bugfixes: +* Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 6d7b97cc4..43e06ef9b 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -222,123 +222,59 @@ string Assembly::assemblyString( return tmp.str(); } -Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType) -{ - Json::Value value{Json::objectValue}; - value["name"] = _name; - value["source"] = _source; - value["begin"] = _begin; - value["end"] = _end; - if (!_value.empty()) - value["value"] = _value; - if (!_jumpType.empty()) - value["jumpType"] = _jumpType; - return value; -} - -string Assembly::toStringInHex(u256 _value) -{ - std::stringstream hexStr; - hexStr << std::uppercase << hex << _value; - return hexStr.str(); -} - -Json::Value Assembly::assemblyJSON(map const& _sourceIndices) const +Json::Value Assembly::assemblyJSON(map const& _sourceIndices, bool _includeSourceList) const { Json::Value root; root[".code"] = Json::arrayValue; - - Json::Value& collection = root[".code"]; - for (AssemblyItem const& i: m_items) + Json::Value& code = root[".code"]; + for (AssemblyItem const& item: m_items) { int sourceIndex = -1; - if (i.location().sourceName) + if (item.location().sourceName) { - auto iter = _sourceIndices.find(*i.location().sourceName); + auto iter = _sourceIndices.find(*item.location().sourceName); if (iter != _sourceIndices.end()) sourceIndex = static_cast(iter->second); } - switch (i.type()) + auto [name, data] = item.nameAndData(); + Json::Value jsonItem; + jsonItem["name"] = name; + jsonItem["begin"] = item.location().start; + jsonItem["end"] = item.location().end; + if (item.m_modifierDepth != 0) + jsonItem["modifierDepth"] = static_cast(item.m_modifierDepth); + std::string jumpType = item.getJumpTypeAsString(); + if (!jumpType.empty()) + jsonItem["jumpType"] = jumpType; + if (name == "PUSHLIB") + data = m_libraries.at(h256(data)); + else if (name == "PUSHIMMUTABLE" || name == "ASSIGNIMMUTABLE") + data = m_immutables.at(h256(data)); + if (!data.empty()) + jsonItem["value"] = data; + jsonItem["source"] = sourceIndex; + code.append(move(jsonItem)); + + if (item.type() == AssemblyItemType::Tag) { - case Operation: - collection.append( - createJsonValue( - instructionInfo(i.instruction()).name, - sourceIndex, - i.location().start, - i.location().end, - i.getJumpTypeAsString()) - ); - break; - case Push: - collection.append( - createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString())); - break; - case PushTag: - if (i.data() == 0) - collection.append( - createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, "")); - else - collection.append( - createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data()))); - break; - case PushSub: - collection.append( - createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data())))); - break; - case PushSubSize: - collection.append( - createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data())))); - break; - case PushProgramSize: - collection.append( - createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end)); - break; - case PushLibraryAddress: - collection.append( - createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data()))) - ); - break; - case PushDeployTimeAddress: - collection.append( - createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end) - ); - break; - case PushImmutable: - collection.append(createJsonValue( - "PUSHIMMUTABLE", - sourceIndex, - i.location().start, - i.location().end, - m_immutables.at(h256(i.data())) - )); - break; - case AssignImmutable: - collection.append(createJsonValue( - "ASSIGNIMMUTABLE", - sourceIndex, - i.location().start, - i.location().end, - m_immutables.at(h256(i.data())) - )); - break; - case Tag: - collection.append( - createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data()))); - collection.append( - createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end)); - break; - case PushData: - collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()))); - break; - case VerbatimBytecode: - collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, util::toHex(i.verbatimData()))); - break; - default: - assertThrow(false, InvalidOpcode, ""); + Json::Value jumpdest; + jumpdest["name"] = "JUMPDEST"; + jumpdest["begin"] = item.location().start; + jumpdest["end"] = item.location().end; + jumpdest["source"] = sourceIndex; + if (item.m_modifierDepth != 0) + jumpdest["modifierDepth"] = static_cast(item.m_modifierDepth); + code.append(move(jumpdest)); } } + if (_includeSourceList) + { + root["sourceList"] = Json::arrayValue; + Json::Value& jsonSourceList = root["sourceList"]; + for (auto const& [name, index]: _sourceIndices) + jsonSourceList[index] = name; + } if (!m_data.empty() || !m_subs.empty()) { @@ -346,17 +282,17 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) Json::Value& data = root[".data"]; for (auto const& i: m_data) if (u256(i.first) >= m_subs.size()) - data[toStringInHex((u256)i.first)] = util::toHex(i.second); + data[util::toHex(toBigEndian((u256)i.first), util::HexPrefix::DontAdd, util::HexCase::Upper)] = util::toHex(i.second); for (size_t i = 0; i < m_subs.size(); ++i) { std::stringstream hexStr; hexStr << hex << i; - data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices); + data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices, /*_includeSourceList = */false); } } - if (m_auxiliaryData.size() > 0) + if (!m_auxiliaryData.empty()) root[".auxdata"] = util::toHex(m_auxiliaryData); return root; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 11bc16662..592119f17 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -39,6 +39,7 @@ #include #include #include +#include namespace solidity::evmasm { @@ -147,7 +148,8 @@ public: /// Create a JSON representation of the assembly. Json::Value assemblyJSON( - std::map const& _sourceIndices = std::map() + std::map const& _sourceIndices = std::map(), + bool _includeSourceList = true ) const; /// Mark this assembly as invalid. Calling ``assemble`` on it will throw. @@ -167,16 +169,6 @@ protected: unsigned codeSize(unsigned subTagSize) const; private: - static Json::Value createJsonValue( - std::string _name, - int _source, - int _begin, - int _end, - std::string _value = std::string(), - std::string _jumpType = std::string() - ); - static std::string toStringInHex(u256 _value); - bool m_invalid = false; Assembly const* subAssemblyById(size_t _subId) const; @@ -222,6 +214,7 @@ protected: std::string m_name; langutil::SourceLocation m_currentSourceLocation; + public: size_t m_currentModifierDepth = 0; }; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 984058765..a36e0ddb1 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,18 @@ using namespace solidity::langutil; static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide"); +namespace +{ + +string toStringInHex(u256 _value) +{ + std::stringstream hexStr; + hexStr << std::uppercase << hex << _value; + return hexStr.str(); +} + +} + AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const { assertThrow(data() < (u256(1) << 64), util::Exception, "Tag already has subassembly set."); @@ -56,6 +69,44 @@ pair AssemblyItem::splitForeignPushTag() const return make_pair(subId, tag); } +pair AssemblyItem::nameAndData() const +{ + switch (type()) + { + case Operation: + return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""}; + case Push: + return {"PUSH", toStringInHex(data())}; + case PushTag: + if (data() == 0) + return {"PUSH [ErrorTag]", ""}; + else + return {"PUSH [tag]", util::toString(data())}; + case PushSub: + return {"PUSH [$]", toString(util::h256(data()))}; + case PushSubSize: + return {"PUSH #[$]", toString(util::h256(data()))}; + case PushProgramSize: + return {"PUSHSIZE", ""}; + case PushLibraryAddress: + return {"PUSHLIB", toString(util::h256(data()))}; + case PushDeployTimeAddress: + return {"PUSHDEPLOYADDRESS", ""}; + case PushImmutable: + return {"PUSHIMMUTABLE", toString(util::h256(data()))}; + case AssignImmutable: + return {"ASSIGNIMMUTABLE", toString(util::h256(data()))}; + case Tag: + return {"tag", util::toString(data())}; + case PushData: + return {"PUSH data", toStringInHex(data())}; + case VerbatimBytecode: + return {"VERBATIM", util::toHex(verbatimData())}; + default: + assertThrow(false, InvalidOpcode, ""); + } +} + void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 796a792e8..4aef82d8b 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -106,6 +106,13 @@ public: u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; } void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared(_data); } + /// This function is used in `Assembly::assemblyJSON`. + /// It returns the name & data of the current assembly item. + /// @returns a pair, where the first element is the json-assembly + /// item name, where second element is the string representation + /// of it's data. + std::pair nameAndData() const; + bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); } /// @returns the instruction of this item (only valid if type() == Operation) diff --git a/test/cmdlineTests/asm_json/output b/test/cmdlineTests/asm_json/output index 26ab87147..000ad1b75 100644 --- a/test/cmdlineTests/asm_json/output +++ b/test/cmdlineTests/asm_json/output @@ -450,9 +450,9 @@ EVM assembly: { "begin": 77, "end": 158, + "jumpType": "[in]", "name": "JUMP", - "source": 0, - "value": "[in]" + "source": 0 }, { "begin": 77, @@ -477,9 +477,9 @@ EVM assembly: { "begin": 77, "end": 158, + "jumpType": "[in]", "name": "JUMP", - "source": 0, - "value": "[in]" + "source": 0 }, { "begin": 77, @@ -555,9 +555,9 @@ EVM assembly: { "begin": 118, "end": 125, + "jumpType": "[in]", "name": "JUMP", - "source": 0, - "value": "[in]" + "source": 0 }, { "begin": 118, @@ -657,9 +657,9 @@ EVM assembly: { "begin": 77, "end": 158, + "jumpType": "[out]", "name": "JUMP", - "source": 0, - "value": "[out]" + "source": 0 }, { "begin": 88, @@ -752,9 +752,9 @@ EVM assembly: { "begin": 334, "end": 411, + "jumpType": "[out]", "name": "JUMP", - "source": 1, - "value": "[out]" + "source": 1 }, { "begin": 417, @@ -792,9 +792,9 @@ EVM assembly: { "begin": 490, "end": 514, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 490, @@ -875,9 +875,9 @@ EVM assembly: { "begin": 417, "end": 539, + "jumpType": "[out]", "name": "JUMP", - "source": 1, - "value": "[out]" + "source": 1 }, { "begin": 545, @@ -946,9 +946,9 @@ EVM assembly: { "begin": 645, "end": 678, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 645, @@ -990,9 +990,9 @@ EVM assembly: { "begin": 545, "end": 684, + "jumpType": "[out]", "name": "JUMP", - "source": 1, - "value": "[out]" + "source": 1 }, { "begin": 690, @@ -1081,9 +1081,9 @@ EVM assembly: { "begin": 804, "end": 883, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 804, @@ -1159,9 +1159,9 @@ EVM assembly: { "begin": 949, "end": 1002, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 949, @@ -1221,9 +1221,9 @@ EVM assembly: { "begin": 690, "end": 1019, + "jumpType": "[out]", "name": "JUMP", - "source": 1, - "value": "[out]" + "source": 1 }, { "begin": 1025, @@ -1341,9 +1341,9 @@ EVM assembly: { "begin": 1270, "end": 1290, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 1270, @@ -1393,9 +1393,9 @@ EVM assembly: { "begin": 1304, "end": 1324, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 1304, @@ -1489,9 +1489,9 @@ EVM assembly: { "begin": 1464, "end": 1482, + "jumpType": "[in]", "name": "JUMP", - "source": 1, - "value": "[in]" + "source": 1 }, { "begin": 1464, @@ -1576,11 +1576,16 @@ EVM assembly: { "begin": 1211, "end": 1516, + "jumpType": "[out]", "name": "JUMP", - "source": 1, - "value": "[out]" + "source": 1 } ] } - } + }, + "sourceList": + [ + "asm_json/input.sol", + "#utility.yul" + ] } diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 3a3167816..680531799 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -56,7 +56,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) { map indices = { { "root.asm", 0 }, - { "sub.asm", 1 } + { "sub.asm", 1 }, + { "verbatim.asm", 2 } }; Assembly _assembly{false, {}}; auto root_asm = make_shared("root.asm"); @@ -65,11 +66,22 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) Assembly _subAsm{false, {}}; auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); + + Assembly _verbatimAsm(true, ""); + auto verbatim_asm = make_shared("verbatim.asm"); + _verbatimAsm.setSourceLocation({8, 18, verbatim_asm}); + // PushImmutable _subAsm.appendImmutable("someImmutable"); + _subAsm.append(AssemblyItem(PushTag, 0)); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); + _verbatimAsm.appendVerbatim({0xff,0xff}, 0, 0); + _verbatimAsm.appendVerbatim({0x74, 0x65, 0x73, 0x74}, 0, 1); + _verbatimAsm.append(Instruction::MSTORE); + shared_ptr _verbatimAsmPtr = make_shared(_verbatimAsm); + // Tag auto tag = _assembly.newTag(); _assembly.append(tag); @@ -77,7 +89,10 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) _assembly.append(u256(1)); _assembly.append(u256(2)); // Push - _assembly.append(Instruction::KECCAK256); + auto keccak256 = AssemblyItem(Instruction::KECCAK256); + _assembly.m_currentModifierDepth = 1; + _assembly.append(keccak256); + _assembly.m_currentModifierDepth = 0; // PushProgramSize _assembly.appendProgramSize(); // PushLibraryAddress @@ -90,6 +105,10 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) auto sub = _assembly.appendSubroutine(_subAsmPtr); // PushSub _assembly.pushSubroutineOffset(static_cast(sub.data())); + // PushSubSize + auto verbatim_sub = _assembly.appendSubroutine(_verbatimAsmPtr); + // PushSub + _assembly.pushSubroutineOffset(static_cast(verbatim_sub.data())); // PushDeployTimeAddress _assembly.append(PushDeployTimeAddress); // AssignImmutable. @@ -102,16 +121,21 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) _assembly.appendToAuxiliaryData(bytes{0x42, 0x66}); _assembly.appendToAuxiliaryData(bytes{0xee, 0xaa}); + _assembly.m_currentModifierDepth = 2; + _assembly.appendJump(tag); + _assembly.m_currentModifierDepth = 0; + checkCompilation(_assembly); BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), - "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" - "6000566067602260457300000000000000000000000000000000000000005050" + "5b6001600220607f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" + "60005660776024604c600760707300000000000000000000000000000000000000005050" "600260010152" - "00fe" + "006000" + "56fe" "7f0000000000000000000000000000000000000000000000000000000000000000" - "fe010203044266eeaa" + "6000feffff7465737452010203044266eeaa" ); BOOST_CHECK_EQUAL( _assembly.assemblyString(), @@ -124,30 +148,40 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) " data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n" " dataSize(sub_0)\n" " dataOffset(sub_0)\n" + " dataSize(sub_1)\n" + " dataOffset(sub_1)\n" " deployTimeAddress()\n" " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" " 0x02\n" " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " stop\n" + " jump(tag_1)\n" "stop\n" "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n" "\n" "sub_0: assembly {\n" " /* \"sub.asm\":6:8 */\n" " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " tag_0\n" " invalid\n" "}\n" "\n" + "sub_1: assembly {\n" + " /* \"verbatim.asm\":8:18 */\n" + " verbatimbytecode_ffff\n" + " verbatimbytecode_74657374\n" + " mstore\n" + "}\n" + "\n" "auxdata: 0x4266eeaa\n" ); - BOOST_CHECK_EQUAL( - util::jsonCompactPrint(_assembly.assemblyJSON(indices)), + string json{ "{\".auxdata\":\"4266eeaa\",\".code\":[" "{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"}," "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," - "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"modifierDepth\":1,\"name\":\"KECCAK256\",\"source\":0}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"}," @@ -155,16 +189,28 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000001\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0}," "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," - "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}" + "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"modifierDepth\":2,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},{\"begin\":1,\"end\":3,\"modifierDepth\":2,\"name\":\"JUMP\",\"source\":0}" "],\".data\":{\"0\":{\".code\":[" "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSH [ErrorTag]\",\"source\":1}," "{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}" - "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" - ); + "]}," + "\"1\":{\".code\":[" + "{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"ffff\"}," + "{\"begin\":8,\"end\":18,\"name\":\"VERBATIM\",\"source\":2,\"value\":\"74657374\"}," + "{\"begin\":8,\"end\":18,\"name\":\"MSTORE\",\"source\":2}" + "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"},\"sourceList\":[\"root.asm\",\"sub.asm\",\"verbatim.asm\"]}" + }; + Json::Value jsonValue; + BOOST_CHECK(util::jsonParseStrict(json, jsonValue)); + BOOST_CHECK_EQUAL(util::jsonCompactPrint(_assembly.assemblyJSON(indices)), util::jsonCompactPrint(jsonValue)); } BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) @@ -343,7 +389,7 @@ BOOST_AUTO_TEST_CASE(immutable) "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"}," "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}" - "]}}}" + "]}},\"sourceList\":[\"root.asm\",\"sub.asm\"]}" ); } From 54fa215885b568b0e6c14951c6ce412ed67c0ff2 Mon Sep 17 00:00:00 2001 From: aathan Date: Wed, 23 Mar 2022 17:15:17 -0700 Subject: [PATCH 11/51] Update mapping-types.rst typo --- docs/types/mapping-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 1c94b20ab..c59d6e845 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -119,7 +119,7 @@ Iterable Mappings You cannot iterate over mappings, i.e. you cannot enumerate their keys. It is possible, though, to implement a data structure on top of them and iterate over that. For example, the code below implements an -``IterableMapping`` library that the ``User`` contract then adds data too, and +``IterableMapping`` library that the ``User`` contract then adds data to, and the ``sum`` function iterates over to sum all the values. .. code-block:: solidity From 4c6066bfad29941b8011542951ee7ef99f73abdc Mon Sep 17 00:00:00 2001 From: nishant-sachdeva Date: Mon, 7 Mar 2022 18:38:34 +0530 Subject: [PATCH 12/51] typeCheckAbiEncodeCallFunction should type check the arguments on functionPointerType->asExternallyCallableFunction instead of teh plain function type --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 51 ++++++++++--------- .../abiEncoder/abi_encode_conversions.sol | 35 +++++++++++++ .../abi_encode_conversions_address.sol | 11 ++++ ...i_encode_conversions_function_pointers.sol | 16 ++++++ ...ode_convert_address_to_address_payable.sol | 11 ---- ..._pointer_to_different_function_pointer.sol | 13 ----- ...convert_function_to_different_function.sol | 13 ----- ...ode_convert_string_to_different_string.sol | 12 ----- 9 files changed, 90 insertions(+), 73 deletions(-) create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol create mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol delete mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol delete mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol delete mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol delete mode 100644 test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol diff --git a/Changelog.md b/Changelog.md index 33291c6bc..8f666eac1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Assembly-Json: Fix assembly json export to store jump types of operations in `jumpType` field instead of `value`. +* TypeChecker: Convert parameters of function type to how they would be called for ``abi.encodeCall``. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 43334563b..da24782b4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2111,57 +2111,60 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa return; } - auto const functionPointerType = dynamic_cast(type(*arguments.front())); - - if (!functionPointerType) + FunctionType const* externalFunctionType = nullptr; + if (auto const functionPointerType = dynamic_cast(type(*arguments.front()))) + { + // this cannot be a library function, that is checked below + externalFunctionType = functionPointerType->asExternallyCallableFunction(false); + solAssert(externalFunctionType->kind() == functionPointerType->kind()); + } + else { m_errorReporter.typeError( 5511_error, arguments.front()->location(), "Expected first argument to be a function pointer, not \"" + - type(*arguments.front())->canonicalName() + + type(*arguments.front())->toString() + "\"." ); return; } if ( - functionPointerType->kind() != FunctionType::Kind::External && - functionPointerType->kind() != FunctionType::Kind::Declaration + externalFunctionType->kind() != FunctionType::Kind::External && + externalFunctionType->kind() != FunctionType::Kind::Declaration ) { string msg = "Expected regular external function type, or external view on public function."; - if (functionPointerType->kind() == FunctionType::Kind::Internal) + if (externalFunctionType->kind() == FunctionType::Kind::Internal) msg += " Provided internal function."; - else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall) + else if (externalFunctionType->kind() == FunctionType::Kind::DelegateCall) msg += " Cannot use library functions for abi.encodeCall."; - else if (functionPointerType->kind() == FunctionType::Kind::Creation) + else if (externalFunctionType->kind() == FunctionType::Kind::Creation) msg += " Provided creation function."; else msg += " Cannot use special function."; SecondarySourceLocation ssl{}; - if (functionPointerType->hasDeclaration()) + if (externalFunctionType->hasDeclaration()) { - ssl.append("Function is declared here:", functionPointerType->declaration().location()); + ssl.append("Function is declared here:", externalFunctionType->declaration().location()); if ( - functionPointerType->declaration().visibility() == Visibility::Public && - functionPointerType->declaration().scope() == m_currentContract + externalFunctionType->declaration().visibility() == Visibility::Public && + externalFunctionType->declaration().scope() == m_currentContract ) msg += " Did you forget to prefix \"this.\"?"; else if (util::contains( m_currentContract->annotation().linearizedBaseContracts, - functionPointerType->declaration().scope() - ) && functionPointerType->declaration().scope() != m_currentContract) + externalFunctionType->declaration().scope() + ) && externalFunctionType->declaration().scope() != m_currentContract) msg += " Functions from base contracts have to be external."; } m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg); return; } - - solAssert(!functionPointerType->takesArbitraryParameters(), "Function must have fixed parameters."); - + solAssert(!externalFunctionType->takesArbitraryParameters(), "Function must have fixed parameters."); // Tuples with only one component become that component vector> callArguments; @@ -2174,14 +2177,14 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa else callArguments.push_back(arguments[1]); - if (functionPointerType->parameterTypes().size() != callArguments.size()) + if (externalFunctionType->parameterTypes().size() != callArguments.size()) { if (tupleType) m_errorReporter.typeError( 7788_error, _functionCall.location(), "Expected " + - to_string(functionPointerType->parameterTypes().size()) + + to_string(externalFunctionType->parameterTypes().size()) + " instead of " + to_string(callArguments.size()) + " components for the tuple parameter." @@ -2191,18 +2194,18 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa 7515_error, _functionCall.location(), "Expected a tuple with " + - to_string(functionPointerType->parameterTypes().size()) + + to_string(externalFunctionType->parameterTypes().size()) + " components instead of a single non-tuple parameter." ); } // Use min() to check as much as we can before failing fatally - size_t const numParameters = min(callArguments.size(), functionPointerType->parameterTypes().size()); + size_t const numParameters = min(callArguments.size(), externalFunctionType->parameterTypes().size()); for (size_t i = 0; i < numParameters; i++) { Type const& argType = *type(*callArguments[i]); - BoolResult result = argType.isImplicitlyConvertibleTo(*functionPointerType->parameterTypes()[i]); + BoolResult result = argType.isImplicitlyConvertibleTo(*externalFunctionType->parameterTypes()[i]); if (!result) m_errorReporter.typeError( 5407_error, @@ -2212,7 +2215,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa " from \"" + argType.toString() + "\" to \"" + - functionPointerType->parameterTypes()[i]->toString() + + externalFunctionType->parameterTypes()[i]->toString() + "\"" + (result.message().empty() ? "." : ": " + result.message()) ); diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol new file mode 100644 index 000000000..710478580 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions.sol @@ -0,0 +1,35 @@ +interface testInterface { + function C(function (string memory) external) external; + function D(string calldata) external; + function E(string memory) external; + function F(address) external; +} + +contract testContract { + function g(string calldata) external {} + function h(string memory) external {} + function i(string calldata str) external { + this.h(str); + this.g(str); + } + function j(string memory str) external { + this.h(str); + this.g(str); + } + function k(string memory str) external pure { + abi.encodeCall(testInterface.D, (str)); + } + string s; + + function main() external view { + abi.encodeCall(testInterface.C, (this.g)); + abi.encodeCall(testInterface.C, (this.h)); + abi.encodeCall(testInterface.D, (s)); + abi.encodeCall(testInterface.E, (s)); + abi.encodeCall(testInterface.F, (payable(address(0)))); + abi.encodeCall(this.i, (s)); + abi.encodeCall(this.j, (s)); + } +} +// ---- +// Warning 6133: (860-914): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol new file mode 100644 index 000000000..b5d53142c --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_address.sol @@ -0,0 +1,11 @@ +interface testInterface { + function A(address payable) external; +} + +contract testContract { + function main() external view { + abi.encodeCall(testInterface.A, (address(0))); + } +} +// ---- +// TypeError 5407: (171-183): Cannot implicitly convert component at position 0 from "address" to "address payable". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol new file mode 100644 index 000000000..242eb61c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_conversions_function_pointers.sol @@ -0,0 +1,16 @@ +interface testInterface { + function B(function (string calldata) external) external; +} + +contract testContract { + function g(string calldata) external {} + function h(string memory) external {} + + function main() external view { + abi.encodeCall(testInterface.B, (this.g)); + abi.encodeCall(testInterface.B, (this.h)); + } +} +// ---- +// TypeError 5407: (278-286): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". +// TypeError 5407: (329-337): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol deleted file mode 100644 index a4c823a85..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_address_to_address_payable.sol +++ /dev/null @@ -1,11 +0,0 @@ -interface I { - function f(address payable) external; -} - -contract C { - function main() external view { - abi.encodeCall(I.f, (address(0))); - } -} -// ---- -// TypeError 5407: (136-148): Cannot implicitly convert component at position 0 from "address" to "address payable". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol deleted file mode 100644 index 7c570e29e..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_pointer_to_different_function_pointer.sol +++ /dev/null @@ -1,13 +0,0 @@ -interface I { - function f(function (string calldata) external) external; -} - -contract C { - function g(string calldata) external {} - - function main() external view { - abi.encodeCall(I.f, (this.g)); - } -} -// ---- -// TypeError 5407: (201-209): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) external". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol deleted file mode 100644 index 7a35bfa55..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_function_to_different_function.sol +++ /dev/null @@ -1,13 +0,0 @@ -interface I { - function f(function (string calldata) external view returns (uint)) external; -} - -contract C { - function g(string memory) external {} - - function main() external view { - abi.encodeCall(I.f, (this.g)); - } -} -// ---- -// TypeError 5407: (219-227): Cannot implicitly convert component at position 0 from "function (string memory) external" to "function (string calldata) view external returns (uint256)". diff --git a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol b/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol deleted file mode 100644 index 72efe4a07..000000000 --- a/test/libsolidity/syntaxTests/abiEncoder/abi_encode_convert_string_to_different_string.sol +++ /dev/null @@ -1,12 +0,0 @@ -interface I { - function f(string calldata) external; -} - -contract C { - string s; - function main() external view { - abi.encodeCall(I.f, (s)); - } -} -// ---- -// TypeError 5407: (150-153): Cannot implicitly convert component at position 0 from "string storage ref" to "string calldata". From 34c06c27a5658b6b65d80d64d9cc684f8e1eddf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 28 Mar 2022 17:56:30 +0200 Subject: [PATCH 13/51] Downgrade ethers.js to 5.6.1 in ens, gnosis, euler, perpetual-pools and uniswap external tests --- test/externalTests/ens.sh | 4 ++++ test/externalTests/euler.sh | 4 ++++ test/externalTests/gnosis.sh | 4 ++++ test/externalTests/perpetual-pools.sh | 4 ++++ test/externalTests/uniswap.sh | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/test/externalTests/ens.sh b/test/externalTests/ens.sh index e4edbf46c..bf018d0ce 100755 --- a/test/externalTests/ens.sh +++ b/test/externalTests/ens.sh @@ -65,6 +65,10 @@ function ens_test force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" yarn install + # With ethers.js 5.6.2 many tests for revert messages fail. + # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. + yarn add ethers@5.6.1 + replace_version_pragmas neutralize_packaged_contracts diff --git a/test/externalTests/euler.sh b/test/externalTests/euler.sh index 39283bcd5..0d63ecd43 100755 --- a/test/externalTests/euler.sh +++ b/test/externalTests/euler.sh @@ -68,6 +68,10 @@ function euler_test force_hardhat_unlimited_contract_size "$config_file" npm install + # With ethers.js 5.6.2 many tests for revert messages fail. + # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. + npm install ethers@5.6.1 + replace_version_pragmas neutralize_packaged_contracts diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index ccfc6cf5c..b8553e4eb 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -81,6 +81,10 @@ function gnosis_safe_test npm install npm install hardhat-gas-reporter + # With ethers.js 5.6.2 many tests for revert messages fail. + # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. + npm install ethers@5.6.1 + replace_version_pragmas [[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist" diff --git a/test/externalTests/perpetual-pools.sh b/test/externalTests/perpetual-pools.sh index 7e4289698..51d729d5d 100755 --- a/test/externalTests/perpetual-pools.sh +++ b/test/externalTests/perpetual-pools.sh @@ -71,6 +71,10 @@ function perpetual_pools_test # TODO: Remove when https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/528 is fixed. yarn add hardhat@2.8.4 + # With ethers.js 5.6.2 many tests for revert messages fail. + # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. + yarn add ethers@5.6.1 + replace_version_pragmas for preset in $SELECTED_PRESETS; do diff --git a/test/externalTests/uniswap.sh b/test/externalTests/uniswap.sh index 2f94c6abd..efea480b6 100755 --- a/test/externalTests/uniswap.sh +++ b/test/externalTests/uniswap.sh @@ -77,6 +77,10 @@ function uniswap_test yarn install yarn add hardhat-gas-reporter + # With ethers.js 5.6.2 many tests for revert messages fail. + # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. + yarn add ethers@5.6.1 + replace_version_pragmas for preset in $SELECTED_PRESETS; do From b445e7e74c47d7627b55d59091d8fe27209a3a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 28 Mar 2022 19:51:46 +0200 Subject: [PATCH 14/51] Revert "perpetual-pools: Use Hardhat 2.8.4 to work around @openzeppelin/hardhat-upgrades's problem with parallel compilation on Hardhat 2.9.0" This reverts commit 30008465bf095291b4ea222a9e5c77a97eb28ced. --- test/externalTests/perpetual-pools.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/externalTests/perpetual-pools.sh b/test/externalTests/perpetual-pools.sh index 51d729d5d..1b54fc577 100755 --- a/test/externalTests/perpetual-pools.sh +++ b/test/externalTests/perpetual-pools.sh @@ -66,11 +66,6 @@ function perpetual_pools_test force_hardhat_unlimited_contract_size "$config_file" "$config_var" yarn install - # The project depends on @openzeppelin/hardhat-upgrades, which is currently not prepared - # for the parallel compilation introduced in Hardhat 2.9.0. - # TODO: Remove when https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/528 is fixed. - yarn add hardhat@2.8.4 - # With ethers.js 5.6.2 many tests for revert messages fail. # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. yarn add ethers@5.6.1 From 5ef0048d85eff80c542a7946c441980890ac153a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 1 Apr 2022 22:27:33 +0200 Subject: [PATCH 15/51] zeppelin: Disable the newly added Polygon-Child test, failing via IR due to Hardhat heuristics --- test/externalTests/zeppelin.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index ab79a8775..ccbf7595a 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -70,6 +70,8 @@ function zeppelin_test # In some cases Hardhat does not detect revert reasons properly via IR. # TODO: Remove this when https://github.com/NomicFoundation/hardhat/issues/2453 gets fixed. sed -i "s|it(\('reverts if the current value is 0'\)|it.skip(\1|g" test/utils/Counters.test.js + # TODO: Remove this when https://github.com/NomicFoundation/hardhat/issues/2115 gets fixed. + sed -i "s|describe\(('Polygon-Child'\)|describe.skip\1|g" test/crosschain/CrossChainEnabled.test.js neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" From 73443c0ada55a7b3f941e034d207df22de2ceb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 1 Apr 2022 22:53:59 +0200 Subject: [PATCH 16/51] elementfi: Switch all presets to compile-only --- test/externalTests/elementfi.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/externalTests/elementfi.sh b/test/externalTests/elementfi.sh index 74ed93a5c..898627da8 100755 --- a/test/externalTests/elementfi.sh +++ b/test/externalTests/elementfi.sh @@ -42,9 +42,9 @@ function elementfi_test local config_file="hardhat.config.ts" local config_var=config - local compile_only_presets=() - local settings_presets=( - "${compile_only_presets[@]}" + local compile_only_presets=( + # ElementFi's test suite is hard-coded for mainnet forked via alchemy.io. + # Locally we can only compile. #ir-no-optimize # Compilation fails with "YulException: Variable var_amount_9311 is 10 slot(s) too deep inside the stack." #ir-optimize-evm-only # Compilation fails with "YulException: Variable var_amount_9311 is 10 slot(s) too deep inside the stack." ir-optimize-evm+yul @@ -52,6 +52,9 @@ function elementfi_test legacy-optimize-evm-only legacy-optimize-evm+yul ) + local settings_presets=( + "${compile_only_presets[@]}" + ) [[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}") print_presets_or_exit "$SELECTED_PRESETS" From 1e7688e8a14b0d8617e6274982dc6b37a3a32457 Mon Sep 17 00:00:00 2001 From: wechman Date: Thu, 24 Mar 2022 12:08:20 +0100 Subject: [PATCH 17/51] Tests for the equivalence of one and two-stage compilation via IR supports multi-file contracts --- test/cmdlineTests.sh | 88 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 6798a83f8..8b6f64630 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -317,47 +317,56 @@ function test_via_ir_equivalence() fail "The second argument must be --optimize if present." fi - local solidity_code="$1" + local solidity_file="$1" local optimize_flag="$2" + filename=$(basename "$1" .sol) + local optimizer_flags=() [[ $optimize_flag == "" ]] || optimizer_flags+=("$optimize_flag") + [[ $optimize_flag == "" ]] || filename+="_optimize" - local ir_output - ir_output=$( - echo "$solidity_code" | - msg_on_error --no-stderr "$SOLC" - --ir-optimized --debug-info location "${optimizer_flags[@]}" | - sed '/^Optimized IR:$/d' - ) + msg_on_error --no-stderr "$SOLC" --ir-optimized --debug-info location "${optimizer_flags[@]}" "$solidity_file" | + sed '/^Optimized IR:$/d' | + awk -v RS= "{print > (\"${filename}_\"NR \".yul\")}" + + for yul_file in $(find . -name "$filename*.yul" | sort -V); do + msg_on_error --no-stderr "$SOLC" --strict-assembly --asm "${optimizer_flags[@]}" "$yul_file" | + sed '/^======= /d' | + sed '/^Text representation:$/d' >> "${yul_file/.yul/.asm}" + done local asm_output_two_stage asm_output_via_ir - asm_output_two_stage=$( - echo "$ir_output" | - msg_on_error --no-stderr "$SOLC" - --strict-assembly --asm "${optimizer_flags[@]}" | - sed '/^======= /d' | - sed '/^Text representation:$/d' - ) + for asm_file in $(find . -name "$filename*.asm" | sort -V); do + asm_output_two_stage+=$(sed '/^asm_output_two_stage:$/d' "$asm_file" | sed '/^=======/d') + done + asm_output_via_ir=$( - echo "$solidity_code" | - msg_on_error --no-stderr "$SOLC" - --via-ir --asm --debug-info location "${optimizer_flags[@]}" | + msg_on_error --no-stderr "$SOLC" --via-ir --asm --debug-info location "${optimizer_flags[@]}" "$solidity_file" | sed '/^======= /d' | - sed '/^EVM assembly:$/d' + sed '/^EVM assembly:$/d' | + sed '/^asm_output_via_ir/d' | + sed '/^=======/d' ) diff_values "$asm_output_two_stage" "$asm_output_via_ir" --ignore-space-change --ignore-blank-lines local bin_output_two_stage bin_output_via_ir - bin_output_two_stage=$( - echo "$ir_output" | - msg_on_error --no-stderr "$SOLC" - --strict-assembly --bin "${optimizer_flags[@]}" | - sed '/^======= /d' | - sed '/^Binary representation:$/d' - ) + + for yul_file in $(find . -name "$filename*.yul" | sort -V); do + bin_output_two_stage+=$( + msg_on_error --no-stderr "$SOLC" --strict-assembly --bin "${optimizer_flags[@]}" "$yul_file" | + sed '/^======= /d' | + sed '/^Binary representation:$/d' | + sed '/^=======/d' + ) + done + bin_output_via_ir=$( - echo "$solidity_code" | - msg_on_error --no-stderr "$SOLC" - --via-ir --bin "${optimizer_flags[@]}" | + msg_on_error --no-stderr "$SOLC" --via-ir --bin "${optimizer_flags[@]}" "$solidity_file" | sed '/^======= /d' | - sed '/^Binary:$/d' + sed '/^Binary:$/d' | + sed '/^=======/d' ) diff_values "$bin_output_two_stage" "$bin_output_via_ir" --ignore-space-change --ignore-blank-lines @@ -589,21 +598,25 @@ printTask "Testing assemble, yul, strict-assembly and optimize..." ) printTask "Testing the eqivalence of --via-ir and a two-stage compilation..." +SOLTMPDIR=$(mktemp -d) ( - printTask " - Smoke test" - test_via_ir_equivalence "contract C {}" - - printTask " - Smoke test (optimized)" - test_via_ir_equivalence "contract C {}" --optimize + cd "$SOLTMPDIR" externalContracts=( - deposit_contract.sol - FixedFeeRegistrar.sol - _stringutils/stringutils.sol + externalTests/solc-js/DAO/TokenCreation.sol + libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol + libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol + libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol + libsolidity/semanticTests/externalContracts/deposit_contract.sol + libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol + libsolidity/semanticTests/externalContracts/snark.sol ) + requiresOptimizer=( - deposit_contract.sol - FixedFeeRegistrar.sol + externalTests/solc-js/DAO/TokenCreation.sol + libsolidity/semanticTests/externalContracts/deposit_contract.sol + libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol + libsolidity/semanticTests/externalContracts/snark.sol ) for contractFile in "${externalContracts[@]}" @@ -611,13 +624,14 @@ printTask "Testing the eqivalence of --via-ir and a two-stage compilation..." if ! [[ "${requiresOptimizer[*]}" =~ $contractFile ]] then printTask " - ${contractFile}" - test_via_ir_equivalence "$(cat "${REPO_ROOT}/test/libsolidity/semanticTests/externalContracts/${contractFile}")" + test_via_ir_equivalence "${REPO_ROOT}/test/${contractFile}" fi printTask " - ${contractFile} (optimized)" - test_via_ir_equivalence "$(cat "${REPO_ROOT}/test/libsolidity/semanticTests/externalContracts/${contractFile}")" --optimize + test_via_ir_equivalence "${REPO_ROOT}/test/${contractFile}" --optimize done ) +rm -r "$SOLTMPDIR" printTask "Testing standard input..." SOLTMPDIR=$(mktemp -d) From 755e043a6d4126c66be163685a02e73be84efc8f Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 21 Mar 2022 17:33:11 +0100 Subject: [PATCH 18/51] Use error message for unary operators. --- libsolidity/analysis/TypeChecker.cpp | 11 +++++++---- test/libsolidity/syntaxTests/negation.sol | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index da24782b4..d83253b29 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1663,19 +1663,22 @@ bool TypeChecker::visit(UnaryOperation const& _operation) else _operation.subExpression().accept(*this); Type const* subExprType = type(_operation.subExpression()); - Type const* t = type(_operation.subExpression())->unaryOperatorResult(op); - if (!t) + TypeResult result = type(_operation.subExpression())->unaryOperatorResult(op); + if (!result) { string description = "Unary operator " + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString(); + if (!result.message().empty()) + description += ". " + result.message(); if (modifying) // Cannot just report the error, ignore the unary operator, and continue, // because the sub-expression was already processed with requireLValue() m_errorReporter.fatalTypeError(9767_error, _operation.location(), description); else m_errorReporter.typeError(4907_error, _operation.location(), description); - t = subExprType; + _operation.annotation().type = subExprType; } - _operation.annotation().type = t; + else + _operation.annotation().type = result.get(); _operation.annotation().isConstant = false; _operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure; _operation.annotation().isLValue = false; diff --git a/test/libsolidity/syntaxTests/negation.sol b/test/libsolidity/syntaxTests/negation.sol index f79920954..4a9d87b8a 100644 --- a/test/libsolidity/syntaxTests/negation.sol +++ b/test/libsolidity/syntaxTests/negation.sol @@ -6,4 +6,4 @@ contract test { } } // ---- -// TypeError 4907: (97-99): Unary operator - cannot be applied to type uint256 +// TypeError 4907: (97-99): Unary operator - cannot be applied to type uint256. Unary negation is only allowed for signed integers. From c7222044a2b0fb0d704c4c8b7795a3f365a4f098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 29 Mar 2022 16:16:45 +0200 Subject: [PATCH 19/51] externalTests.sh: Fix broken ${} syntax in the script --- test/externalTests.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/externalTests.sh b/test/externalTests.sh index 2c4c6f76c..89e7515d4 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -37,16 +37,16 @@ verify_input "$@" printTask "Running external tests..." -"{$REPO_ROOT}/test/externalTests/zeppelin.sh" "$@" -"{$REPO_ROOT}/test/externalTests/gnosis.sh" "$@" -"{$REPO_ROOT}/test/externalTests/colony.sh" "$@" -"{$REPO_ROOT}/test/externalTests/ens.sh" "$@" -"{$REPO_ROOT}/test/externalTests/trident.sh" "$@" -"{$REPO_ROOT}/test/externalTests/euler.sh" "$@" -"{$REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@" -"{$REPO_ROOT}/test/externalTests/bleeps.sh" "$@" -"{$REPO_ROOT}/test/externalTests/pool-together.sh" "$@" -"{$REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@" -"{$REPO_ROOT}/test/externalTests/uniswap.sh" "$@" -"{$REPO_ROOT}/test/externalTests/prb-math.sh" "$@" -"{$REPO_ROOT}/test/externalTests/elementfi.sh" "$@" +"${REPO_ROOT}/test/externalTests/zeppelin.sh" "$@" +"${REPO_ROOT}/test/externalTests/gnosis.sh" "$@" +"${REPO_ROOT}/test/externalTests/colony.sh" "$@" +"${REPO_ROOT}/test/externalTests/ens.sh" "$@" +"${REPO_ROOT}/test/externalTests/trident.sh" "$@" +"${REPO_ROOT}/test/externalTests/euler.sh" "$@" +"${REPO_ROOT}/test/externalTests/yield-liquidator.sh" "$@" +"${REPO_ROOT}/test/externalTests/bleeps.sh" "$@" +"${REPO_ROOT}/test/externalTests/pool-together.sh" "$@" +"${REPO_ROOT}/test/externalTests/perpetual-pools.sh" "$@" +"${REPO_ROOT}/test/externalTests/uniswap.sh" "$@" +"${REPO_ROOT}/test/externalTests/prb-math.sh" "$@" +"${REPO_ROOT}/test/externalTests/elementfi.sh" "$@" From 6f4844648fede1e0c4020a37d5d4f32f92169412 Mon Sep 17 00:00:00 2001 From: aathan Date: Mon, 28 Mar 2022 13:27:57 -0700 Subject: [PATCH 20/51] abstract-contracts.rst: Clarify when a contract may or must be marked abstract --- docs/contracts/abstract-contracts.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst index 34792f58c..fb1beb2da 100644 --- a/docs/contracts/abstract-contracts.rst +++ b/docs/contracts/abstract-contracts.rst @@ -6,12 +6,15 @@ Abstract Contracts ****************** -Contracts need to be marked as abstract when at least one of their functions is not implemented. -Contracts may be marked as abstract even though all functions are implemented. +Contracts must be marked as abstract when at least one of their functions is not implemented or when +they do not provide arguments for all of their base contract constructors. +Even if this is not the case, a contract may still be marked abstract, such as when you do not intend +for the contract to be created directly. Abstract contracts are similar to :ref:`interfaces` but an +interface is more limited in what it can declare. -This can be done by using the ``abstract`` keyword as shown in the following example. Note that this contract needs to be -defined as abstract, because the function ``utterance()`` was defined, but no implementation was -provided (no implementation body ``{ }`` was given). +An abstract contract is declared using the ``abstract`` keyword as shown in the following example. +Note that this contract needs to be defined as abstract, because the function ``utterance()`` is declared, +but no implementation was provided (no implementation body ``{ }`` was given). .. code-block:: solidity From 2fabb3db8f6bbe90e5c3554087069c2f601bff34 Mon Sep 17 00:00:00 2001 From: dtedesco1 Date: Mon, 4 Apr 2022 13:39:32 +0800 Subject: [PATCH 21/51] attempt rebase, fix free function, fix check errors --- .../MultiSigWallet/MultiSigWallet.sol | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol index b4594344c..6f1b8a975 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol @@ -88,18 +88,6 @@ contract MultiSigWallet { _; } - /// @dev Receive function allows to deposit ether. - receive() - external - payable - { - if (msg.value > 0) - emit Deposit(msg.sender, msg.value); - } - - /* - * Public functions - */ /// @dev Contract constructor sets initial owners and required number of confirmations. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. @@ -115,6 +103,19 @@ contract MultiSigWallet { required = _required; } + /// @dev Receive function allows to deposit ether. + receive() + external + payable + { + if (msg.value > 0) + emit Deposit(msg.sender, msg.value); + } + + /* + * Public functions + */ + /// @dev Allows to add a new owner. Transaction has to be sent by wallet. /// @param owner Address of new owner. function addOwner(address owner) @@ -225,9 +226,9 @@ contract MultiSigWallet { notExecuted(transactionId) { if (isConfirmed(transactionId)) { - Transaction storage tx = transactions[transactionId]; - (tx.executed,) = tx.destination.call{value: tx.value}(tx.data); - if (tx.executed) + Transaction storage transaction = transactions[transactionId]; + (transaction.executed,) = transaction.destination.call{value: transaction.value}(transaction.data); + if (transaction.executed) emit Execution(transactionId); else emit ExecutionFailure(transactionId); @@ -249,30 +250,7 @@ contract MultiSigWallet { if (count == required) return true; } - } - - /* - * Internal functions - */ - /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. - /// @param destination Transaction target address. - /// @param value Transaction ether value. - /// @param data Transaction data payload. - /// @return transactionId Returns transaction ID. - function addTransaction(address destination, uint value, bytes memory data) - internal - notNull(destination) - returns (uint transactionId) - { - transactionId = transactionCount; - transactions[transactionId] = Transaction({ - destination: destination, - value: value, - data: data, - executed: false - }); - transactionCount += 1; - emit Submission(transactionId); + return false; } /* @@ -362,4 +340,27 @@ contract MultiSigWallet { for (i=from; i Date: Fri, 22 Oct 2021 02:05:49 +0000 Subject: [PATCH 22/51] Set handlers use unified jsonPrint with prettifier if needed --- Changelog.md | 1 + libsolidity/ast/ASTJsonConverter.cpp | 4 +- libsolidity/ast/ASTJsonConverter.h | 3 +- solc/CommandLineInterface.cpp | 15 +-- test/cmdlineTests/asm_json/args | 2 +- .../asm_json_no_pretty_print/args | 1 + .../asm_json_no_pretty_print/input.sol | 9 ++ .../asm_json_no_pretty_print/output | 4 + .../ast_compact_json_no_pretty_json/args | 1 + .../ast_compact_json_no_pretty_json/input.sol | 4 + .../ast_compact_json_no_pretty_json/output | 5 + .../ast_compact_json_with_base_path/args | 2 +- .../args | 0 .../input.sol | 0 .../output | 2 +- .../combined_json_no_pretty_print/args | 1 + .../combined_json_no_pretty_print/input.sol | 3 + .../combined_json_no_pretty_print/output | 1 + .../combined_json_with_base_path/args | 2 +- .../combined_json_with_base_path/output | 112 +++++++++++++++++- .../combined_json_with_devdoc/args | 1 + .../combined_json_with_devdoc/input.sol | 8 ++ .../combined_json_with_devdoc/output | 25 ++++ .../combined_json_with_userdoc/args | 1 + .../combined_json_with_userdoc/input.sol | 8 ++ .../combined_json_with_userdoc/output | 25 ++++ .../recovery_ast_constructor/args | 2 +- .../standard_json_no_pretty_print/args | 0 .../standard_json_no_pretty_print/input.json | 10 ++ .../standard_json_no_pretty_print/output.json | 1 + .../storage_layout_user_defined/args | 2 +- .../storage_layout_user_defined/output | 85 ++++++++++++- .../args | 1 + .../input.sol | 4 + .../output | 4 + test/libsolidity/ASTJSONTest.cpp | 3 +- 36 files changed, 333 insertions(+), 19 deletions(-) create mode 100644 test/cmdlineTests/asm_json_no_pretty_print/args create mode 100644 test/cmdlineTests/asm_json_no_pretty_print/input.sol create mode 100644 test/cmdlineTests/asm_json_no_pretty_print/output create mode 100644 test/cmdlineTests/ast_compact_json_no_pretty_json/args create mode 100644 test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol create mode 100644 test/cmdlineTests/ast_compact_json_no_pretty_json/output rename test/cmdlineTests/{pretty_json_combined => combined_json_abi}/args (100%) rename test/cmdlineTests/{pretty_json_combined => combined_json_abi}/input.sol (100%) rename test/cmdlineTests/{pretty_json_combined => combined_json_abi}/output (70%) create mode 100644 test/cmdlineTests/combined_json_no_pretty_print/args create mode 100644 test/cmdlineTests/combined_json_no_pretty_print/input.sol create mode 100644 test/cmdlineTests/combined_json_no_pretty_print/output create mode 100644 test/cmdlineTests/combined_json_with_devdoc/args create mode 100644 test/cmdlineTests/combined_json_with_devdoc/input.sol create mode 100644 test/cmdlineTests/combined_json_with_devdoc/output create mode 100644 test/cmdlineTests/combined_json_with_userdoc/args create mode 100644 test/cmdlineTests/combined_json_with_userdoc/input.sol create mode 100644 test/cmdlineTests/combined_json_with_userdoc/output create mode 100644 test/cmdlineTests/standard_json_no_pretty_print/args create mode 100644 test/cmdlineTests/standard_json_no_pretty_print/input.json create mode 100644 test/cmdlineTests/standard_json_no_pretty_print/output.json create mode 100644 test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args create mode 100644 test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol create mode 100644 test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output diff --git a/Changelog.md b/Changelog.md index 8f666eac1..203081823 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: * Peephole Optimizer: Remove operations without side effects before simple terminations. * Assembly-Json: Export: Include source list in `sourceList` field. + * Commandline Interface: option ``--pretty-json`` works also with the following options: ``--abi``, ``--asm-json``, ``--ast-compact-json``, ``--devdoc``, ``--storage-layout``, ``--userdoc``. Bugfixes: diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4338cc64f..f75f5bc15 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -190,9 +190,9 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair #include +#include #include #include @@ -58,7 +59,7 @@ public: std::map _sourceIndices = std::map() ); /// Output the json representation of the AST to _stream. - void print(std::ostream& _stream, ASTNode const& _node); + void print(std::ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format); Json::Value toJson(ASTNode const& _node); template Json::Value toJson(std::vector> const& _nodes) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 55b703392..8cf3a8ebc 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -317,7 +317,7 @@ void CommandLineInterface::handleABI(string const& _contract) if (!m_options.compiler.outputs.abi) return; - string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract))); + string data = jsonPrint(removeNullMembers(m_compiler->contractABI(_contract)), m_options.formatting.json); if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); else @@ -331,7 +331,7 @@ void CommandLineInterface::handleStorageLayout(string const& _contract) if (!m_options.compiler.outputs.storageLayout) return; - string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract))); + string data = jsonPrint(removeNullMembers(m_compiler->storageLayout(_contract)), m_options.formatting.json); if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); else @@ -361,12 +361,13 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra if (enabled) { - std::string output = jsonPrettyPrint( + std::string output = jsonPrint( removeNullMembers( _natspecDev ? m_compiler->natspecDev(_contract) : m_compiler->natspecUser(_contract) - ) + ), + m_options.formatting.json ); if (!m_options.output.dir.empty()) @@ -892,7 +893,7 @@ void CommandLineInterface::handleAst() { stringstream data; string postfix = ""; - ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first)); + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first), m_options.formatting.json); postfix += "_json"; boost::filesystem::path path(sourceCode.first); createFile(path.filename().string() + postfix + ".ast", data.str()); @@ -904,7 +905,7 @@ void CommandLineInterface::handleAst() for (auto const& sourceCode: m_fileReader.sourceUnits()) { sout() << endl << "======= " << sourceCode.first << " =======" << endl; - ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first), m_options.formatting.json); } } } @@ -1146,7 +1147,7 @@ void CommandLineInterface::outputCompilationResults() { string ret; if (m_options.compiler.outputs.asmJson) - ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract))); + ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json); else ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits()); diff --git a/test/cmdlineTests/asm_json/args b/test/cmdlineTests/asm_json/args index 8f4713692..dd9e77b16 100644 --- a/test/cmdlineTests/asm_json/args +++ b/test/cmdlineTests/asm_json/args @@ -1 +1 @@ ---asm-json +--asm-json --pretty-json diff --git a/test/cmdlineTests/asm_json_no_pretty_print/args b/test/cmdlineTests/asm_json_no_pretty_print/args new file mode 100644 index 000000000..8f4713692 --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/args @@ -0,0 +1 @@ +--asm-json diff --git a/test/cmdlineTests/asm_json_no_pretty_print/input.sol b/test/cmdlineTests/asm_json_no_pretty_print/input.sol new file mode 100644 index 000000000..da3a08d7f --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f(uint x) public pure { + x += 42; + require(x > 100); + } +} diff --git a/test/cmdlineTests/asm_json_no_pretty_print/output b/test/cmdlineTests/asm_json_no_pretty_print/output new file mode 100644 index 000000000..232d0bdea --- /dev/null +++ b/test/cmdlineTests/asm_json_no_pretty_print/output @@ -0,0 +1,4 @@ + +======= asm_json_no_pretty_print/input.sol:C ======= +EVM assembly: +{".code":[{"begin":60,"end":160,"name":"PUSH","source":0,"value":"80"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"40"},{"begin":60,"end":160,"name":"MSTORE","source":0},{"begin":60,"end":160,"name":"CALLVALUE","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"ISZERO","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"POP","source":0},{"begin":60,"end":160,"name":"PUSH #[$]","source":0,"value":"0000000000000000000000000000000000000000000000000000000000000000"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"PUSH [$]","source":0,"value":"0000000000000000000000000000000000000000000000000000000000000000"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"CODECOPY","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"RETURN","source":0}],".data":{"0":{".auxdata":"",".code":[{"begin":60,"end":160,"name":"PUSH","source":0,"value":"80"},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"40"},{"begin":60,"end":160,"name":"MSTORE","source":0},{"begin":60,"end":160,"name":"CALLVALUE","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"ISZERO","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"1"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"POP","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"4"},{"begin":60,"end":160,"name":"CALLDATASIZE","source":0},{"begin":60,"end":160,"name":"LT","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"2"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"CALLDATALOAD","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"E0"},{"begin":60,"end":160,"name":"SHR","source":0},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"B3DE648B"},{"begin":60,"end":160,"name":"EQ","source":0},{"begin":60,"end":160,"name":"PUSH [tag]","source":0,"value":"3"},{"begin":60,"end":160,"name":"JUMPI","source":0},{"begin":60,"end":160,"name":"tag","source":0,"value":"2"},{"begin":60,"end":160,"name":"JUMPDEST","source":0},{"begin":60,"end":160,"name":"PUSH","source":0,"value":"0"},{"begin":60,"end":160,"name":"DUP1","source":0},{"begin":60,"end":160,"name":"REVERT","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"3"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"4"},{"begin":77,"end":158,"name":"PUSH","source":0,"value":"4"},{"begin":77,"end":158,"name":"DUP1","source":0},{"begin":77,"end":158,"name":"CALLDATASIZE","source":0},{"begin":77,"end":158,"name":"SUB","source":0},{"begin":77,"end":158,"name":"DUP2","source":0},{"begin":77,"end":158,"name":"ADD","source":0},{"begin":77,"end":158,"name":"SWAP1","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"5"},{"begin":77,"end":158,"name":"SWAP2","source":0},{"begin":77,"end":158,"name":"SWAP1","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"6"},{"begin":77,"end":158,"jumpType":"[in]","name":"JUMP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"5"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"PUSH [tag]","source":0,"value":"7"},{"begin":77,"end":158,"jumpType":"[in]","name":"JUMP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"4"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"STOP","source":0},{"begin":77,"end":158,"name":"tag","source":0,"value":"7"},{"begin":77,"end":158,"name":"JUMPDEST","source":0},{"begin":123,"end":125,"name":"PUSH","source":0,"value":"2A"},{"begin":118,"end":125,"name":"DUP2","source":0},{"begin":118,"end":125,"name":"PUSH [tag]","source":0,"value":"9"},{"begin":118,"end":125,"name":"SWAP2","source":0},{"begin":118,"end":125,"name":"SWAP1","source":0},{"begin":118,"end":125,"name":"PUSH [tag]","source":0,"value":"10"},{"begin":118,"end":125,"jumpType":"[in]","name":"JUMP","source":0},{"begin":118,"end":125,"name":"tag","source":0,"value":"9"},{"begin":118,"end":125,"name":"JUMPDEST","source":0},{"begin":118,"end":125,"name":"SWAP1","source":0},{"begin":118,"end":125,"name":"POP","source":0},{"begin":147,"end":150,"name":"PUSH","source":0,"value":"64"},{"begin":143,"end":144,"name":"DUP2","source":0},{"begin":143,"end":150,"name":"GT","source":0},{"begin":135,"end":151,"name":"PUSH [tag]","source":0,"value":"11"},{"begin":135,"end":151,"name":"JUMPI","source":0},{"begin":135,"end":151,"name":"PUSH","source":0,"value":"0"},{"begin":135,"end":151,"name":"DUP1","source":0},{"begin":135,"end":151,"name":"REVERT","source":0},{"begin":135,"end":151,"name":"tag","source":0,"value":"11"},{"begin":135,"end":151,"name":"JUMPDEST","source":0},{"begin":77,"end":158,"name":"POP","source":0},{"begin":77,"end":158,"jumpType":"[out]","name":"JUMP","source":0},{"begin":88,"end":205,"name":"tag","source":1,"value":"13"},{"begin":88,"end":205,"name":"JUMPDEST","source":1},{"begin":197,"end":198,"name":"PUSH","source":1,"value":"0"},{"begin":194,"end":195,"name":"DUP1","source":1},{"begin":187,"end":199,"name":"REVERT","source":1},{"begin":334,"end":411,"name":"tag","source":1,"value":"15"},{"begin":334,"end":411,"name":"JUMPDEST","source":1},{"begin":371,"end":378,"name":"PUSH","source":1,"value":"0"},{"begin":400,"end":405,"name":"DUP2","source":1},{"begin":389,"end":405,"name":"SWAP1","source":1},{"begin":389,"end":405,"name":"POP","source":1},{"begin":334,"end":411,"name":"SWAP2","source":1},{"begin":334,"end":411,"name":"SWAP1","source":1},{"begin":334,"end":411,"name":"POP","source":1},{"begin":334,"end":411,"jumpType":"[out]","name":"JUMP","source":1},{"begin":417,"end":539,"name":"tag","source":1,"value":"16"},{"begin":417,"end":539,"name":"JUMPDEST","source":1},{"begin":490,"end":514,"name":"PUSH [tag]","source":1,"value":"25"},{"begin":508,"end":513,"name":"DUP2","source":1},{"begin":490,"end":514,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":490,"end":514,"jumpType":"[in]","name":"JUMP","source":1},{"begin":490,"end":514,"name":"tag","source":1,"value":"25"},{"begin":490,"end":514,"name":"JUMPDEST","source":1},{"begin":483,"end":488,"name":"DUP2","source":1},{"begin":480,"end":515,"name":"EQ","source":1},{"begin":470,"end":533,"name":"PUSH [tag]","source":1,"value":"26"},{"begin":470,"end":533,"name":"JUMPI","source":1},{"begin":529,"end":530,"name":"PUSH","source":1,"value":"0"},{"begin":526,"end":527,"name":"DUP1","source":1},{"begin":519,"end":531,"name":"REVERT","source":1},{"begin":470,"end":533,"name":"tag","source":1,"value":"26"},{"begin":470,"end":533,"name":"JUMPDEST","source":1},{"begin":417,"end":539,"name":"POP","source":1},{"begin":417,"end":539,"jumpType":"[out]","name":"JUMP","source":1},{"begin":545,"end":684,"name":"tag","source":1,"value":"17"},{"begin":545,"end":684,"name":"JUMPDEST","source":1},{"begin":591,"end":596,"name":"PUSH","source":1,"value":"0"},{"begin":629,"end":635,"name":"DUP2","source":1},{"begin":616,"end":636,"name":"CALLDATALOAD","source":1},{"begin":607,"end":636,"name":"SWAP1","source":1},{"begin":607,"end":636,"name":"POP","source":1},{"begin":645,"end":678,"name":"PUSH [tag]","source":1,"value":"28"},{"begin":672,"end":677,"name":"DUP2","source":1},{"begin":645,"end":678,"name":"PUSH [tag]","source":1,"value":"16"},{"begin":645,"end":678,"jumpType":"[in]","name":"JUMP","source":1},{"begin":645,"end":678,"name":"tag","source":1,"value":"28"},{"begin":645,"end":678,"name":"JUMPDEST","source":1},{"begin":545,"end":684,"name":"SWAP3","source":1},{"begin":545,"end":684,"name":"SWAP2","source":1},{"begin":545,"end":684,"name":"POP","source":1},{"begin":545,"end":684,"name":"POP","source":1},{"begin":545,"end":684,"jumpType":"[out]","name":"JUMP","source":1},{"begin":690,"end":1019,"name":"tag","source":1,"value":"6"},{"begin":690,"end":1019,"name":"JUMPDEST","source":1},{"begin":749,"end":755,"name":"PUSH","source":1,"value":"0"},{"begin":798,"end":800,"name":"PUSH","source":1,"value":"20"},{"begin":786,"end":795,"name":"DUP3","source":1},{"begin":777,"end":784,"name":"DUP5","source":1},{"begin":773,"end":796,"name":"SUB","source":1},{"begin":769,"end":801,"name":"SLT","source":1},{"begin":766,"end":885,"name":"ISZERO","source":1},{"begin":766,"end":885,"name":"PUSH [tag]","source":1,"value":"30"},{"begin":766,"end":885,"name":"JUMPI","source":1},{"begin":804,"end":883,"name":"PUSH [tag]","source":1,"value":"31"},{"begin":804,"end":883,"name":"PUSH [tag]","source":1,"value":"13"},{"begin":804,"end":883,"jumpType":"[in]","name":"JUMP","source":1},{"begin":804,"end":883,"name":"tag","source":1,"value":"31"},{"begin":804,"end":883,"name":"JUMPDEST","source":1},{"begin":766,"end":885,"name":"tag","source":1,"value":"30"},{"begin":766,"end":885,"name":"JUMPDEST","source":1},{"begin":924,"end":925,"name":"PUSH","source":1,"value":"0"},{"begin":949,"end":1002,"name":"PUSH [tag]","source":1,"value":"32"},{"begin":994,"end":1001,"name":"DUP5","source":1},{"begin":985,"end":991,"name":"DUP3","source":1},{"begin":974,"end":983,"name":"DUP6","source":1},{"begin":970,"end":992,"name":"ADD","source":1},{"begin":949,"end":1002,"name":"PUSH [tag]","source":1,"value":"17"},{"begin":949,"end":1002,"jumpType":"[in]","name":"JUMP","source":1},{"begin":949,"end":1002,"name":"tag","source":1,"value":"32"},{"begin":949,"end":1002,"name":"JUMPDEST","source":1},{"begin":939,"end":1002,"name":"SWAP2","source":1},{"begin":939,"end":1002,"name":"POP","source":1},{"begin":895,"end":1012,"name":"POP","source":1},{"begin":690,"end":1019,"name":"SWAP3","source":1},{"begin":690,"end":1019,"name":"SWAP2","source":1},{"begin":690,"end":1019,"name":"POP","source":1},{"begin":690,"end":1019,"name":"POP","source":1},{"begin":690,"end":1019,"jumpType":"[out]","name":"JUMP","source":1},{"begin":1025,"end":1205,"name":"tag","source":1,"value":"18"},{"begin":1025,"end":1205,"name":"JUMPDEST","source":1},{"begin":1073,"end":1150,"name":"PUSH","source":1,"value":"4E487B7100000000000000000000000000000000000000000000000000000000"},{"begin":1070,"end":1071,"name":"PUSH","source":1,"value":"0"},{"begin":1063,"end":1151,"name":"MSTORE","source":1},{"begin":1170,"end":1174,"name":"PUSH","source":1,"value":"11"},{"begin":1167,"end":1168,"name":"PUSH","source":1,"value":"4"},{"begin":1160,"end":1175,"name":"MSTORE","source":1},{"begin":1194,"end":1198,"name":"PUSH","source":1,"value":"24"},{"begin":1191,"end":1192,"name":"PUSH","source":1,"value":"0"},{"begin":1184,"end":1199,"name":"REVERT","source":1},{"begin":1211,"end":1516,"name":"tag","source":1,"value":"10"},{"begin":1211,"end":1516,"name":"JUMPDEST","source":1},{"begin":1251,"end":1254,"name":"PUSH","source":1,"value":"0"},{"begin":1270,"end":1290,"name":"PUSH [tag]","source":1,"value":"35"},{"begin":1288,"end":1289,"name":"DUP3","source":1},{"begin":1270,"end":1290,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":1270,"end":1290,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1270,"end":1290,"name":"tag","source":1,"value":"35"},{"begin":1270,"end":1290,"name":"JUMPDEST","source":1},{"begin":1265,"end":1290,"name":"SWAP2","source":1},{"begin":1265,"end":1290,"name":"POP","source":1},{"begin":1304,"end":1324,"name":"PUSH [tag]","source":1,"value":"36"},{"begin":1322,"end":1323,"name":"DUP4","source":1},{"begin":1304,"end":1324,"name":"PUSH [tag]","source":1,"value":"15"},{"begin":1304,"end":1324,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1304,"end":1324,"name":"tag","source":1,"value":"36"},{"begin":1304,"end":1324,"name":"JUMPDEST","source":1},{"begin":1299,"end":1324,"name":"SWAP3","source":1},{"begin":1299,"end":1324,"name":"POP","source":1},{"begin":1458,"end":1459,"name":"DUP3","source":1},{"begin":1390,"end":1456,"name":"PUSH","source":1,"value":"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"},{"begin":1386,"end":1460,"name":"SUB","source":1},{"begin":1383,"end":1384,"name":"DUP3","source":1},{"begin":1380,"end":1461,"name":"GT","source":1},{"begin":1377,"end":1484,"name":"ISZERO","source":1},{"begin":1377,"end":1484,"name":"PUSH [tag]","source":1,"value":"37"},{"begin":1377,"end":1484,"name":"JUMPI","source":1},{"begin":1464,"end":1482,"name":"PUSH [tag]","source":1,"value":"38"},{"begin":1464,"end":1482,"name":"PUSH [tag]","source":1,"value":"18"},{"begin":1464,"end":1482,"jumpType":"[in]","name":"JUMP","source":1},{"begin":1464,"end":1482,"name":"tag","source":1,"value":"38"},{"begin":1464,"end":1482,"name":"JUMPDEST","source":1},{"begin":1377,"end":1484,"name":"tag","source":1,"value":"37"},{"begin":1377,"end":1484,"name":"JUMPDEST","source":1},{"begin":1508,"end":1509,"name":"DUP3","source":1},{"begin":1505,"end":1506,"name":"DUP3","source":1},{"begin":1501,"end":1510,"name":"ADD","source":1},{"begin":1494,"end":1510,"name":"SWAP1","source":1},{"begin":1494,"end":1510,"name":"POP","source":1},{"begin":1211,"end":1516,"name":"SWAP3","source":1},{"begin":1211,"end":1516,"name":"SWAP2","source":1},{"begin":1211,"end":1516,"name":"POP","source":1},{"begin":1211,"end":1516,"name":"POP","source":1},{"begin":1211,"end":1516,"jumpType":"[out]","name":"JUMP","source":1}]}},"sourceList":["asm_json_no_pretty_print/input.sol","#utility.yul"]} diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/args b/test/cmdlineTests/ast_compact_json_no_pretty_json/args new file mode 100644 index 000000000..f60d99b44 --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/args @@ -0,0 +1 @@ +--ast-compact-json --allow-paths . diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol b/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol new file mode 100644 index 000000000..f123f6c8b --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/ast_compact_json_no_pretty_json/output b/test/cmdlineTests/ast_compact_json_no_pretty_json/output new file mode 100644 index 000000000..abdc17125 --- /dev/null +++ b/test/cmdlineTests/ast_compact_json_no_pretty_json/output @@ -0,0 +1,5 @@ +JSON AST (compact format): + + +======= ast_compact_json_no_pretty_json/input.sol ======= +{"absolutePath":"ast_compact_json_no_pretty_json/input.sol","exportedSymbols":{"C":[2]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":2,"linearizedBaseContracts":[2],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":3,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"} diff --git a/test/cmdlineTests/ast_compact_json_with_base_path/args b/test/cmdlineTests/ast_compact_json_with_base_path/args index 103ae311a..a30ef24c0 100644 --- a/test/cmdlineTests/ast_compact_json_with_base_path/args +++ b/test/cmdlineTests/ast_compact_json_with_base_path/args @@ -1 +1 @@ ---ast-compact-json --base-path . --allow-paths . +--ast-compact-json --pretty-json --base-path . --allow-paths . diff --git a/test/cmdlineTests/pretty_json_combined/args b/test/cmdlineTests/combined_json_abi/args similarity index 100% rename from test/cmdlineTests/pretty_json_combined/args rename to test/cmdlineTests/combined_json_abi/args diff --git a/test/cmdlineTests/pretty_json_combined/input.sol b/test/cmdlineTests/combined_json_abi/input.sol similarity index 100% rename from test/cmdlineTests/pretty_json_combined/input.sol rename to test/cmdlineTests/combined_json_abi/input.sol diff --git a/test/cmdlineTests/pretty_json_combined/output b/test/cmdlineTests/combined_json_abi/output similarity index 70% rename from test/cmdlineTests/pretty_json_combined/output rename to test/cmdlineTests/combined_json_abi/output index 6bed3b94d..c775c6d72 100644 --- a/test/cmdlineTests/pretty_json_combined/output +++ b/test/cmdlineTests/combined_json_abi/output @@ -1,7 +1,7 @@ { "contracts": { - "pretty_json_combined/input.sol:C": + "combined_json_abi/input.sol:C": { "abi": [] } diff --git a/test/cmdlineTests/combined_json_no_pretty_print/args b/test/cmdlineTests/combined_json_no_pretty_print/args new file mode 100644 index 000000000..92b156465 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/args @@ -0,0 +1 @@ +--combined-json abi diff --git a/test/cmdlineTests/combined_json_no_pretty_print/input.sol b/test/cmdlineTests/combined_json_no_pretty_print/input.sol new file mode 100644 index 000000000..625af5682 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/input.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C {} diff --git a/test/cmdlineTests/combined_json_no_pretty_print/output b/test/cmdlineTests/combined_json_no_pretty_print/output new file mode 100644 index 000000000..081dc4fa4 --- /dev/null +++ b/test/cmdlineTests/combined_json_no_pretty_print/output @@ -0,0 +1 @@ +{"contracts":{"combined_json_no_pretty_print/input.sol:C":{"abi":[]}},"version": ""} diff --git a/test/cmdlineTests/combined_json_with_base_path/args b/test/cmdlineTests/combined_json_with_base_path/args index 0dec582ba..4e1f48462 100644 --- a/test/cmdlineTests/combined_json_with_base_path/args +++ b/test/cmdlineTests/combined_json_with_base_path/args @@ -1 +1 @@ ---combined-json ast --base-path . --allow-paths . +--combined-json ast --pretty-json --base-path . --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_base_path/output b/test/cmdlineTests/combined_json_with_base_path/output index fe00b71a0..9acdea553 100644 --- a/test/cmdlineTests/combined_json_with_base_path/output +++ b/test/cmdlineTests/combined_json_with_base_path/output @@ -1 +1,111 @@ -{"contracts":{"combined_json_with_base_path/c.sol:C":{}},"sourceList":["combined_json_with_base_path/c.sol","combined_json_with_base_path/input.sol"],"sources":{"combined_json_with_base_path/c.sol":{"AST":{"absolutePath":"combined_json_with_base_path/c.sol","exportedSymbols":{"C":[5]},"id":6,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":4,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":5,"linearizedBaseContracts":[5],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":6,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"}},"combined_json_with_base_path/input.sol":{"AST":{"absolutePath":"combined_json_with_base_path/input.sol","exportedSymbols":{"C":[5]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:1"},{"absolutePath":"combined_json_with_base_path/c.sol","file":"./c.sol","id":2,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":3,"sourceUnit":6,"src":"60:17:1","symbolAliases":[],"unitAlias":""}],"src":"36:42:1"}}},"version": ""} +{ + "contracts": + { + "combined_json_with_base_path/c.sol:C": {} + }, + "sourceList": + [ + "combined_json_with_base_path/c.sol", + "combined_json_with_base_path/input.sol" + ], + "sources": + { + "combined_json_with_base_path/c.sol": + { + "AST": + { + "absolutePath": "combined_json_with_base_path/c.sol", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 6, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 4, + "literals": + [ + "solidity", + ">=", + "0.0" + ], + "nodeType": "PragmaDirective", + "src": "36:22:0" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nameLocation": "69:1:0", + "nodeType": "ContractDefinition", + "nodes": [], + "scope": 6, + "src": "60:13:0", + "usedErrors": [] + } + ], + "src": "36:38:0" + } + }, + "combined_json_with_base_path/input.sol": + { + "AST": + { + "absolutePath": "combined_json_with_base_path/input.sol", + "exportedSymbols": + { + "C": + [ + 5 + ] + }, + "id": 3, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 1, + "literals": + [ + "solidity", + ">=", + "0.0" + ], + "nodeType": "PragmaDirective", + "src": "36:22:1" + }, + { + "absolutePath": "combined_json_with_base_path/c.sol", + "file": "./c.sol", + "id": 2, + "nameLocation": "-1:-1:-1", + "nodeType": "ImportDirective", + "scope": 3, + "sourceUnit": 6, + "src": "60:17:1", + "symbolAliases": [], + "unitAlias": "" + } + ], + "src": "36:42:1" + } + } + }, + "version": "" +} diff --git a/test/cmdlineTests/combined_json_with_devdoc/args b/test/cmdlineTests/combined_json_with_devdoc/args new file mode 100644 index 000000000..dc55cdec7 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/args @@ -0,0 +1 @@ +--combined-json devdoc --pretty-json --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_devdoc/input.sol b/test/cmdlineTests/combined_json_with_devdoc/input.sol new file mode 100644 index 000000000..6787b59b1 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +/// @dev This is devdoc. +contract C {} + +/// And this is a notice. +contract D {} diff --git a/test/cmdlineTests/combined_json_with_devdoc/output b/test/cmdlineTests/combined_json_with_devdoc/output new file mode 100644 index 000000000..d3df44a90 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_devdoc/output @@ -0,0 +1,25 @@ +{ + "contracts": + { + "combined_json_with_devdoc/input.sol:C": + { + "devdoc": + { + "details": "This is devdoc.", + "kind": "dev", + "methods": {}, + "version": 1 + } + }, + "combined_json_with_devdoc/input.sol:D": + { + "devdoc": + { + "kind": "dev", + "methods": {}, + "version": 1 + } + } + }, + "version": "" +} diff --git a/test/cmdlineTests/combined_json_with_userdoc/args b/test/cmdlineTests/combined_json_with_userdoc/args new file mode 100644 index 000000000..b7c2697aa --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/args @@ -0,0 +1 @@ +--combined-json userdoc --pretty-json --allow-paths . diff --git a/test/cmdlineTests/combined_json_with_userdoc/input.sol b/test/cmdlineTests/combined_json_with_userdoc/input.sol new file mode 100644 index 000000000..c6d37c8d4 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +/// @notice Description for users. +contract C {} + +/// @dev Description for developers. +contract D {} diff --git a/test/cmdlineTests/combined_json_with_userdoc/output b/test/cmdlineTests/combined_json_with_userdoc/output new file mode 100644 index 000000000..650981d67 --- /dev/null +++ b/test/cmdlineTests/combined_json_with_userdoc/output @@ -0,0 +1,25 @@ +{ + "contracts": + { + "combined_json_with_userdoc/input.sol:C": + { + "userdoc": + { + "kind": "user", + "methods": {}, + "notice": "Description for users.", + "version": 1 + } + }, + "combined_json_with_userdoc/input.sol:D": + { + "userdoc": + { + "kind": "user", + "methods": {}, + "version": 1 + } + } + }, + "version": "" +} diff --git a/test/cmdlineTests/recovery_ast_constructor/args b/test/cmdlineTests/recovery_ast_constructor/args index c7bde9942..8b7c575db 100644 --- a/test/cmdlineTests/recovery_ast_constructor/args +++ b/test/cmdlineTests/recovery_ast_constructor/args @@ -1 +1 @@ ---error-recovery --ast-compact-json --hashes +--error-recovery --ast-compact-json --pretty-json --hashes diff --git a/test/cmdlineTests/standard_json_no_pretty_print/args b/test/cmdlineTests/standard_json_no_pretty_print/args new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_json_no_pretty_print/input.json b/test/cmdlineTests/standard_json_no_pretty_print/input.json new file mode 100644 index 000000000..e9dc4cf38 --- /dev/null +++ b/test/cmdlineTests/standard_json_no_pretty_print/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C {}" + } + } +} diff --git a/test/cmdlineTests/standard_json_no_pretty_print/output.json b/test/cmdlineTests/standard_json_no_pretty_print/output.json new file mode 100644 index 000000000..59b90c8cc --- /dev/null +++ b/test/cmdlineTests/standard_json_no_pretty_print/output.json @@ -0,0 +1 @@ +{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_user_defined/args b/test/cmdlineTests/storage_layout_user_defined/args index f69ac4e39..0538465fe 100644 --- a/test/cmdlineTests/storage_layout_user_defined/args +++ b/test/cmdlineTests/storage_layout_user_defined/args @@ -1 +1 @@ ---storage-layout +--storage-layout --pretty-json diff --git a/test/cmdlineTests/storage_layout_user_defined/output b/test/cmdlineTests/storage_layout_user_defined/output index 0f6890dae..f79b8413e 100644 --- a/test/cmdlineTests/storage_layout_user_defined/output +++ b/test/cmdlineTests/storage_layout_user_defined/output @@ -1,4 +1,87 @@ ======= storage_layout_user_defined/input.sol:C ======= Contract Storage Layout: -{"storage":[{"astId":7,"contract":"storage_layout_user_defined/input.sol:C","label":"a","offset":0,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":10,"contract":"storage_layout_user_defined/input.sol:C","label":"b","offset":16,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":13,"contract":"storage_layout_user_defined/input.sol:C","label":"c","offset":0,"slot":"1","type":"t_userDefinedValueType(MyInt128)2"},{"astId":16,"contract":"storage_layout_user_defined/input.sol:C","label":"d","offset":16,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":19,"contract":"storage_layout_user_defined/input.sol:C","label":"e","offset":17,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":22,"contract":"storage_layout_user_defined/input.sol:C","label":"f","offset":18,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":25,"contract":"storage_layout_user_defined/input.sol:C","label":"g","offset":19,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":28,"contract":"storage_layout_user_defined/input.sol:C","label":"h","offset":20,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"}],"types":{"t_userDefinedValueType(MyInt128)2":{"encoding":"inplace","label":"MyInt128","numberOfBytes":"16"},"t_userDefinedValueType(MyInt8)4":{"encoding":"inplace","label":"MyInt8","numberOfBytes":"1"}}} +{ + "storage": + [ + { + "astId": 7, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "a", + "offset": 0, + "slot": "0", + "type": "t_userDefinedValueType(MyInt128)2" + }, + { + "astId": 10, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "b", + "offset": 16, + "slot": "0", + "type": "t_userDefinedValueType(MyInt128)2" + }, + { + "astId": 13, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "c", + "offset": 0, + "slot": "1", + "type": "t_userDefinedValueType(MyInt128)2" + }, + { + "astId": 16, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "d", + "offset": 16, + "slot": "1", + "type": "t_userDefinedValueType(MyInt8)4" + }, + { + "astId": 19, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "e", + "offset": 17, + "slot": "1", + "type": "t_userDefinedValueType(MyInt8)4" + }, + { + "astId": 22, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "f", + "offset": 18, + "slot": "1", + "type": "t_userDefinedValueType(MyInt8)4" + }, + { + "astId": 25, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "g", + "offset": 19, + "slot": "1", + "type": "t_userDefinedValueType(MyInt8)4" + }, + { + "astId": 28, + "contract": "storage_layout_user_defined/input.sol:C", + "label": "h", + "offset": 20, + "slot": "1", + "type": "t_userDefinedValueType(MyInt8)4" + } + ], + "types": + { + "t_userDefinedValueType(MyInt128)2": + { + "encoding": "inplace", + "label": "MyInt128", + "numberOfBytes": "16" + }, + "t_userDefinedValueType(MyInt8)4": + { + "encoding": "inplace", + "label": "MyInt8", + "numberOfBytes": "1" + } + } +} diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args new file mode 100644 index 000000000..f69ac4e39 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/args @@ -0,0 +1 @@ +--storage-layout diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol new file mode 100644 index 000000000..c80f7c736 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL v3 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output new file mode 100644 index 000000000..1be4da0b6 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined_no_pretty_print/output @@ -0,0 +1,4 @@ + +======= storage_layout_user_defined_no_pretty_print/input.sol:C ======= +Contract Storage Layout: +{"storage":[]} diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 14e2c2a8a..a509adde6 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -248,7 +249,7 @@ bool ASTJSONTest::runTest( for (size_t i = 0; i < m_sources.size(); i++) { ostringstream result; - ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); + ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first), JsonFormat{ JsonFormat::Pretty }); _variant.result += result.str(); if (i != m_sources.size() - 1) _variant.result += ","; From 2d07ea3d420f096a4423710a62f5a6464b6c936a Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Tue, 5 Apr 2022 13:38:59 +0200 Subject: [PATCH 23/51] Implements $/setTrace and uses trace logging. For helping the caller to know in advance how many files are expected with a response for publishDiagnostics. --- libsolidity/lsp/LanguageServer.cpp | 23 +++++++++++++++++++++++ libsolidity/lsp/LanguageServer.h | 1 + libsolidity/lsp/Transport.cpp | 12 ++++++++++++ libsolidity/lsp/Transport.h | 16 ++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/libsolidity/lsp/LanguageServer.cpp b/libsolidity/lsp/LanguageServer.cpp index 671476ab0..8a42ca104 100644 --- a/libsolidity/lsp/LanguageServer.cpp +++ b/libsolidity/lsp/LanguageServer.cpp @@ -76,6 +76,7 @@ LanguageServer::LanguageServer(Transport& _transport): {"exit", [this](auto, auto) { m_state = (m_state == State::ShutdownRequested ? State::ExitRequested : State::ExitWithoutShutdown); }}, {"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)}, {"initialized", [](auto, auto) {}}, + {"$/setTrace", bind(&LanguageServer::setTrace, this, _2)}, {"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }}, {"textDocument/definition", GotoDefinition(*this) }, {"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)}, @@ -166,6 +167,13 @@ void LanguageServer::compileAndUpdateDiagnostics() diagnosticsBySourceUnit[*location->sourceName].append(jsonDiag); } + if (m_client.traceValue() != TraceValue::Off) + { + Json::Value extra; + extra["openFileCount"] = Json::UInt64(diagnosticsBySourceUnit.size()); + m_client.trace("Number of currently open files: " + to_string(diagnosticsBySourceUnit.size()), extra); + } + m_nonemptyDiagnostics.clear(); for (auto&& [sourceUnitName, diagnostics]: diagnosticsBySourceUnit) { @@ -273,6 +281,21 @@ void LanguageServer::handleWorkspaceDidChangeConfiguration(Json::Value const& _a changeConfiguration(_args["settings"]); } +void LanguageServer::setTrace(Json::Value const& _args) +{ + if (!_args["value"].isString()) + // Simply ignore invalid parameter. + return; + + string const stringValue = _args["value"].asString(); + if (stringValue == "off") + m_client.setTrace(TraceValue::Off); + else if (stringValue == "messages") + m_client.setTrace(TraceValue::Messages); + else if (stringValue == "verbose") + m_client.setTrace(TraceValue::Verbose); +} + void LanguageServer::handleTextDocumentDidOpen(Json::Value const& _args) { requireServerInitialized(); diff --git a/libsolidity/lsp/LanguageServer.h b/libsolidity/lsp/LanguageServer.h index d2ad09367..e827f6259 100644 --- a/libsolidity/lsp/LanguageServer.h +++ b/libsolidity/lsp/LanguageServer.h @@ -68,6 +68,7 @@ private: void requireServerInitialized(); void handleInitialize(MessageID _id, Json::Value const& _args); void handleWorkspaceDidChangeConfiguration(Json::Value const& _args); + void setTrace(Json::Value const& _args); void handleTextDocumentDidOpen(Json::Value const& _args); void handleTextDocumentDidChange(Json::Value const& _args); void handleTextDocumentDidClose(Json::Value const& _args); diff --git a/libsolidity/lsp/Transport.cpp b/libsolidity/lsp/Transport.cpp index fcd3c8249..b82b34ec4 100644 --- a/libsolidity/lsp/Transport.cpp +++ b/libsolidity/lsp/Transport.cpp @@ -98,6 +98,18 @@ void IOStreamTransport::error(MessageID _id, ErrorCode _code, string _message) send(move(json), _id); } +void Transport::trace(std::string _message, Json::Value _extra) +{ + if (m_logTrace != TraceValue::Off) + { + Json::Value params; + if (_extra.isObject()) + params = move(_extra); + params["message"] = move(_message); + notify("$/logTrace", move(params)); + } +} + void IOStreamTransport::send(Json::Value _json, MessageID _id) { solAssert(_json.isObject()); diff --git a/libsolidity/lsp/Transport.h b/libsolidity/lsp/Transport.h index 82fe43909..d84edf49d 100644 --- a/libsolidity/lsp/Transport.h +++ b/libsolidity/lsp/Transport.h @@ -34,6 +34,13 @@ namespace solidity::lsp using MessageID = Json::Value; +enum class TraceValue +{ + Off, + Messages, + Verbose +}; + enum class ErrorCode { // Defined by JSON RPC @@ -89,6 +96,15 @@ public: virtual void notify(std::string _method, Json::Value _params) = 0; virtual void reply(MessageID _id, Json::Value _result) = 0; virtual void error(MessageID _id, ErrorCode _code, std::string _message) = 0; + + void trace(std::string _message, Json::Value _extra = Json::nullValue); + + TraceValue traceValue() const noexcept { return m_logTrace; } + void setTrace(TraceValue _value) noexcept { m_logTrace = _value; } + + +private: + TraceValue m_logTrace = TraceValue::Off; }; /** From ee5e878ad794dd38e778d37099373f3eda762d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 9 Feb 2022 18:05:38 +0100 Subject: [PATCH 24/51] benchmark_diff: A Python script for diffing summarized benchmarks from external tests --- scripts/externalTests/benchmark_diff.py | 212 ++++++++++++ .../summarized-benchmarks-branch.json | 100 ++++++ .../summarized-benchmarks-develop.json | 99 ++++++ .../test_externalTests_benchmark_diff.py | 309 ++++++++++++++++++ 4 files changed, 720 insertions(+) create mode 100755 scripts/externalTests/benchmark_diff.py create mode 100644 test/scripts/fixtures/summarized-benchmarks-branch.json create mode 100644 test/scripts/fixtures/summarized-benchmarks-develop.json create mode 100644 test/scripts/test_externalTests_benchmark_diff.py diff --git a/scripts/externalTests/benchmark_diff.py b/scripts/externalTests/benchmark_diff.py new file mode 100755 index 000000000..0669e62eb --- /dev/null +++ b/scripts/externalTests/benchmark_diff.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +from dataclasses import dataclass +from enum import Enum +from pathlib import Path +from typing import Any, Optional, Union +import json +import sys + + +class DifferenceStyle(Enum): + ABSOLUTE = 'absolute' + RELATIVE = 'relative' + HUMANIZED = 'humanized' + + +DEFAULT_RELATIVE_PRECISION = 4 +DEFAULT_DIFFERENCE_STYLE = DifferenceStyle.ABSOLUTE + + +class ValidationError(Exception): + pass + + +class CommandLineError(ValidationError): + pass + + +class BenchmarkDiffer: + difference_style: DifferenceStyle + relative_precision: Optional[int] + + def __init__( + self, + difference_style: DifferenceStyle, + relative_precision: Optional[int], + ): + self.difference_style = difference_style + self.relative_precision = relative_precision + + def run(self, before: Any, after: Any) -> Optional[Union[dict, str, int, float]]: + if not isinstance(before, dict) or not isinstance(after, dict): + return self._diff_scalars(before, after) + + if before.get('version') != after.get('version'): + return self._humanize_diff('!V') + + diff = {} + for key in (set(before) | set(after)) - {'version'}: + value_diff = self.run(before.get(key), after.get(key)) + if value_diff not in [None, {}]: + diff[key] = value_diff + + return diff + + def _diff_scalars(self, before: Any, after: Any) -> Optional[Union[str, int, float, dict]]: + assert not isinstance(before, dict) or not isinstance(after, dict) + + if before is None and after is None: + return {} + if before is None: + return self._humanize_diff('!B') + if after is None: + return self._humanize_diff('!A') + if not isinstance(before, (int, float)) or not isinstance(after, (int, float)): + return self._humanize_diff('!T') + + number_diff = self._diff_numbers(before, after) + if self.difference_style != DifferenceStyle.HUMANIZED: + return number_diff + + return self._humanize_diff(number_diff) + + def _diff_numbers(self, value_before: Union[int, float], value_after: Union[int, float]) -> Union[str, int, float]: + diff: Union[str, int, float] + + if self.difference_style == DifferenceStyle.ABSOLUTE: + diff = value_after - value_before + if isinstance(diff, float) and diff.is_integer(): + diff = int(diff) + + return diff + + if value_before == 0: + if value_after > 0: + return '+INF' + elif value_after < 0: + return '-INF' + else: + return 0 + + diff = (value_after - value_before) / abs(value_before) + if self.relative_precision is not None: + rounded_diff = round(diff, self.relative_precision) + if rounded_diff == 0 and diff < 0: + diff = '-0' + elif rounded_diff == 0 and diff > 0: + diff = '+0' + else: + diff = rounded_diff + + if isinstance(diff, float) and diff.is_integer(): + diff = int(diff) + + return diff + + def _humanize_diff(self, diff: Union[str, int, float]) -> str: + if isinstance(diff, str) and diff.startswith('!'): + return diff + + value: Union[str, int, float] + if isinstance(diff, (int, float)): + value = diff * 100 + if isinstance(value, float) and self.relative_precision is not None: + # The multiplication can result in new significant digits appearing. We need to reround. + # NOTE: round() works fine with negative precision. + value = round(value, self.relative_precision - 2) + if isinstance(value, float) and value.is_integer(): + value = int(value) + prefix = '' + if diff < 0: + prefix = '' + elif diff > 0: + prefix = '+' + else: + value = diff + prefix = '' + + return f"{prefix}{value}%" + + +@dataclass(frozen=True) +class CommandLineOptions: + report_before: Path + report_after: Path + difference_style: DifferenceStyle + relative_precision: int + + +def process_commandline() -> CommandLineOptions: + script_description = ( + "Compares summarized benchmark reports and outputs JSON with the same structure but listing only differences." + ) + + parser = ArgumentParser(description=script_description) + parser.add_argument(dest='report_before', help="Path to a JSON file containing original benchmark results.") + parser.add_argument(dest='report_after', help="Path to a JSON file containing new benchmark results.") + parser.add_argument( + '--style', + dest='difference_style', + choices=[s.value for s in DifferenceStyle], + help=( + "How to present numeric differences: " + f"'{DifferenceStyle.ABSOLUTE.value}' subtracts new from original; " + f"'{DifferenceStyle.RELATIVE.value}' also divides by the original; " + f"'{DifferenceStyle.HUMANIZED.value}' is like relative but value is a percentage and " + "positive/negative changes are emphasized. " + f"(default: '{DEFAULT_DIFFERENCE_STYLE}')." + ) + ) + # NOTE: Negative values are valid for precision. round() handles them in a sensible way. + parser.add_argument( + '--precision', + dest='relative_precision', + type=int, + default=DEFAULT_RELATIVE_PRECISION, + help=( + "Number of significant digits for relative differences. " + f"Note that with --style={DifferenceStyle.HUMANIZED.value} the rounding is applied " + "**before** converting the value to a percentage so you need to add 2. " + f"Has no effect when used together with --style={DifferenceStyle.ABSOLUTE.value}. " + f"(default: {DEFAULT_RELATIVE_PRECISION})" + ) + ) + + options = parser.parse_args() + + if options.difference_style is not None: + difference_style = DifferenceStyle(options.difference_style) + else: + difference_style = DEFAULT_DIFFERENCE_STYLE + + processed_options = CommandLineOptions( + report_before=Path(options.report_before), + report_after=Path(options.report_after), + difference_style=difference_style, + relative_precision=options.relative_precision, + ) + + return processed_options + + +def main(): + try: + options = process_commandline() + + differ = BenchmarkDiffer(options.difference_style, options.relative_precision) + diff = differ.run( + json.loads(options.report_before.read_text('utf-8')), + json.loads(options.report_after.read_text('utf-8')), + ) + + print(json.dumps(diff, indent=4, sort_keys=True)) + + return 0 + except CommandLineError as exception: + print(f"ERROR: {exception}", file=sys.stderr) + return 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test/scripts/fixtures/summarized-benchmarks-branch.json b/test/scripts/fixtures/summarized-benchmarks-branch.json new file mode 100644 index 000000000..0ba46b87d --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmarks-branch.json @@ -0,0 +1,100 @@ +{ + "bleeps": { + "ir-optimize-evm+yul": { + "bytecode_size": 132868, + "deployment_gas": 0, + "method_gas": 39289198, + "version": "bb90cd0" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 137869, + "deployment_gas": 0, + "method_gas": 38863224, + "version": "bb90cd0" + } + }, + "colony": { + "legacy-no-optimize": { + "bytecode_size": 664190, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + } + }, + "elementfi": { + "legacy-no-optimize": { + "bytecode_size": null, + "deployment_gas": 69200158, + "method_gas": null, + "version": "87f8b5e" + }, + "legacy-optimize-evm+yul": { + "deployment_gas": 40951128, + "version": "87f8b5e" + }, + "ir-optimize-evm-only": {}, + "ir-no-optimize": { + "deployment_gas": null, + "method_gas": 2777867251, + "version": "87f8b5e" + } + }, + "euler": { + "ir-no-optimize": { + "bytecode_size": 328540, + "deployment_gas": 61591870, + "method_gas": 3537419168, + "version": "2ef99fc" + }, + "legacy-no-optimize": { + "bytecode_size": 328540, + "deployment_gas": 62590688, + "method_gas": 3537419168, + "version": "2ef99fc" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251, + "version": "2ef99fc" + }, + "legacy-optimize-evm-only": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2978467272, + "version": "2ef99fc" + }, + "ir-optimize-evm-only": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2978467272, + "version": "2ef99fc" + }, + "ir-optimize-evm+yul": { + "bytecode_size": 205211, + "deployment_gas": 39459629, + "method_gas": 2777867251 + } + }, + "gnosis": { + "ir-optimize-evm+yul": { + "bytecode_size": 56069, + "deployment_gas": null, + "method_gas": null, + "version": "ea09294" + } + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + "bytecode_size": 510428, + "deployment_gas": 94501114, + "version": "af7ec04" + } + } +} diff --git a/test/scripts/fixtures/summarized-benchmarks-develop.json b/test/scripts/fixtures/summarized-benchmarks-develop.json new file mode 100644 index 000000000..1961870f3 --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmarks-develop.json @@ -0,0 +1,99 @@ +{ + "bleeps": { + "ir-optimize-evm+yul": { + "bytecode_size": 132165, + "deployment_gas": 0, + "method_gas": 39289935, + "version": "bb90cd0" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 137869, + "deployment_gas": 0, + "method_gas": 38863224, + "version": "bb90cd0" + } + }, + "colony": { + "ir-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 363606, + "deployment_gas": null, + "method_gas": null, + "version": "573399b" + } + }, + "elementfi": { + "legacy-no-optimize": { + "bytecode_size": 890560, + "deployment_gas": null, + "method_gas": null, + "version": "87f8b5e" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 536668, + "version": "87f8b5e" + }, + "legacy-optimize-evm-only": {}, + "ir-no-optimize": { + "bytecode_size": null, + "method_gas": 2777867251, + "version": "87f8b5e" + } + }, + "euler": { + "ir-no-optimize": { + "bytecode_size": 323909, + "deployment_gas": 61591870, + "method_gas": 3452105184, + "version": "2ef99fc" + }, + "legacy-no-optimize": { + "bytecode_size": 323909, + "deployment_gas": 61591870, + "method_gas": 3452105184, + "version": "c23e8bd" + }, + "legacy-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251, + "version": "c23e8bd" + }, + "legacy-optimize-evm-only": { + "bytecode_size": 202106, + "deployment_gas": 38790600, + "method_gas": 2907368790, + "version": "v1.2.3" + }, + "ir-optimize-evm-only": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251 + }, + "ir-optimize-evm+yul": { + "bytecode_size": 182190, + "deployment_gas": 35236828, + "method_gas": 2777867251 + } + }, + "ens": { + "legacy-optimize-evm+yul": { + "bytecode_size": 156937, + "deployment_gas": 30073789, + "method_gas": 105365362, + "version": "v0.0.8" + } + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + "bytecode_size": 510428, + "deployment_gas": 94501114, + "version": "af7ec04" + } + } +} diff --git a/test/scripts/test_externalTests_benchmark_diff.py b/test/scripts/test_externalTests_benchmark_diff.py new file mode 100644 index 000000000..b4ccf0745 --- /dev/null +++ b/test/scripts/test_externalTests_benchmark_diff.py @@ -0,0 +1,309 @@ +#!/usr/bin/env python3 + +import json +import unittest + +from unittest_helpers import FIXTURE_DIR, load_fixture + +# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports +# pragma pylint: disable=import-error +from externalTests.benchmark_diff import BenchmarkDiffer, DifferenceStyle +# pragma pylint: enable=import-error + +SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-develop.json' +SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-branch.json' + + +class TestBenchmarkDiff(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + def test_benchmark_diff(self): + report_before = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH)) + report_after = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH)) + expected_diff = { + "bleeps": { + "ir-optimize-evm+yul": { + # Numerical difference -> negative/positive/zero. + # Zeros are not skipped to differentiate them from missing values. + "bytecode_size": 132868 - 132165, + "deployment_gas": 0, + "method_gas": 39289198 - 39289935, + }, + "legacy-optimize-evm+yul": { + # No differences within preset -> zeros still present. + "bytecode_size": 0, + "deployment_gas": 0, + "method_gas": 0, + }, + }, + "colony": { + # Preset missing on one side -> replace dict with string + "ir-optimize-evm+yul": "!A", + "legacy-no-optimize": "!B", + "legacy-optimize-evm+yul": { + "bytecode_size": 0, + # Attribute missing on both sides -> skip + #"deployment_gas": + #"method_gas": + }, + }, + "elementfi": { + "legacy-no-optimize": { + # Attributes null on one side -> replace value with string + "bytecode_size": "!A", + "deployment_gas": "!B", + # Attribute null on both sides -> skip + #"method_gas": + }, + "legacy-optimize-evm+yul": { + # Attributes missing on one side -> replace value with string + "bytecode_size": "!A", + "deployment_gas": "!B", + # Attribute missing on both sides -> skip + #"method_gas": + }, + "ir-no-optimize": { + # Attributes missing on one side, null on the other -> skip + #"bytecode_size": + #"deployment_gas": + "method_gas": 0, + }, + # Empty preset missing on one side -> replace dict with string + "legacy-optimize-evm-only": "!A", + "ir-optimize-evm-only": "!B", + }, + "euler": { + # Matching versions -> show attributes, skip version + "ir-no-optimize": { + "bytecode_size": 328540 - 323909, + "deployment_gas": 0, + "method_gas": 3537419168 - 3452105184, + }, + # Different versions, different values -> replace whole preset with string + "legacy-no-optimize": "!V", + # Different versions, same values -> replace whole preset with string + "legacy-optimize-evm+yul": "!V", + # Different versions (not a commit hash), different values -> replace whole preset with string + "legacy-optimize-evm-only": "!V", + # Version missing on one side -> replace whole preset with string + "ir-optimize-evm-only": "!V", + # Version missing on both sides -> assume same version + "ir-optimize-evm+yul": { + "bytecode_size": 205211 - 182190, + "deployment_gas": 39459629 - 35236828, + "method_gas": 0, + }, + }, + "zeppelin": { + "legacy-optimize-evm+yul": { + # Whole project identical -> attributes still present, with zeros + "bytecode_size": 0, + "deployment_gas": 0, + # Field missing on both sides -> skip + #"method_gas": + } + }, + # Empty project missing on one side -> replace its dict with a string + "gnosis": "!B", + "ens": "!A", + } + differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, None) + self.assertEqual(differ.run(report_before, report_after), expected_diff) + + +class TestBenchmarkDiffer(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + @staticmethod + def _nest(value, levels): + nested_value = value + for level in levels: + nested_value = {level: nested_value} + + return nested_value + + def _assert_single_value_diff_matches(self, differ, cases, nest_result=True, nestings=None): + if nestings is None: + nestings = [[], ['p'], ['p', 's'], ['p', 's', 'a']] + + for levels in nestings: + for (before, after, expected_diff) in cases: + self.assertEqual( + differ.run(self._nest(before, levels), self._nest(after, levels)), + self._nest(expected_diff, levels) if nest_result else expected_diff, + f'Wrong diff for {self._nest(before, levels)} vs {self._nest(after, levels)}' + ) + + def test_empty(self): + for style in DifferenceStyle: + differ = BenchmarkDiffer(style, None) + self._assert_single_value_diff_matches(differ, [({}, {}, {})], nest_result=False) + + def test_null(self): + for style in DifferenceStyle: + differ = BenchmarkDiffer(style, None) + self._assert_single_value_diff_matches(differ, [(None, None, {})], nest_result=False) + + def test_number_diff_absolute_json(self): + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4), + [ + (2, 2, 0), + (2, 5, 3), + (5, 2, -3), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, 2.5 - 2), + (2.5, 2, 2 - 2.5), + + (0, 0, 0), + (0, 2, 2), + (0, -2, -2), + + (-3, -1, 2), + (-1, -3, -2), + (2, 0, -2), + (-2, 0, 2), + + (1.00006, 1, 1 - 1.00006), + (1, 1.00006, 1.00006 - 1), + (1.00004, 1, 1 - 1.00004), + (1, 1.00004, 1.00004 - 1), + ], + ) + + def test_number_diff_json(self): + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.RELATIVE, 4), + [ + (2, 2, 0), + (2, 5, (5 - 2) / 2), + (5, 2, (2 - 5) / 5), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, (2.5 - 2) / 2), + (2.5, 2, (2 - 2.5) / 2.5), + + (0, 0, 0), + (0, 2, '+INF'), + (0, -2, '-INF'), + + (-3, -1, 0.6667), + (-1, -3, -2), + (2, 0, -1), + (-2, 0, 1), + + (1.00006, 1, -0.0001), + (1, 1.00006, 0.0001), + (1.000004, 1, '-0'), + (1, 1.000004, '+0'), + ], + ) + + def test_number_diff_humanized_json(self): + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4), + [ + (2, 2, '0%'), + (2, 5, '+150%'), + (5, 2, '-60%'), + (2.0, 2.0, '0%'), + (2, 2.0, '0%'), + (2.0, 2, '0%'), + (2, 2.5, '+25%'), + (2.5, 2, '-20%'), + + (0, 0, '0%'), + (0, 2, '+INF%'), + (0, -2, '-INF%'), + + (-3, -1, '+66.67%'), + (-1, -3, '-200%'), + (2, 0, '-100%'), + (-2, 0, '+100%'), + + (1.00006, 1, '-0.01%'), + (1, 1.00006, '+0.01%'), + (1.000004, 1, '-0%'), + (1, 1.000004, '+0%'), + ], + ) + + def test_type_mismatch(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, 4), + [ + (1, {}, '!T'), + ({}, 1, '!T'), + (1.5, {}, '!T'), + ({}, 1.5, '!T'), + ('1', {}, '!T'), + ({}, '1', '!T'), + (1, '1', '!T'), + ('1', 1, '!T'), + (1.5, '1', '!T'), + ('1', 1.5, '!T'), + ('1', '1', '!T'), + ], + ) + + def test_version_mismatch(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, 4), + [ + ({'a': 123, 'version': 1}, {'a': 123, 'version': 2}, '!V'), + ({'a': 123, 'version': 2}, {'a': 123, 'version': 1}, '!V'), + ({'a': 123, 'version': 'a'}, {'a': 123, 'version': 'b'}, '!V'), + ({'a': 123, 'version': 'a'}, {'a': 123, 'version': 1}, '!V'), + + ({'a': 'a', 'version': 1}, {'a': 'a', 'version': 2}, '!V'), + ({'a': {}, 'version': 1}, {'a': {}, 'version': 2}, '!V'), + ({'s': {'a': 1}, 'version': 1}, {'s': {'a': 1}, 'version': 2}, '!V'), + + ({'a': 123, 'version': 1}, {'a': 456, 'version': 2}, '!V'), + ({'a': 'a', 'version': 1}, {'a': 'b', 'version': 2}, '!V'), + ({'s': {'a': 1}, 'version': 1}, {'s': {'a': 2}, 'version': 2}, '!V'), + ], + ) + + def test_missing(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, None), + [ + (1, None, '!A'), + (None, 1, '!B'), + ('1', None, '!A'), + (None, '1', '!B'), + ({}, None, '!A'), + (None, {}, '!B'), + + ({'x': 1}, {}, {'x': '!A'}), + ({}, {'x': 1}, {'x': '!B'}), + ({'x': 1}, {'x': None}, {'x': '!A'}), + ({'x': None}, {'x': 1}, {'x': '!B'}), + ({'x': 1}, {'y': 1}, {'x': '!A', 'y': '!B'}), + + ({'x': {}}, {}, {'x': '!A'}), + ({}, {'x': {}}, {'x': '!B'}), + ({'p': {'x': {}}}, {}, {'p': '!A'}), + ({}, {'p': {'x': {}}}, {'p': '!B'}), + ], + ) + + def test_missing_vs_null(self): + for style in DifferenceStyle: + self._assert_single_value_diff_matches( + BenchmarkDiffer(style, None), + [ + ({'a': None}, {}, {}), + ({}, {'a': None}, {}), + ], + nest_result=False, + ) From 8c9856c52c464c598b55e75c7873a27778d9957d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 18 Mar 2022 14:51:11 +0100 Subject: [PATCH 25/51] benchmark_diff: Add table mode with support for json, console and markdown output --- scripts/externalTests/benchmark_diff.py | 259 ++++++++++++- ...benchmark-diff-develop-branch-humanized.md | 75 ++++ .../test_externalTests_benchmark_diff.py | 361 ++++++++++++++---- 3 files changed, 605 insertions(+), 90 deletions(-) create mode 100644 test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md diff --git a/scripts/externalTests/benchmark_diff.py b/scripts/externalTests/benchmark_diff.py index 0669e62eb..234f85ac0 100755 --- a/scripts/externalTests/benchmark_diff.py +++ b/scripts/externalTests/benchmark_diff.py @@ -4,19 +4,44 @@ from argparse import ArgumentParser from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, Optional, Union +from textwrap import dedent +from typing import Any, Mapping, Optional, Set, Sequence, Union import json import sys +class DiffMode(Enum): + IN_PLACE = 'inplace' + TABLE = 'table' + + class DifferenceStyle(Enum): ABSOLUTE = 'absolute' RELATIVE = 'relative' HUMANIZED = 'humanized' +class OutputFormat(Enum): + JSON = 'json' + CONSOLE = 'console' + MARKDOWN = 'markdown' + + DEFAULT_RELATIVE_PRECISION = 4 -DEFAULT_DIFFERENCE_STYLE = DifferenceStyle.ABSOLUTE + +DEFAULT_DIFFERENCE_STYLE = { + DiffMode.IN_PLACE: DifferenceStyle.ABSOLUTE, + DiffMode.TABLE: DifferenceStyle.HUMANIZED, +} +assert all(t in DiffMode for t in DEFAULT_DIFFERENCE_STYLE) +assert all(d in DifferenceStyle for d in DEFAULT_DIFFERENCE_STYLE.values()) + +DEFAULT_OUTPUT_FORMAT = { + DiffMode.IN_PLACE: OutputFormat.JSON, + DiffMode.TABLE: OutputFormat.CONSOLE, +} +assert all(m in DiffMode for m in DEFAULT_OUTPUT_FORMAT) +assert all(o in OutputFormat for o in DEFAULT_OUTPUT_FORMAT.values()) class ValidationError(Exception): @@ -30,14 +55,17 @@ class CommandLineError(ValidationError): class BenchmarkDiffer: difference_style: DifferenceStyle relative_precision: Optional[int] + output_format: OutputFormat def __init__( self, difference_style: DifferenceStyle, relative_precision: Optional[int], + output_format: OutputFormat, ): self.difference_style = difference_style self.relative_precision = relative_precision + self.output_format = output_format def run(self, before: Any, after: Any) -> Optional[Union[dict, str, int, float]]: if not isinstance(before, dict) or not isinstance(after, dict): @@ -106,8 +134,13 @@ class BenchmarkDiffer: return diff def _humanize_diff(self, diff: Union[str, int, float]) -> str: + def wrap(value: str, symbol: str): + return f"{symbol}{value}{symbol}" + + markdown = (self.output_format == OutputFormat.MARKDOWN) + if isinstance(diff, str) and diff.startswith('!'): - return diff + return wrap(diff, '`' if markdown else '') value: Union[str, int, float] if isinstance(diff, (int, float)): @@ -118,32 +151,209 @@ class BenchmarkDiffer: value = round(value, self.relative_precision - 2) if isinstance(value, float) and value.is_integer(): value = int(value) + suffix = '' prefix = '' if diff < 0: prefix = '' + if markdown: + suffix += ' ✅' elif diff > 0: prefix = '+' + if markdown: + suffix += ' ❌' + important = (diff != 0) else: value = diff + important = False prefix = '' + suffix = '' - return f"{prefix}{value}%" + return wrap( + wrap( + f"{prefix}{value}%{suffix}", + '`' if markdown else '' + ), + '**' if important and markdown else '' + ) + + +@dataclass(frozen=True) +class DiffTable: + columns: Mapping[str, Sequence[Union[int, float, str]]] + + +class DiffTableSet: + table_headers: Sequence[str] + row_headers: Sequence[str] + column_headers: Sequence[str] + + # Cells is a nested dict rather than a 3D array so that conversion to JSON is straightforward + cells: Mapping[str, Mapping[str, Mapping[str, Union[int, float, str]]]] # preset -> project -> attribute + + def __init__(self, diff: dict): + self.table_headers = sorted(self._find_all_preset_names(diff)) + self.column_headers = sorted(self._find_all_attribute_names(diff)) + self.row_headers = sorted(project for project in diff) + + # All dimensions must have unique values + assert len(self.table_headers) == len(set(self.table_headers)) + assert len(self.column_headers) == len(set(self.column_headers)) + assert len(self.row_headers) == len(set(self.row_headers)) + + self.cells = { + preset: { + project: { + attribute: self._cell_content(diff, project, preset, attribute) + for attribute in self.column_headers + } + for project in self.row_headers + } + for preset in self.table_headers + } + + def calculate_row_column_width(self) -> int: + return max(len(h) for h in self.row_headers) + + def calculate_column_widths(self, table_header: str) -> Sequence[int]: + assert table_header in self.table_headers + + return [ + max( + len(column_header), + max( + len(str(self.cells[table_header][row_header][column_header])) + for row_header in self.row_headers + ) + ) + for column_header in self.column_headers + ] + + @classmethod + def _find_all_preset_names(cls, diff: dict) -> Set[str]: + return { + preset + for project, project_diff in diff.items() + if isinstance(project_diff, dict) + for preset in project_diff + } + + @classmethod + def _find_all_attribute_names(cls, diff: dict) -> Set[str]: + return { + attribute + for project, project_diff in diff.items() + if isinstance(project_diff, dict) + for preset, preset_diff in project_diff.items() + if isinstance(preset_diff, dict) + for attribute in preset_diff + } + + @classmethod + def _cell_content(cls, diff: dict, project: str, preset: str, attribute: str) -> str: + assert project in diff + + if isinstance(diff[project], str): + return diff[project] + if preset not in diff[project]: + return '' + if isinstance(diff[project][preset], str): + return diff[project][preset] + if attribute not in diff[project][preset]: + return '' + + return diff[project][preset][attribute] + + +class DiffTableFormatter: + LEGEND = dedent(""" + `!V` = version mismatch + `!B` = no value in the "before" version + `!A` = no value in the "after" version + `!T` = one or both values were not numeric and could not be compared + `-0` = very small negative value rounded to zero + `+0` = very small positive value rounded to zero + """) + + @classmethod + def run(cls, diff_table_set: DiffTableSet, output_format: OutputFormat): + if output_format == OutputFormat.JSON: + return json.dumps(diff_table_set.cells, indent=4, sort_keys=True) + else: + assert output_format in {OutputFormat.CONSOLE, OutputFormat.MARKDOWN} + + output = '' + for table_header in diff_table_set.table_headers: + column_widths = ([ + diff_table_set.calculate_row_column_width(), + *diff_table_set.calculate_column_widths(table_header) + ]) + + if output_format == OutputFormat.MARKDOWN: + output += f'\n### `{table_header}`\n' + else: + output += f'\n{table_header.upper()}\n' + + if output_format == OutputFormat.CONSOLE: + output += cls._format_separator_row(column_widths, output_format) + '\n' + output += cls._format_data_row(['project', *diff_table_set.column_headers], column_widths) + '\n' + output += cls._format_separator_row(column_widths, output_format) + '\n' + + for row_header in diff_table_set.row_headers: + row = [ + diff_table_set.cells[table_header][row_header][column_header] + for column_header in diff_table_set.column_headers + ] + output += cls._format_data_row([row_header, *row], column_widths) + '\n' + + if output_format == OutputFormat.CONSOLE: + output += cls._format_separator_row(column_widths, output_format) + '\n' + + if output_format == OutputFormat.MARKDOWN: + output += f'\n{cls.LEGEND}\n' + return output + + @classmethod + def _format_separator_row(cls, widths: Sequence[int], output_format: OutputFormat): + assert output_format in {OutputFormat.CONSOLE, OutputFormat.MARKDOWN} + + if output_format == OutputFormat.MARKDOWN: + return '|:' + ':|-'.join('-' * width for width in widths) + ':|' + else: + return '|-' + '-|-'.join('-' * width for width in widths) + '-|' + + @classmethod + def _format_data_row(cls, cells: Sequence[Union[int, float, str]], widths: Sequence[int]): + assert len(cells) == len(widths) + + return '| ' + ' | '.join(str(cell).rjust(width) for cell, width in zip(cells, widths)) + ' |' @dataclass(frozen=True) class CommandLineOptions: + diff_mode: DiffMode report_before: Path report_after: Path difference_style: DifferenceStyle relative_precision: int + output_format: OutputFormat def process_commandline() -> CommandLineOptions: script_description = ( - "Compares summarized benchmark reports and outputs JSON with the same structure but listing only differences." + "Compares summarized benchmark reports and outputs JSON with the same structure but listing only differences. " + "Can also print the output as markdown table and format the values to make differences stand out more." ) parser = ArgumentParser(description=script_description) + parser.add_argument( + dest='diff_mode', + choices=[m.value for m in DiffMode], + help=( + "Diff mode: " + f"'{DiffMode.IN_PLACE.value}' preserves input JSON structure and replace values with differences; " + f"'{DiffMode.TABLE.value}' creates a table assuming 3-level project/preset/attribute structure." + ) + ) parser.add_argument(dest='report_before', help="Path to a JSON file containing original benchmark results.") parser.add_argument(dest='report_after', help="Path to a JSON file containing new benchmark results.") parser.add_argument( @@ -156,7 +366,8 @@ def process_commandline() -> CommandLineOptions: f"'{DifferenceStyle.RELATIVE.value}' also divides by the original; " f"'{DifferenceStyle.HUMANIZED.value}' is like relative but value is a percentage and " "positive/negative changes are emphasized. " - f"(default: '{DEFAULT_DIFFERENCE_STYLE}')." + f"(default: '{DEFAULT_DIFFERENCE_STYLE[DiffMode.IN_PLACE]}' in '{DiffMode.IN_PLACE.value}' mode, " + f"'{DEFAULT_DIFFERENCE_STYLE[DiffMode.TABLE]}' in '{DiffMode.TABLE.value}' mode)" ) ) # NOTE: Negative values are valid for precision. round() handles them in a sensible way. @@ -173,21 +384,47 @@ def process_commandline() -> CommandLineOptions: f"(default: {DEFAULT_RELATIVE_PRECISION})" ) ) + parser.add_argument( + '--output-format', + dest='output_format', + choices=[o.value for o in OutputFormat], + help=( + "The format to use for the diff: " + f"'{OutputFormat.JSON.value}' is raw JSON; " + f"'{OutputFormat.CONSOLE.value}' is a table with human-readable values that will look good in the console output. " + f"'{OutputFormat.MARKDOWN.value}' is similar '{OutputFormat.CONSOLE.value}' but adjusted to " + "render as proper markdown and with extra elements (legend, emoji to make non-zero values stand out more, etc)." + f"(default: '{DEFAULT_OUTPUT_FORMAT[DiffMode.IN_PLACE]}' in '{DiffMode.IN_PLACE.value}' mode, " + f"'{DEFAULT_OUTPUT_FORMAT[DiffMode.TABLE]}' in '{DiffMode.TABLE.value}' mode)" + ) + ) options = parser.parse_args() if options.difference_style is not None: difference_style = DifferenceStyle(options.difference_style) else: - difference_style = DEFAULT_DIFFERENCE_STYLE + difference_style = DEFAULT_DIFFERENCE_STYLE[DiffMode(options.diff_mode)] + + if options.output_format is not None: + output_format = OutputFormat(options.output_format) + else: + output_format = DEFAULT_OUTPUT_FORMAT[DiffMode(options.diff_mode)] processed_options = CommandLineOptions( + diff_mode=DiffMode(options.diff_mode), report_before=Path(options.report_before), report_after=Path(options.report_after), difference_style=difference_style, relative_precision=options.relative_precision, + output_format=output_format, ) + if processed_options.diff_mode == DiffMode.IN_PLACE and processed_options.output_format != OutputFormat.JSON: + raise CommandLineError( + f"Only the '{OutputFormat.JSON.value}' output format is supported in the '{DiffMode.IN_PLACE.value}' mode." + ) + return processed_options @@ -195,13 +432,17 @@ def main(): try: options = process_commandline() - differ = BenchmarkDiffer(options.difference_style, options.relative_precision) + differ = BenchmarkDiffer(options.difference_style, options.relative_precision, options.output_format) diff = differ.run( json.loads(options.report_before.read_text('utf-8')), json.loads(options.report_after.read_text('utf-8')), ) - print(json.dumps(diff, indent=4, sort_keys=True)) + if options.diff_mode == DiffMode.IN_PLACE: + print(json.dumps(diff, indent=4, sort_keys=True)) + else: + assert options.diff_mode == DiffMode.TABLE + print(DiffTableFormatter.run(DiffTableSet(diff), options.output_format)) return 0 except CommandLineError as exception: diff --git a/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md b/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md new file mode 100644 index 000000000..ce351a5b7 --- /dev/null +++ b/test/scripts/fixtures/summarized-benchmark-diff-develop-branch-humanized.md @@ -0,0 +1,75 @@ + +### `ir-no-optimize` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|---------------:|---------------:|---------------:| +| bleeps | | | | +| colony | | | | +| elementfi | | | `0%` | +| ens | `!A` | `!A` | `!A` | +| euler | **`+1.43% ❌`** | `0%` | **`+2.47% ❌`** | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `ir-optimize-evm+yul` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|----------------:|----------------:|-----------:| +| bleeps | **`+0.53% ❌`** | `0%` | `-0%` | +| colony | `!A` | `!A` | `!A` | +| elementfi | | | | +| ens | `!A` | `!A` | `!A` | +| euler | **`+12.64% ❌`** | **`+11.98% ❌`** | `0%` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `ir-optimize-evm-only` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | | | | +| elementfi | `!B` | `!B` | `!B` | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `legacy-no-optimize` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | `!B` | `!B` | `!B` | +| elementfi | `!A` | `!B` | | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + +### `legacy-optimize-evm+yul` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | `0%` | `0%` | `0%` | +| colony | `0%` | | | +| elementfi | `!A` | `!B` | | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | `0%` | `0%` | | + +### `legacy-optimize-evm-only` +| project | bytecode_size | deployment_gas | method_gas | +|:---------:|--------------:|---------------:|-----------:| +| bleeps | | | | +| colony | | | | +| elementfi | `!A` | `!A` | `!A` | +| ens | `!A` | `!A` | `!A` | +| euler | `!V` | `!V` | `!V` | +| gnosis | `!B` | `!B` | `!B` | +| zeppelin | | | | + + +`!V` = version mismatch +`!B` = no value in the "before" version +`!A` = no value in the "after" version +`!T` = one or both values were not numeric and could not be compared +`-0` = very small negative value rounded to zero +`+0` = very small positive value rounded to zero + diff --git a/test/scripts/test_externalTests_benchmark_diff.py b/test/scripts/test_externalTests_benchmark_diff.py index b4ccf0745..b40db8e63 100644 --- a/test/scripts/test_externalTests_benchmark_diff.py +++ b/test/scripts/test_externalTests_benchmark_diff.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from textwrap import dedent import json import unittest @@ -7,12 +8,15 @@ from unittest_helpers import FIXTURE_DIR, load_fixture # NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports # pragma pylint: disable=import-error -from externalTests.benchmark_diff import BenchmarkDiffer, DifferenceStyle +from externalTests.benchmark_diff import BenchmarkDiffer, DifferenceStyle, DiffTableSet, DiffTableFormatter, OutputFormat # pragma pylint: enable=import-error SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-develop.json' SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH = FIXTURE_DIR / 'summarized-benchmarks-branch.json' +SUMMARIZED_DIFF_HUMANIZED_MD_PATH = FIXTURE_DIR / 'summarized-benchmark-diff-develop-branch-humanized.md' +SUMMARIZED_DIFF_HUMANIZED_MD = load_fixture(SUMMARIZED_DIFF_HUMANIZED_MD_PATH) + class TestBenchmarkDiff(unittest.TestCase): def setUp(self): @@ -108,7 +112,7 @@ class TestBenchmarkDiff(unittest.TestCase): "gnosis": "!B", "ens": "!A", } - differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, None) + differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, None, OutputFormat.JSON) self.assertEqual(differ.run(report_before, report_after), expected_diff) @@ -138,105 +142,137 @@ class TestBenchmarkDiffer(unittest.TestCase): def test_empty(self): for style in DifferenceStyle: - differ = BenchmarkDiffer(style, None) + differ = BenchmarkDiffer(style, None, OutputFormat.JSON) self._assert_single_value_diff_matches(differ, [({}, {}, {})], nest_result=False) def test_null(self): for style in DifferenceStyle: - differ = BenchmarkDiffer(style, None) + differ = BenchmarkDiffer(style, None, OutputFormat.JSON) self._assert_single_value_diff_matches(differ, [(None, None, {})], nest_result=False) def test_number_diff_absolute_json(self): - self._assert_single_value_diff_matches( - BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4), - [ - (2, 2, 0), - (2, 5, 3), - (5, 2, -3), - (2.0, 2.0, 0), - (2, 2.0, 0), - (2.0, 2, 0), - (2, 2.5, 2.5 - 2), - (2.5, 2, 2 - 2.5), + for output_format in OutputFormat: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4, output_format), + [ + (2, 2, 0), + (2, 5, 3), + (5, 2, -3), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, 2.5 - 2), + (2.5, 2, 2 - 2.5), - (0, 0, 0), - (0, 2, 2), - (0, -2, -2), + (0, 0, 0), + (0, 2, 2), + (0, -2, -2), - (-3, -1, 2), - (-1, -3, -2), - (2, 0, -2), - (-2, 0, 2), + (-3, -1, 2), + (-1, -3, -2), + (2, 0, -2), + (-2, 0, 2), - (1.00006, 1, 1 - 1.00006), - (1, 1.00006, 1.00006 - 1), - (1.00004, 1, 1 - 1.00004), - (1, 1.00004, 1.00004 - 1), - ], - ) + (1.00006, 1, 1 - 1.00006), + (1, 1.00006, 1.00006 - 1), + (1.00004, 1, 1 - 1.00004), + (1, 1.00004, 1.00004 - 1), + ], + ) def test_number_diff_json(self): + for output_format in OutputFormat: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.RELATIVE, 4, output_format), + [ + (2, 2, 0), + (2, 5, (5 - 2) / 2), + (5, 2, (2 - 5) / 5), + (2.0, 2.0, 0), + (2, 2.0, 0), + (2.0, 2, 0), + (2, 2.5, (2.5 - 2) / 2), + (2.5, 2, (2 - 2.5) / 2.5), + + (0, 0, 0), + (0, 2, '+INF'), + (0, -2, '-INF'), + + (-3, -1, 0.6667), + (-1, -3, -2), + (2, 0, -1), + (-2, 0, 1), + + (1.00006, 1, -0.0001), + (1, 1.00006, 0.0001), + (1.000004, 1, '-0'), + (1, 1.000004, '+0'), + ], + ) + + def test_number_diff_humanized_json_and_console(self): + for output_format in [OutputFormat.JSON, OutputFormat.CONSOLE]: + self._assert_single_value_diff_matches( + BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, output_format), + [ + (2, 2, '0%'), + (2, 5, '+150%'), + (5, 2, '-60%'), + (2.0, 2.0, '0%'), + (2, 2.0, '0%'), + (2.0, 2, '0%'), + (2, 2.5, '+25%'), + (2.5, 2, '-20%'), + + (0, 0, '0%'), + (0, 2, '+INF%'), + (0, -2, '-INF%'), + + (-3, -1, '+66.67%'), + (-1, -3, '-200%'), + (2, 0, '-100%'), + (-2, 0, '+100%'), + + (1.00006, 1, '-0.01%'), + (1, 1.00006, '+0.01%'), + (1.000004, 1, '-0%'), + (1, 1.000004, '+0%'), + ], + ) + + def test_number_diff_humanized_markdown(self): self._assert_single_value_diff_matches( - BenchmarkDiffer(DifferenceStyle.RELATIVE, 4), + BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN), [ - (2, 2, 0), - (2, 5, (5 - 2) / 2), - (5, 2, (2 - 5) / 5), - (2.0, 2.0, 0), - (2, 2.0, 0), - (2.0, 2, 0), - (2, 2.5, (2.5 - 2) / 2), - (2.5, 2, (2 - 2.5) / 2.5), + (2, 2, '`0%`'), + (2, 5, '**`+150% ❌`**'), + (5, 2, '**`-60% ✅`**'), + (2.0, 2.0, '`0%`'), + (2, 2.0, '`0%`'), + (2.0, 2, '`0%`'), + (2, 2.5, '**`+25% ❌`**'), + (2.5, 2, '**`-20% ✅`**'), - (0, 0, 0), - (0, 2, '+INF'), - (0, -2, '-INF'), + (0, 0, '`0%`'), + (0, 2, '`+INF%`'), + (0, -2, '`-INF%`'), - (-3, -1, 0.6667), - (-1, -3, -2), - (2, 0, -1), - (-2, 0, 1), + (-3, -1, '**`+66.67% ❌`**'), + (-1, -3, '**`-200% ✅`**'), + (2, 0, '**`-100% ✅`**'), + (-2, 0, '**`+100% ❌`**'), - (1.00006, 1, -0.0001), - (1, 1.00006, 0.0001), - (1.000004, 1, '-0'), - (1, 1.000004, '+0'), - ], - ) - - def test_number_diff_humanized_json(self): - self._assert_single_value_diff_matches( - BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4), - [ - (2, 2, '0%'), - (2, 5, '+150%'), - (5, 2, '-60%'), - (2.0, 2.0, '0%'), - (2, 2.0, '0%'), - (2.0, 2, '0%'), - (2, 2.5, '+25%'), - (2.5, 2, '-20%'), - - (0, 0, '0%'), - (0, 2, '+INF%'), - (0, -2, '-INF%'), - - (-3, -1, '+66.67%'), - (-1, -3, '-200%'), - (2, 0, '-100%'), - (-2, 0, '+100%'), - - (1.00006, 1, '-0.01%'), - (1, 1.00006, '+0.01%'), - (1.000004, 1, '-0%'), - (1, 1.000004, '+0%'), + (1.00006, 1, '**`-0.01% ✅`**'), + (1, 1.00006, '**`+0.01% ❌`**'), + (1.000004, 1, '`-0%`'), + (1, 1.000004, '`+0%`'), ], ) def test_type_mismatch(self): for style in DifferenceStyle: self._assert_single_value_diff_matches( - BenchmarkDiffer(style, 4), + BenchmarkDiffer(style, 4, OutputFormat.JSON), [ (1, {}, '!T'), ({}, 1, '!T'), @@ -255,7 +291,7 @@ class TestBenchmarkDiffer(unittest.TestCase): def test_version_mismatch(self): for style in DifferenceStyle: self._assert_single_value_diff_matches( - BenchmarkDiffer(style, 4), + BenchmarkDiffer(style, 4, OutputFormat.JSON), [ ({'a': 123, 'version': 1}, {'a': 123, 'version': 2}, '!V'), ({'a': 123, 'version': 2}, {'a': 123, 'version': 1}, '!V'), @@ -275,7 +311,7 @@ class TestBenchmarkDiffer(unittest.TestCase): def test_missing(self): for style in DifferenceStyle: self._assert_single_value_diff_matches( - BenchmarkDiffer(style, None), + BenchmarkDiffer(style, None, OutputFormat.JSON), [ (1, None, '!A'), (None, 1, '!B'), @@ -300,10 +336,173 @@ class TestBenchmarkDiffer(unittest.TestCase): def test_missing_vs_null(self): for style in DifferenceStyle: self._assert_single_value_diff_matches( - BenchmarkDiffer(style, None), + BenchmarkDiffer(style, None, OutputFormat.JSON), [ ({'a': None}, {}, {}), ({}, {'a': None}, {}), ], nest_result=False, ) + + +class TestDiffTableFormatter(unittest.TestCase): + def setUp(self): + self.maxDiff = 10000 + + self.report_before = { + 'project A': { + 'preset X': {'A1': 99, 'A2': 50, 'version': 1}, + 'preset Y': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project B': { + 'preset X': { 'A2': 50}, + 'preset Y': {'A1': 0}, + }, + 'project C': { + 'preset X': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project D': { + 'preset X': {'A1': 999}, + }, + } + self.report_after = { + 'project A': { + 'preset X': {'A1': 100, 'A2': 50, 'version': 1}, + 'preset Y': {'A1': 500, 'A2': 500, 'version': 2}, + }, + 'project B': { + 'preset X': {'A1': 0}, + 'preset Y': { 'A2': 50}, + }, + 'project C': { + 'preset Y': {'A1': 0, 'A2': 50, 'version': 1}, + }, + 'project E': { + 'preset Y': { 'A2': 999}, + }, + } + + def test_diff_table_formatter(self): + report_before = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_DEVELOP_JSON_PATH)) + report_after = json.loads(load_fixture(SUMMARIZED_BENCHMARKS_BRANCH_JSON_PATH)) + differ = BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN) + diff = differ.run(report_before, report_after) + + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.MARKDOWN), SUMMARIZED_DIFF_HUMANIZED_MD) + + def test_diff_table_formatter_json_absolute(self): + differ = BenchmarkDiffer(DifferenceStyle.ABSOLUTE, 4, OutputFormat.JSON) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent("""\ + { + "preset X": { + "project A": { + "A1": 1, + "A2": 0 + }, + "project B": { + "A1": "!B", + "A2": "!A" + }, + "project C": { + "A1": "!A", + "A2": "!A" + }, + "project D": { + "A1": "!A", + "A2": "!A" + }, + "project E": { + "A1": "!B", + "A2": "!B" + } + }, + "preset Y": { + "project A": { + "A1": "!V", + "A2": "!V" + }, + "project B": { + "A1": "!A", + "A2": "!B" + }, + "project C": { + "A1": "!B", + "A2": "!B" + }, + "project D": { + "A1": "!A", + "A2": "!A" + }, + "project E": { + "A1": "!B", + "A2": "!B" + } + } + }""" + ) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.JSON), expected_formatted_table) + + def test_diff_table_formatter_console_relative(self): + differ = BenchmarkDiffer(DifferenceStyle.RELATIVE, 4, OutputFormat.CONSOLE) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent(""" + PRESET X + |-----------|--------|----| + | project | A1 | A2 | + |-----------|--------|----| + | project A | 0.0101 | 0 | + | project B | !B | !A | + | project C | !A | !A | + | project D | !A | !A | + | project E | !B | !B | + |-----------|--------|----| + + PRESET Y + |-----------|----|----| + | project | A1 | A2 | + |-----------|----|----| + | project A | !V | !V | + | project B | !A | !B | + | project C | !B | !B | + | project D | !A | !A | + | project E | !B | !B | + |-----------|----|----| + """) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.CONSOLE), expected_formatted_table) + + def test_diff_table_formatter_markdown_humanized(self): + differ = BenchmarkDiffer(DifferenceStyle.HUMANIZED, 4, OutputFormat.MARKDOWN) + diff = differ.run(self.report_before, self.report_after) + + expected_formatted_table = dedent(""" + ### `preset X` + | project | A1 | A2 | + |:---------:|---------------:|-----:| + | project A | **`+1.01% ❌`** | `0%` | + | project B | `!B` | `!A` | + | project C | `!A` | `!A` | + | project D | `!A` | `!A` | + | project E | `!B` | `!B` | + + ### `preset Y` + | project | A1 | A2 | + |:---------:|-----:|-----:| + | project A | `!V` | `!V` | + | project B | `!A` | `!B` | + | project C | `!B` | `!B` | + | project D | `!A` | `!A` | + | project E | `!B` | `!B` | + + + `!V` = version mismatch + `!B` = no value in the "before" version + `!A` = no value in the "after" version + `!T` = one or both values were not numeric and could not be compared + `-0` = very small negative value rounded to zero + `+0` = very small positive value rounded to zero + + """) + self.assertEqual(DiffTableFormatter.run(DiffTableSet(diff), OutputFormat.MARKDOWN), expected_formatted_table) From 3a17274691210191a6a407e7314a3070af87233c Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 31 Mar 2022 20:36:45 -0500 Subject: [PATCH 26/51] Improve error message for static array with size too large --- libsolidity/analysis/DeclarationTypeChecker.cpp | 8 +++++++- .../array/length/bytes32_too_large.sol | 2 +- .../array/length/bytes32_too_large_multidim.sol | 2 +- .../array/length/literal_conversion.sol | 16 ++++++++++++++++ .../syntaxTests/array/length/too_large.sol | 5 ++++- .../array/length/uint_too_large_multidim.sol | 2 +- 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 test/libsolidity/syntaxTests/array/length/literal_conversion.sol diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 4fb210435..54a7354e2 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -297,7 +297,7 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) else if (optional value = ConstantEvaluator::evaluate(m_errorReporter, *length)) lengthValue = value->value; - if (!lengthValue || lengthValue > TypeProvider::uint256()->max()) + if (!lengthValue) m_errorReporter.typeError( 5462_error, length->location(), @@ -309,6 +309,12 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified."); else if (*lengthValue < 0) m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified."); + else if (lengthValue > TypeProvider::uint256()->max()) + m_errorReporter.typeError( + 1847_error, + length->location(), + "Array length too large, maximum is 2**256 - 1." + ); _typeName.annotation().type = TypeProvider::array( DataLocation::Storage, diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol index 37f194899..8ab641984 100644 --- a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol @@ -2,4 +2,4 @@ contract C { bytes32[8**90] ids; } // ---- -// TypeError 5462: (25-30): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (25-30): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol index 1a4e7f64c..8a7eeb4d5 100644 --- a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol @@ -2,4 +2,4 @@ contract C { bytes32[8**90][500] ids; } // ---- -// TypeError 5462: (25-30): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (25-30): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/literal_conversion.sol b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol new file mode 100644 index 000000000..cf769f337 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol @@ -0,0 +1,16 @@ +contract C { + uint[uint(1)] valid_size_invalid_expr1; + uint[uint(2**256-1)] valid_size_invalid_expr2; + uint[uint(2**256)] invalid_size_invalid_expr3; + + uint[int(1)] valid_size_invalid_expr4; + uint[int(2**256-1)] valid_size_invalid_expr5; + uint[int(2**256)] invalid_size_invalid_expr6; +} +// ---- +// TypeError 5462: (22-29): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (66-80): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (117-129): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (169-175): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (212-225): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (262-273): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/too_large.sol b/test/libsolidity/syntaxTests/array/length/too_large.sol index 687e80303..7d59fd86c 100644 --- a/test/libsolidity/syntaxTests/array/length/too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/too_large.sol @@ -1,5 +1,8 @@ contract C { uint[8**90] ids; + uint[2**256-1] okay; + uint[2**256] tooLarge; } // ---- -// TypeError 5462: (22-27): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (22-27): Array length too large, maximum is 2**256 - 1. +// TypeError 1847: (68-74): Array length too large, maximum is 2**256 - 1. diff --git a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol index 6ea3eda50..f3e249f83 100644 --- a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol +++ b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol @@ -2,4 +2,4 @@ contract C { uint[8**90][500] ids; } // ---- -// TypeError 5462: (22-27): Invalid array length, expected integer literal or constant expression. +// TypeError 1847: (22-27): Array length too large, maximum is 2**256 - 1. From fd0df0b05d72b58a52e7159ee46f208c75c6ef8a Mon Sep 17 00:00:00 2001 From: aathan Date: Tue, 5 Apr 2022 12:19:59 -0700 Subject: [PATCH 27/51] Update inheritance.rst --- docs/contracts/inheritance.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 54817837c..25838f3c5 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -443,7 +443,7 @@ cannot be assigned valid values from outside but only through the constructors o ``internal`` or ``public``. -.. index:: ! base;constructor +.. index:: ! base;constructor, inheritance list, contract;abstract, abstract contract Arguments for Base Constructors =============================== @@ -467,11 +467,20 @@ derived contracts need to specify all of them. This can be done in two ways: constructor() {} } - // or through a "modifier" of the derived constructor. + // or through a "modifier" of the derived constructor... contract Derived2 is Base { constructor(uint y) Base(y * y) {} } + // or declare abstract... + abstract contract Derived3 is Base { + } + + // and have the next concrete derived contract initialize it. + contract DerivedFromDerived is Derived3 { + constructor() Base(10 + 10) {} + } + One way is directly in the inheritance list (``is Base(7)``). The other is in the way a modifier is invoked as part of the derived constructor (``Base(y * y)``). The first way to @@ -484,7 +493,12 @@ inheritance list or in modifier-style in the derived constructor. Specifying arguments in both places is an error. If a derived contract does not specify the arguments to all of its base -contracts' constructors, it will be abstract. +contracts' constructors, it must be declared abstract. In that case, when +another contract derives from it, that other contract's inheritance list +or constructor must provide the necessary parameters +for all base classes that haven't had their parameters specified (otherwise, +that other contract must be declared abstract as well). For example, in the above +code snippet, see ``Derived3`` and ``DerivedFromDerived``. .. index:: ! inheritance;multiple, ! linearization, ! C3 linearization From 44093f2ed63782e02ee3611e701c92b2bdf9bd0c Mon Sep 17 00:00:00 2001 From: a3d4 Date: Wed, 6 Apr 2022 01:34:58 +0200 Subject: [PATCH 28/51] Fix MSVC Debug crash --- libyul/optimiser/StructuralSimplifier.cpp | 43 +++++++++++++---------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index ca8f94239..0b80c42cd 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -93,26 +93,31 @@ void StructuralSimplifier::operator()(Block& _block) void StructuralSimplifier::simplify(std::vector& _statements) { - util::GenericVisitor visitor{ - util::VisitorFallback{}, - [&](If& _ifStmt) -> OptionalStatements { - if (expressionAlwaysTrue(*_ifStmt.condition)) - return {std::move(_ifStmt.body.statements)}; - else if (expressionAlwaysFalse(*_ifStmt.condition)) - return {vector{}}; - return {}; - }, - [&](Switch& _switchStmt) -> OptionalStatements { - if (std::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) - return replaceConstArgSwitch(_switchStmt, constExprVal.value()); - return {}; - }, - [&](ForLoop& _forLoop) -> OptionalStatements { - if (expressionAlwaysFalse(*_forLoop.condition)) - return {std::move(_forLoop.pre.statements)}; - return {}; - } + // Explicit local variables ifLambda, switchLambda, forLoopLambda are created to avoid MSVC C++17 Debug test crash + // (Run-Time Check Failure #2 - Stack around the variable '....' was corrupted). + // As soon as the issue is fixed, this workaround can be removed. + auto ifLambda = [&](If& _ifStmt) -> OptionalStatements + { + if (expressionAlwaysTrue(*_ifStmt.condition)) + return {std::move(_ifStmt.body.statements)}; + else if (expressionAlwaysFalse(*_ifStmt.condition)) + return {vector{}}; + return {}; }; + auto switchLambda = [&](Switch& _switchStmt) -> OptionalStatements + { + if (std::optional const constExprVal = hasLiteralValue(*_switchStmt.expression)) + return replaceConstArgSwitch(_switchStmt, constExprVal.value()); + return {}; + }; + auto forLoopLambda = [&](ForLoop& _forLoop) -> OptionalStatements + { + if (expressionAlwaysFalse(*_forLoop.condition)) + return {std::move(_forLoop.pre.statements)}; + return {}; + }; + + util::GenericVisitor visitor{util::VisitorFallback{}, ifLambda, switchLambda, forLoopLambda}; util::iterateReplacing( _statements, From 4d8a34370e9f433de9f773718fafe88021b5cf3f Mon Sep 17 00:00:00 2001 From: wechman Date: Tue, 5 Apr 2022 09:06:04 +0200 Subject: [PATCH 29/51] Create split_on_empty_lines_into_numbered_files function in scripts/common.sh --- scripts/common.sh | 9 +++++++++ test/cmdlineTests.sh | 39 ++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/scripts/common.sh b/scripts/common.sh index 7cb96ed02..1ea0e1fc1 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -246,3 +246,12 @@ function first_word echo "$words" | cut -d " " -f 1 } + +# Function reads from stdin. Therefore it has to be used with pipes. +function split_on_empty_lines_into_numbered_files +{ + path_prefix="${1}" + path_suffix="${2}" + + awk -v RS= "{print > (\"${path_prefix}_\"NR \"${path_suffix}\")}" +} diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 8b6f64630..91b233b89 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -310,6 +310,9 @@ function test_solc_assembly_output function test_via_ir_equivalence() { + SOLTMPDIR=$(mktemp -d) + pushd "$SOLTMPDIR" + (( $# <= 2 )) || fail "This function accepts at most two arguments." if [[ $2 != --optimize ]] && [[ $2 != "" ]] @@ -320,56 +323,54 @@ function test_via_ir_equivalence() local solidity_file="$1" local optimize_flag="$2" - filename=$(basename "$1" .sol) + output_file_prefix=$(basename "$1" .sol) local optimizer_flags=() [[ $optimize_flag == "" ]] || optimizer_flags+=("$optimize_flag") - [[ $optimize_flag == "" ]] || filename+="_optimize" + [[ $optimize_flag == "" ]] || output_file_prefix+="_optimize" msg_on_error --no-stderr "$SOLC" --ir-optimized --debug-info location "${optimizer_flags[@]}" "$solidity_file" | - sed '/^Optimized IR:$/d' | - awk -v RS= "{print > (\"${filename}_\"NR \".yul\")}" + sed '/^Optimized IR:$/d' | + split_on_empty_lines_into_numbered_files $output_file_prefix ".yul" - for yul_file in $(find . -name "$filename*.yul" | sort -V); do + for yul_file in $(find . -name "${output_file_prefix}*.yul" | sort -V); do msg_on_error --no-stderr "$SOLC" --strict-assembly --asm "${optimizer_flags[@]}" "$yul_file" | - sed '/^======= /d' | - sed '/^Text representation:$/d' >> "${yul_file/.yul/.asm}" + sed '/^Text representation:$/d' > "${yul_file/.yul/.asm}" done local asm_output_two_stage asm_output_via_ir - for asm_file in $(find . -name "$filename*.asm" | sort -V); do + for asm_file in $(find . -name "${output_file_prefix}*.asm" | sort -V); do asm_output_two_stage+=$(sed '/^asm_output_two_stage:$/d' "$asm_file" | sed '/^=======/d') done asm_output_via_ir=$( msg_on_error --no-stderr "$SOLC" --via-ir --asm --debug-info location "${optimizer_flags[@]}" "$solidity_file" | - sed '/^======= /d' | - sed '/^EVM assembly:$/d' | - sed '/^asm_output_via_ir/d' | - sed '/^=======/d' + sed '/^EVM assembly:$/d' | + sed '/^=======/d' ) diff_values "$asm_output_two_stage" "$asm_output_via_ir" --ignore-space-change --ignore-blank-lines local bin_output_two_stage bin_output_via_ir - for yul_file in $(find . -name "$filename*.yul" | sort -V); do + for yul_file in $(find . -name "${output_file_prefix}*.yul" | sort -V); do bin_output_two_stage+=$( msg_on_error --no-stderr "$SOLC" --strict-assembly --bin "${optimizer_flags[@]}" "$yul_file" | - sed '/^======= /d' | - sed '/^Binary representation:$/d' | - sed '/^=======/d' + sed '/^Binary representation:$/d' | + sed '/^=======/d' ) done bin_output_via_ir=$( msg_on_error --no-stderr "$SOLC" --via-ir --bin "${optimizer_flags[@]}" "$solidity_file" | - sed '/^======= /d' | sed '/^Binary:$/d' | sed '/^=======/d' ) diff_values "$bin_output_two_stage" "$bin_output_via_ir" --ignore-space-change --ignore-blank-lines + + popd + rm -r "$SOLTMPDIR" } ## RUN @@ -598,10 +599,7 @@ printTask "Testing assemble, yul, strict-assembly and optimize..." ) printTask "Testing the eqivalence of --via-ir and a two-stage compilation..." -SOLTMPDIR=$(mktemp -d) ( - cd "$SOLTMPDIR" - externalContracts=( externalTests/solc-js/DAO/TokenCreation.sol libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol @@ -631,7 +629,6 @@ SOLTMPDIR=$(mktemp -d) test_via_ir_equivalence "${REPO_ROOT}/test/${contractFile}" --optimize done ) -rm -r "$SOLTMPDIR" printTask "Testing standard input..." SOLTMPDIR=$(mktemp -d) From 0bb885dab2c64dc1dec40b0f1f898b141a7e65d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Nov 2021 17:04:37 +0100 Subject: [PATCH 30/51] Bring soltestAssert() up to date with solAssert() - Allow omitting description. - Provide a default description. - Use a custom exception type derived from util::Exception rather than std::exception. --- test/libsolidity/util/SoltestErrors.h | 29 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/util/SoltestErrors.h b/test/libsolidity/util/SoltestErrors.h index e223f714b..849a8b34e 100644 --- a/test/libsolidity/util/SoltestErrors.h +++ b/test/libsolidity/util/SoltestErrors.h @@ -15,20 +15,35 @@ #pragma once #include +#include #include #include +#include +#include +#include + namespace solidity::frontend::test { -#define soltestAssert(CONDITION, DESCRIPTION) \ - do \ - { \ - if (!(CONDITION)) \ - BOOST_THROW_EXCEPTION(std::runtime_error(DESCRIPTION)); \ - } \ - while (false) +struct InternalSoltestError: virtual util::Exception {}; +#if !BOOST_PP_VARIADICS_MSVC +#define soltestAssert(...) BOOST_PP_OVERLOAD(soltestAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define soltestAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(soltestAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define soltestAssert_1(CONDITION) \ + soltestAssert_2((CONDITION), "") + +#define soltestAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + (CONDITION), \ + ::solidity::frontend::test::InternalSoltestError, \ + (DESCRIPTION), \ + "Soltest assertion failed" \ + ) class TestParserError: virtual public util::Exception { From 3c5930dd8e0ed6622b34799ef98f59ef75813460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 6 Apr 2022 22:22:23 +0200 Subject: [PATCH 31/51] Put arguments in parantheses in assert macro definitions --- liblangutil/Exceptions.h | 16 ++++++++-------- libsmtutil/Exceptions.h | 6 +++--- libsolutil/Assertions.h | 4 ++-- libsolutil/Exceptions.h | 2 +- libyul/Exceptions.h | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index 10fca8029..fb6f23cf1 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -58,13 +58,13 @@ struct InvalidAstError: virtual util::Exception {}; #endif #define solAssert_1(CONDITION) \ - solAssert_2(CONDITION, "") + solAssert_2((CONDITION), "") #define solAssert_2(CONDITION, DESCRIPTION) \ assertThrowWithDefaultDescription( \ - CONDITION, \ + (CONDITION), \ ::solidity::langutil::InternalCompilerError, \ - DESCRIPTION, \ + (DESCRIPTION), \ "Solidity assertion failed" \ ) @@ -77,13 +77,13 @@ struct InvalidAstError: virtual util::Exception {}; #endif #define solUnimplementedAssert_1(CONDITION) \ - solUnimplementedAssert_2(CONDITION, "") + solUnimplementedAssert_2((CONDITION), "") #define solUnimplementedAssert_2(CONDITION, DESCRIPTION) \ assertThrowWithDefaultDescription( \ - CONDITION, \ + (CONDITION), \ ::solidity::langutil::UnimplementedFeatureError, \ - DESCRIPTION, \ + (DESCRIPTION), \ "Unimplemented feature" \ ) @@ -105,9 +105,9 @@ struct InvalidAstError: virtual util::Exception {}; #define astAssert_2(CONDITION, DESCRIPTION) \ assertThrowWithDefaultDescription( \ - CONDITION, \ + (CONDITION), \ ::solidity::langutil::InvalidAstError, \ - DESCRIPTION, \ + (DESCRIPTION), \ "AST assertion failed" \ ) diff --git a/libsmtutil/Exceptions.h b/libsmtutil/Exceptions.h index fd144ca72..2a32c4fcd 100644 --- a/libsmtutil/Exceptions.h +++ b/libsmtutil/Exceptions.h @@ -38,13 +38,13 @@ struct SMTLogicError: virtual util::Exception {}; #endif #define smtAssert_1(CONDITION) \ - smtAssert_2(CONDITION, "") + smtAssert_2((CONDITION), "") #define smtAssert_2(CONDITION, DESCRIPTION) \ assertThrowWithDefaultDescription( \ - CONDITION, \ + (CONDITION), \ ::solidity::smtutil::SMTLogicError, \ - DESCRIPTION, \ + (DESCRIPTION), \ "SMT assertion failed" \ ) diff --git a/libsolutil/Assertions.h b/libsolutil/Assertions.h index 81823e040..4924522d6 100644 --- a/libsolutil/Assertions.h +++ b/libsolutil/Assertions.h @@ -63,7 +63,7 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri if (!(_condition)) \ solThrow( \ _exceptionType, \ - ::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \ + ::solidity::util::assertions::stringOrDefault((_description), (_defaultDescription)) \ ); \ } \ while (false) @@ -72,6 +72,6 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri /// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); /// The second parameter must be an exception class (rather than an instance). #define assertThrow(_condition, _exceptionType, _description) \ - assertThrowWithDefaultDescription(_condition, _exceptionType, _description, "Assertion failed") + assertThrowWithDefaultDescription((_condition), _exceptionType, (_description), "Assertion failed") } diff --git a/libsolutil/Exceptions.h b/libsolutil/Exceptions.h index 66b2442ef..8ac7a5fae 100644 --- a/libsolutil/Exceptions.h +++ b/libsolutil/Exceptions.h @@ -48,7 +48,7 @@ struct Exception: virtual std::exception, virtual boost::exception #define solThrow(_exceptionType, _description) \ ::boost::throw_exception( \ _exceptionType() << \ - ::solidity::util::errinfo_comment(_description) << \ + ::solidity::util::errinfo_comment((_description)) << \ ::boost::throw_function(ETH_FUNC) << \ ::boost::throw_file(__FILE__) << \ ::boost::throw_line(__LINE__) \ diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 1ad7785e9..45c681a5d 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -63,13 +63,13 @@ struct StackTooDeepError: virtual YulException #endif #define yulAssert_1(CONDITION) \ - yulAssert_2(CONDITION, "") + yulAssert_2((CONDITION), "") #define yulAssert_2(CONDITION, DESCRIPTION) \ assertThrowWithDefaultDescription( \ - CONDITION, \ + (CONDITION), \ ::solidity::yul::YulAssertion, \ - DESCRIPTION, \ + (DESCRIPTION), \ "Yul assertion failed" \ ) From ed8403f45657964bf14f9bf8e8fa063e55bd045b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Nov 2021 17:47:21 +0100 Subject: [PATCH 32/51] isoltest: Handle parsing errors differently from unexpected exceptions --- test/tools/isoltest.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index ae2ce6e24..746cee7e6 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -499,9 +499,20 @@ int main(int argc, char const *argv[]) return global_stats ? 0 : 1; } - catch (std::exception const& _exception) + catch (boost::program_options::error const& exception) { - cerr << _exception.what() << endl; + cerr << exception.what() << endl; + return 1; + } + catch (std::runtime_error const& exception) + { + cerr << exception.what() << endl; + return 1; + } + catch (...) + { + cerr << "Unhandled exception caught." << endl; + cerr << boost::current_exception_diagnostic_information() << endl; return 1; } } From 7bace8d25d55a71a243ca117699f9c665a47ca2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Nov 2021 17:49:27 +0100 Subject: [PATCH 33/51] soltest: Don't assume that parse() can never return false --- test/boostTest.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 1cc512c32..0d3b0eb34 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -204,15 +204,18 @@ int registerTests( return numTestsAdded; } -void initializeOptions() +bool initializeOptions() { auto const& suite = boost::unit_test::framework::master_test_suite(); auto options = std::make_unique(); - solAssert(options->parse(suite.argc, suite.argv), "Failed to parse options!"); + bool shouldContinue = options->parse(suite.argc, suite.argv); + if (!shouldContinue) + return false; options->validate(); solidity::test::CommonOptions::setSingleton(std::move(options)); + return true; } } @@ -228,7 +231,9 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - initializeOptions(); + bool shouldContinue = initializeOptions(); + if (!shouldContinue) + exit(0); if (!solidity::test::loadVMs(solidity::test::CommonOptions::get())) exit(1); From cf6704ae0636fce85fda9d4dd52e1e2f86e97fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Nov 2021 18:19:37 +0100 Subject: [PATCH 34/51] isoltest: Do not return an error code from `--help` --- test/Common.h | 3 +++ test/tools/IsolTestOptions.cpp | 6 +++--- test/tools/isoltest.cpp | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/Common.h b/test/Common.h index e7e211d9e..28862348f 100644 --- a/test/Common.h +++ b/test/Common.h @@ -75,6 +75,9 @@ struct CommonOptions langutil::EVMVersion evmVersion() const; virtual void addOptions(); + // @returns true if the program should continue, false if it should exit immediately without + // reporting an error. + // Throws ConfigException or std::runtime_error if parsing fails. virtual bool parse(int argc, char const* const* argv); // Throws a ConfigException on error virtual void validate() const; diff --git a/test/tools/IsolTestOptions.cpp b/test/tools/IsolTestOptions.cpp index cdd1b85ab..ca7761d74 100644 --- a/test/tools/IsolTestOptions.cpp +++ b/test/tools/IsolTestOptions.cpp @@ -75,9 +75,9 @@ void IsolTestOptions::addOptions() bool IsolTestOptions::parse(int _argc, char const* const* _argv) { - bool const res = CommonOptions::parse(_argc, _argv); + bool const shouldContinue = CommonOptions::parse(_argc, _argv); - if (showHelp || !res) + if (showHelp || !shouldContinue) { std::cout << options << std::endl; return false; @@ -85,7 +85,7 @@ bool IsolTestOptions::parse(int _argc, char const* const* _argv) enforceGasTest = enforceGasTest || (evmVersion() == langutil::EVMVersion{} && !useABIEncoderV1); - return res; + return shouldContinue; } void IsolTestOptions::validate() const diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 746cee7e6..a2ef564cb 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -433,8 +433,9 @@ int main(int argc, char const *argv[]) { auto options = std::make_unique(); - if (!options->parse(argc, argv)) - return -1; + bool shouldContinue = options->parse(argc, argv); + if (!shouldContinue) + return 0; options->validate(); CommonOptions::setSingleton(std::move(options)); From 7c835598819877f3b1258f0fa29379fa2bc4b530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Nov 2021 17:51:18 +0100 Subject: [PATCH 35/51] soltest: Handle CLI validation errors gracefully --- test/Common.cpp | 45 +++++++++------- test/boostTest.cpp | 125 +++++++++++++++++++++++++-------------------- 2 files changed, 95 insertions(+), 75 deletions(-) diff --git a/test/Common.cpp b/test/Common.cpp index a702cddd6..8cdf0fed1 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -159,26 +159,33 @@ bool CommonOptions::parse(int argc, char const* const* argv) po::variables_map arguments; addOptions(); - po::command_line_parser cmdLineParser(argc, argv); - cmdLineParser.options(options); - auto parsedOptions = cmdLineParser.run(); - po::store(parsedOptions, arguments); - po::notify(arguments); + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options); + auto parsedOptions = cmdLineParser.run(); + po::store(parsedOptions, arguments); + po::notify(arguments); - for (auto const& parsedOption: parsedOptions.options) - if (parsedOption.position_key >= 0) - { - if ( - parsedOption.original_tokens.empty() || - (parsedOption.original_tokens.size() == 1 && parsedOption.original_tokens.front().empty()) - ) - continue; // ignore empty options - std::stringstream errorMessage; - errorMessage << "Unrecognized option: "; - for (auto const& token: parsedOption.original_tokens) - errorMessage << token; - BOOST_THROW_EXCEPTION(std::runtime_error(errorMessage.str())); - } + for (auto const& parsedOption: parsedOptions.options) + if (parsedOption.position_key >= 0) + { + if ( + parsedOption.original_tokens.empty() || + (parsedOption.original_tokens.size() == 1 && parsedOption.original_tokens.front().empty()) + ) + continue; // ignore empty options + std::stringstream errorMessage; + errorMessage << "Unrecognized option: "; + for (auto const& token: parsedOption.original_tokens) + errorMessage << token; + BOOST_THROW_EXCEPTION(std::runtime_error(errorMessage.str())); + } + } + catch (po::error const& exception) + { + solThrow(ConfigException, exception.what()); + } if (vmPaths.empty()) { diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 0d3b0eb34..014673eba 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -222,75 +222,88 @@ bool initializeOptions() // TODO: Prototype -- why isn't this declared in the boost headers? // TODO: replace this with a (global) fixture. -test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ); +test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]); -test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) +test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { using namespace solidity::test; master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - bool shouldContinue = initializeOptions(); - if (!shouldContinue) - exit(0); - - if (!solidity::test::loadVMs(solidity::test::CommonOptions::get())) - exit(1); - - if (solidity::test::CommonOptions::get().disableSemanticTests) - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; - - if (!solidity::test::CommonOptions::get().enforceGasTest) - cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl; - - Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches); - if (CommonOptions::get().batches > 1) - cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl; - - // Batch the boost tests - BoostBatcher boostBatcher(batcher); - traverse_test_tree(master, boostBatcher, true); - - // Include the interactive tests in the automatic tests as well - for (auto const& ts: g_interactiveTestsuites) + try { - auto const& options = solidity::test::CommonOptions::get(); + bool shouldContinue = initializeOptions(); + if (!shouldContinue) + exit(0); - if (ts.smt && options.disableSMT) - continue; + if (!solidity::test::loadVMs(solidity::test::CommonOptions::get())) + exit(1); - if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests) - continue; + if (solidity::test::CommonOptions::get().disableSemanticTests) + cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; - //TODO - //solAssert( - registerTests( - master, - options.testPath / ts.path, - ts.subpath, - options.enforceViaYul, - options.enforceCompileToEwasm, - ts.labels, - ts.testCaseCreator, - batcher - ); - // > 0, std::string("no ") + ts.title + " tests found"); + if (!solidity::test::CommonOptions::get().enforceGasTest) + cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl; + + Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches); + if (CommonOptions::get().batches > 1) + cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl; + + // Batch the boost tests + BoostBatcher boostBatcher(batcher); + traverse_test_tree(master, boostBatcher, true); + + // Include the interactive tests in the automatic tests as well + for (auto const& ts: g_interactiveTestsuites) + { + auto const& options = solidity::test::CommonOptions::get(); + + if (ts.smt && options.disableSMT) + continue; + + if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests) + continue; + + //TODO + //solAssert( + registerTests( + master, + options.testPath / ts.path, + ts.subpath, + options.enforceViaYul, + options.enforceCompileToEwasm, + ts.labels, + ts.testCaseCreator, + batcher + ); + // > 0, std::string("no ") + ts.title + " tests found"); + } + + if (solidity::test::CommonOptions::get().disableSemanticTests) + { + for (auto suite: { + "ABIDecoderTest", + "ABIEncoderTest", + "SolidityAuctionRegistrar", + "SolidityWallet", + "GasMeterTests", + "GasCostTests", + "SolidityEndToEndTest", + "SolidityOptimizer" + }) + removeTestSuite(suite); + } } - - if (solidity::test::CommonOptions::get().disableSemanticTests) + catch (solidity::test::ConfigException const& exception) { - for (auto suite: { - "ABIDecoderTest", - "ABIEncoderTest", - "SolidityAuctionRegistrar", - "SolidityWallet", - "GasMeterTests", - "GasCostTests", - "SolidityEndToEndTest", - "SolidityOptimizer" - }) - removeTestSuite(suite); + cerr << exception.what() << endl; + exit(1); + } + catch (std::runtime_error const& exception) + { + cerr << exception.what() << endl; + exit(1); } return nullptr; From b3048ccf077e3818c2ce9f2e05ad411c3525423d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 30 Nov 2021 14:26:59 +0100 Subject: [PATCH 36/51] Use EXIT_FAILURE and EXIT_SUCCESS constants in exit() and when returning from main() --- test/boostTest.cpp | 8 ++++---- test/tools/isoltest.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 014673eba..b7a4c80f0 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -235,10 +235,10 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) { bool shouldContinue = initializeOptions(); if (!shouldContinue) - exit(0); + exit(EXIT_SUCCESS); if (!solidity::test::loadVMs(solidity::test::CommonOptions::get())) - exit(1); + exit(EXIT_FAILURE); if (solidity::test::CommonOptions::get().disableSemanticTests) cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; @@ -298,12 +298,12 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) catch (solidity::test::ConfigException const& exception) { cerr << exception.what() << endl; - exit(1); + exit(EXIT_FAILURE); } catch (std::runtime_error const& exception) { cerr << exception.what() << endl; - exit(1); + exit(EXIT_FAILURE); } return nullptr; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index a2ef564cb..ce45819b8 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -435,7 +435,7 @@ int main(int argc, char const *argv[]) bool shouldContinue = options->parse(argc, argv); if (!shouldContinue) - return 0; + return EXIT_SUCCESS; options->validate(); CommonOptions::setSingleton(std::move(options)); @@ -444,7 +444,7 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(CommonOptions::get()); if (!solidity::test::loadVMs(options)) - return 1; + return EXIT_FAILURE; if (options.disableSemanticTests) cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; @@ -480,7 +480,7 @@ int main(int argc, char const *argv[]) if (stats) global_stats += *stats; else - return 1; + return EXIT_FAILURE; } cout << endl << "Summary: "; @@ -498,22 +498,22 @@ int main(int argc, char const *argv[]) if (options.disableSemanticTests) cout << "\nNOTE: Skipped semantics tests.\n" << endl; - return global_stats ? 0 : 1; + return global_stats ? EXIT_SUCCESS : EXIT_FAILURE; } catch (boost::program_options::error const& exception) { cerr << exception.what() << endl; - return 1; + return EXIT_FAILURE; } catch (std::runtime_error const& exception) { cerr << exception.what() << endl; - return 1; + return EXIT_FAILURE; } catch (...) { cerr << "Unhandled exception caught." << endl; cerr << boost::current_exception_diagnostic_information() << endl; - return 1; + return EXIT_FAILURE; } } From 39f3175a378a43319595eadea7882b09f7e81282 Mon Sep 17 00:00:00 2001 From: andreb0x <98777324+andreb0x@users.noreply.github.com> Date: Mon, 7 Mar 2022 08:31:33 -0500 Subject: [PATCH 37/51] updates to gas section small modifications for clarity updates to delegatecall Update introduction-to-smart-contracts.rst modifying "write" to "transactions" updates to gas section small modifications for clarity Update introduction-to-smart-contracts.rst modifying "write" to "transactions" updates to gas section small modifications for clarity updates to delegatecall --- docs/introduction-to-smart-contracts.rst | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 7adbfaedb..d673a0788 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -396,14 +396,15 @@ returns that code when executed. Gas === -Upon creation, each transaction is charged with a certain amount of **gas**, -whose purpose is to limit the amount of work that is needed to execute -the transaction and to pay for this execution at the same time. While the EVM executes the +Upon creation, each transaction is charged with a certain amount of **gas**. +Imposing a cost on operations that write to the blockchain serves to secure the network: +it deincentivizes bad actors from spamming, and pays validators (i.e. miners and stakers) +for the work that is required to validate new blocks. While the EVM executes the transaction, the gas is gradually depleted according to specific rules. -The **gas price** is a value set by the creator of the transaction, who +The **gas price** is a value set by the creator of the transaction (see `msg.sender), who has to pay ``gas_price * gas`` up front from the sending account. -If some gas is left after the execution, it is refunded to the creator in the same way. +If some gas is left after execution, it is refunded to the transaction originator. If the gas is used up at any point (i.e. it would be negative), an out-of-gas exception is triggered, which reverts all modifications @@ -415,8 +416,7 @@ Storage, Memory and the Stack ============================= The Ethereum Virtual Machine has three areas where it can store data- -storage, memory and the stack, which are explained in the following -paragraphs. +storage, memory and the stack. Each account has a data area called **storage**, which is persistent between function calls and transactions. @@ -505,12 +505,25 @@ Delegatecall / Callcode and Libraries There exists a special variant of a message call, named **delegatecall** which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling -contract and ``msg.sender`` and ``msg.value`` do not change their values. +<<<<<<< HEAD +contract. In practice this means ``msg.sender`` and ``msg.value`` retain +the values that were passed to the delegator, while executing code that +lives in the delegated contract. +======= +contract. In practice this means ``msg.sender`` and ``msg.value`` retain +the values that were passed to the delegator, while executing code that +lives in the delegated contract. +>>>>>>> updates to delegatecall This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still refer to the calling contract, only the code is taken from the called address. +For example, say Alice calls a method in contract A to modify +state variable `A.var` according to method ``A.foo()``. +If contract A then delegates call to contract B, the logic of `B.foo()` +will be executed, but modifications will be made to state variable `A.var`. + This makes it possible to implement the "library" feature in Solidity: Reusable library code that can be applied to a contract's storage, e.g. in order to implement a complex data structure. From 60416120aff67768d3860dbdde0e1257b5f7324f Mon Sep 17 00:00:00 2001 From: andreb0x <98777324+andreb0x@users.noreply.github.com> Date: Mon, 7 Mar 2022 10:57:56 -0500 Subject: [PATCH 38/51] Update introduction-to-smart-contracts.rst modifying "write" to "transactions" --- docs/introduction-to-smart-contracts.rst | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index d673a0788..7baae4c31 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -396,13 +396,13 @@ returns that code when executed. Gas === -Upon creation, each transaction is charged with a certain amount of **gas**. -Imposing a cost on operations that write to the blockchain serves to secure the network: +Upon creation, each transaction is charged with a certain amount of **gas**. +Imposing a cost on transactions serves to secure the network: it deincentivizes bad actors from spamming, and pays validators (i.e. miners and stakers) for the work that is required to validate new blocks. While the EVM executes the transaction, the gas is gradually depleted according to specific rules. -The **gas price** is a value set by the creator of the transaction (see `msg.sender), who +The **gas price** is a value set by the originator of the transaction, i.e. `tx.origin`, who has to pay ``gas_price * gas`` up front from the sending account. If some gas is left after execution, it is refunded to the transaction originator. @@ -505,25 +505,14 @@ Delegatecall / Callcode and Libraries There exists a special variant of a message call, named **delegatecall** which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling -<<<<<<< HEAD contract. In practice this means ``msg.sender`` and ``msg.value`` retain the values that were passed to the delegator, while executing code that lives in the delegated contract. -======= -contract. In practice this means ``msg.sender`` and ``msg.value`` retain -the values that were passed to the delegator, while executing code that -lives in the delegated contract. ->>>>>>> updates to delegatecall This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still refer to the calling contract, only the code is taken from the called address. -For example, say Alice calls a method in contract A to modify -state variable `A.var` according to method ``A.foo()``. -If contract A then delegates call to contract B, the logic of `B.foo()` -will be executed, but modifications will be made to state variable `A.var`. - This makes it possible to implement the "library" feature in Solidity: Reusable library code that can be applied to a contract's storage, e.g. in order to implement a complex data structure. From 9e2b26e046adf56f48f73afcd982984d9f697cd1 Mon Sep 17 00:00:00 2001 From: aathan Date: Tue, 29 Mar 2022 13:48:49 -0700 Subject: [PATCH 39/51] Clarify the reason for the memory-unsafe assembly example being unsafe --- docs/assembly.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 0876229b1..70c4a458d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -313,7 +313,8 @@ Furthermore, if the assembly block assigns to Solidity variables in memory, you the Solidity variables only access these memory ranges. Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block -reverts or terminates. As an example, the following assembly snippet is not memory safe: +reverts or terminates. As an example, the following assembly snippet is not memory safe, because the value of +``returndatasize()`` may exceed the 64 byte scratch space: .. code-block:: solidity @@ -322,7 +323,8 @@ reverts or terminates. As an example, the following assembly snippet is not memo revert(0, returndatasize()) } -But the following is: +On the other hand, the following code *is* memory safe, because memory beyond the location pointed to by the +free memory pointer can safely be used as temporary scratch space: .. code-block:: solidity From 22d8c0aecd8ecf0973bae33fe949235f92cf2bda Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 Apr 2022 20:29:07 +0200 Subject: [PATCH 40/51] Some updates to gas and delegatecall. --- docs/introduction-to-smart-contracts.rst | 35 ++++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 7baae4c31..9b2e5fceb 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -396,26 +396,33 @@ returns that code when executed. Gas === -Upon creation, each transaction is charged with a certain amount of **gas**. -Imposing a cost on transactions serves to secure the network: -it deincentivizes bad actors from spamming, and pays validators (i.e. miners and stakers) -for the work that is required to validate new blocks. While the EVM executes the +Upon creation, each transaction is charged with a certain amount of **gas** +that has to be paid for by the originator of the transaction (``tx.origin``). +While the EVM executes the transaction, the gas is gradually depleted according to specific rules. - -The **gas price** is a value set by the originator of the transaction, i.e. `tx.origin`, who -has to pay ``gas_price * gas`` up front from the sending account. -If some gas is left after execution, it is refunded to the transaction originator. - If the gas is used up at any point (i.e. it would be negative), -an out-of-gas exception is triggered, which reverts all modifications +an out-of-gas exception is triggered, which ends execution and reverts all modifications made to the state in the current call frame. +This mechanism incentivizes economical use of EVM execution time +and also compensates EVM executors (i.e. miners / stakers) for their work. +Since each block has a maximum amount of gas, it also limits the amount +of work needed to validate a block. + +The **gas price** is a value set by the originator of the transaction, who +has to pay ``gas_price * gas`` up front to the EVM executor. +If some gas is left after execution, it is refunded to the transaction originator. +In case of an exception that reverts changes, already used up gas is not refunded. + +Since EVM executors can choose to include a transaction or not, +transaction senders cannot abuse the system by setting a low gas price. + .. index:: ! storage, ! memory, ! stack Storage, Memory and the Stack ============================= -The Ethereum Virtual Machine has three areas where it can store data- +The Ethereum Virtual Machine has three areas where it can store data: storage, memory and the stack. Each account has a data area called **storage**, which is persistent between function calls @@ -504,10 +511,8 @@ Delegatecall / Callcode and Libraries There exists a special variant of a message call, named **delegatecall** which is identical to a message call apart from the fact that -the code at the target address is executed in the context of the calling -contract. In practice this means ``msg.sender`` and ``msg.value`` retain -the values that were passed to the delegator, while executing code that -lives in the delegated contract. +the code at the target address is executed in the context (i.e. at the address) of the calling +contract and ``msg.sender`` and ``msg.value`` do not change their values. This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still From c5bf750bf31a12bd4e04cff916235e2f49dc1949 Mon Sep 17 00:00:00 2001 From: Bytecurl <102375389+bytecurl@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:59:05 +0200 Subject: [PATCH 41/51] Add .DS_Store and Thumbs.db to .gitignore Add OS specific local files that should not be included in a git repository - .DS_Store (macOS) and Thumbs.db (Windows) --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 9322b0c81..0ec3f7ace 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,7 @@ CMakeLists.txt.user # place to put local temporary files tmp + +# OS specific local files +.DS_Store +Thumbs.db From e1a59397c65d3b3eff19299fd703a7887a4b4e27 Mon Sep 17 00:00:00 2001 From: Joshua Quinones Date: Mon, 28 Mar 2022 00:27:11 -0400 Subject: [PATCH 42/51] Renamed AssemblyStack to YulStack All files, references, variables, comments, etc. were renamed to YulStack. --- libsolidity/codegen/ir/IRGenerator.cpp | 6 +-- libsolidity/interface/CompilerStack.cpp | 14 +++---- libsolidity/interface/StandardCompiler.cpp | 6 +-- libyul/CMakeLists.txt | 4 +- libyul/{AssemblyStack.cpp => YulStack.cpp} | 38 +++++++++---------- libyul/{AssemblyStack.h => YulStack.h} | 8 ++-- solc/CommandLineInterface.cpp | 24 ++++++------ solc/CommandLineInterface.h | 4 +- solc/CommandLineParser.cpp | 4 +- solc/CommandLineParser.h | 6 +-- test/libsolidity/InlineAssembly.cpp | 34 ++++++++--------- .../SolidityExecutionFramework.cpp | 6 +-- test/libsolidity/SolidityExecutionFramework.h | 2 +- test/libyul/Common.cpp | 6 +-- test/libyul/EVMCodeTransformTest.cpp | 6 +-- test/libyul/EwasmTranslationTest.cpp | 6 +-- test/libyul/EwasmTranslationTest.h | 6 +-- test/libyul/ObjectCompilerTest.cpp | 10 ++--- test/libyul/ObjectParser.cpp | 10 ++--- test/libyul/YulInterpreterTest.cpp | 6 +-- test/solc/CommandLineParser.cpp | 38 +++++++++---------- test/tools/ossfuzz/SolidityEvmoneInterface.h | 2 +- test/tools/ossfuzz/YulEvmoneInterface.cpp | 2 +- test/tools/ossfuzz/YulEvmoneInterface.h | 6 +-- .../ossfuzz/strictasm_assembly_ossfuzz.cpp | 8 ++-- test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp | 6 +-- test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp | 6 +-- test/tools/ossfuzz/yulProtoFuzzer.cpp | 8 ++-- test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp | 8 ++-- test/tools/yulrun.cpp | 6 +-- 30 files changed, 148 insertions(+), 148 deletions(-) rename libyul/{AssemblyStack.cpp => YulStack.cpp} (89%) rename libyul/{AssemblyStack.h => YulStack.h} (97%) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index cb62cc292..7c76f8344 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -95,9 +95,9 @@ pair IRGenerator::run( { string ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources)); - yul::AssemblyStack asmStack( + yul::YulStack asmStack( m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, + yul::YulStack::Language::StrictAssembly, m_optimiserSettings, m_context.debugInfoSelection() ); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index de5bea137..6560b0be5 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -62,7 +62,7 @@ #include #include #include -#include +#include #include #include @@ -1382,9 +1382,9 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) return; // Re-parse the Yul IR in EVM dialect - yul::AssemblyStack stack( + yul::YulStack stack( m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, + yul::YulStack::Language::StrictAssembly, m_optimiserSettings, m_debugInfoSelection ); @@ -1414,22 +1414,22 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract) return; // Re-parse the Yul IR in EVM dialect - yul::AssemblyStack stack( + yul::YulStack stack( m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, + yul::YulStack::Language::StrictAssembly, m_optimiserSettings, m_debugInfoSelection ); stack.parseAndAnalyze("", compiledContract.yulIROptimized); stack.optimize(); - stack.translate(yul::AssemblyStack::Language::Ewasm); + stack.translate(yul::YulStack::Language::Ewasm); stack.optimize(); //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; // Turn into Ewasm text representation. - auto result = stack.assemble(yul::AssemblyStack::Machine::Ewasm); + auto result = stack.assemble(yul::YulStack::Machine::Ewasm); compiledContract.ewasm = std::move(result.assembly); compiledContract.ewasmObject = std::move(*result.bytecode); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 31400ccad..9ca8de3ea 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -1407,9 +1407,9 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) return output; } - AssemblyStack stack( + YulStack stack( _inputsAndSettings.evmVersion, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, _inputsAndSettings.optimiserSettings, _inputsAndSettings.debugInfoSelection.has_value() ? _inputsAndSettings.debugInfoSelection.value() : diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 806f094fe..cc7033a0b 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -30,8 +30,8 @@ add_library(yul AsmParser.h AsmPrinter.cpp AsmPrinter.h - AssemblyStack.h - AssemblyStack.cpp + YulStack.h + YulStack.cpp CompilabilityChecker.cpp CompilabilityChecker.h ControlFlowSideEffects.h diff --git a/libyul/AssemblyStack.cpp b/libyul/YulStack.cpp similarity index 89% rename from libyul/AssemblyStack.cpp rename to libyul/YulStack.cpp index 3fefb9caf..3bca7288c 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/YulStack.cpp @@ -21,7 +21,7 @@ */ -#include +#include #include #include @@ -48,16 +48,16 @@ using namespace solidity::langutil; namespace { -Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _version) +Dialect const& languageToDialect(YulStack::Language _language, EVMVersion _version) { switch (_language) { - case AssemblyStack::Language::Assembly: - case AssemblyStack::Language::StrictAssembly: + case YulStack::Language::Assembly: + case YulStack::Language::StrictAssembly: return EVMDialect::strictAssemblyForEVMObjects(_version); - case AssemblyStack::Language::Yul: + case YulStack::Language::Yul: return EVMDialectTyped::instance(_version); - case AssemblyStack::Language::Ewasm: + case YulStack::Language::Ewasm: return WasmDialect::instance(); } yulAssert(false, ""); @@ -88,14 +88,14 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings( } -CharStream const& AssemblyStack::charStream(string const& _sourceName) const +CharStream const& YulStack::charStream(string const& _sourceName) const { yulAssert(m_charStream, ""); yulAssert(m_charStream->name() == _sourceName, ""); return *m_charStream; } -bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) +bool YulStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) { m_errors.clear(); m_analysisSuccessful = false; @@ -110,7 +110,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string return analyzeParsed(); } -void AssemblyStack::optimize() +void YulStack::optimize() { if (!m_optimiserSettings.runYulOptimiser) return; @@ -123,7 +123,7 @@ void AssemblyStack::optimize() yulAssert(analyzeParsed(), "Invalid source code after optimization."); } -void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) +void YulStack::translate(YulStack::Language _targetLanguage) { if (m_language == _targetLanguage) return; @@ -141,14 +141,14 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage) m_language = _targetLanguage; } -bool AssemblyStack::analyzeParsed() +bool YulStack::analyzeParsed() { yulAssert(m_parserResult, ""); m_analysisSuccessful = analyzeParsed(*m_parserResult); return m_analysisSuccessful; } -bool AssemblyStack::analyzeParsed(Object& _object) +bool YulStack::analyzeParsed(Object& _object) { yulAssert(_object.code, ""); _object.analysisInfo = make_shared(); @@ -168,7 +168,7 @@ bool AssemblyStack::analyzeParsed(Object& _object) return success; } -void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const +void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const { EVMDialect const* dialect = nullptr; switch (m_language) @@ -188,7 +188,7 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) cons EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _optimize); } -void AssemblyStack::optimize(Object& _object, bool _isCreation) +void YulStack::optimize(Object& _object, bool _isCreation) { yulAssert(_object.code, ""); yulAssert(_object.analysisInfo, ""); @@ -214,7 +214,7 @@ void AssemblyStack::optimize(Object& _object, bool _isCreation) ); } -MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const +MachineAssemblyObject YulStack::assemble(Machine _machine) const { yulAssert(m_analysisSuccessful, ""); yulAssert(m_parserResult, ""); @@ -243,7 +243,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const } std::pair -AssemblyStack::assembleWithDeployed(optional _deployName) const +YulStack::assembleWithDeployed(optional _deployName) const { auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); yulAssert(creationAssembly, ""); @@ -277,7 +277,7 @@ AssemblyStack::assembleWithDeployed(optional _deployName) const } std::pair, std::shared_ptr> -AssemblyStack::assembleEVMWithDeployed(optional _deployName) const +YulStack::assembleEVMWithDeployed(optional _deployName) const { yulAssert(m_analysisSuccessful, ""); yulAssert(m_parserResult, ""); @@ -317,7 +317,7 @@ AssemblyStack::assembleEVMWithDeployed(optional _deployName) const return {make_shared(assembly), {}}; } -string AssemblyStack::print( +string YulStack::print( CharStreamProvider const* _soliditySourceProvider ) const { @@ -326,7 +326,7 @@ string AssemblyStack::print( return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion), m_debugInfoSelection, _soliditySourceProvider) + "\n"; } -shared_ptr AssemblyStack::parserResult() const +shared_ptr YulStack::parserResult() const { yulAssert(m_analysisSuccessful, "Analysis was not successful."); yulAssert(m_parserResult, ""); diff --git a/libyul/AssemblyStack.h b/libyul/YulStack.h similarity index 97% rename from libyul/AssemblyStack.h rename to libyul/YulStack.h index 950265ca4..77d0025f1 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/YulStack.h @@ -63,14 +63,14 @@ struct MachineAssemblyObject * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Ewasm as output. */ -class AssemblyStack: public langutil::CharStreamProvider +class YulStack: public langutil::CharStreamProvider { public: enum class Language { Yul, Assembly, StrictAssembly, Ewasm }; enum class Machine { EVM, Ewasm }; - AssemblyStack(): - AssemblyStack( + YulStack(): + YulStack( langutil::EVMVersion{}, Language::Assembly, solidity::frontend::OptimiserSettings::none(), @@ -78,7 +78,7 @@ public: ) {} - AssemblyStack( + YulStack( langutil::EVMVersion _evmVersion, Language _language, solidity::frontend::OptimiserSettings _optimiserSettings, diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8cf3a8ebc..40a428c95 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -1014,18 +1014,18 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _ return out; } -void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine) +void CommandLineInterface::assemble(yul::YulStack::Language _language, yul::YulStack::Machine _targetMachine) { solAssert(m_options.input.mode == InputMode::Assembler, ""); bool successful = true; - map assemblyStacks; + map yulStacks; for (auto const& src: m_fileReader.sourceUnits()) { // --no-optimize-yul option is not accepted in assembly mode. solAssert(!m_options.optimizer.noOptimizeYul, ""); - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack( + auto& stack = yulStacks[src.first] = yul::YulStack( m_options.output.evmVersion, _language, m_options.optimiserSettings(), @@ -1040,7 +1040,7 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul: stack.optimize(); } - for (auto const& sourceAndStack: assemblyStacks) + for (auto const& sourceAndStack: yulStacks) { auto const& stack = sourceAndStack.second; SourceReferenceFormatter formatter(serr(false), stack, coloredOutput(m_options), m_options.formatting.withErrorIds); @@ -1063,11 +1063,11 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul: for (auto const& src: m_fileReader.sourceUnits()) { string machine = - _targetMachine == yul::AssemblyStack::Machine::EVM ? "EVM" : + _targetMachine == yul::YulStack::Machine::EVM ? "EVM" : "Ewasm"; sout() << endl << "======= " << src.first << " (" << machine << ") =======" << endl; - yul::AssemblyStack& stack = assemblyStacks[src.first]; + yul::YulStack& stack = yulStacks[src.first]; if (m_options.compiler.outputs.irOptimized) { @@ -1077,9 +1077,9 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul: sout() << stack.print() << endl; } - if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm) + if (_language != yul::YulStack::Language::Ewasm && _targetMachine == yul::YulStack::Machine::Ewasm) { - stack.translate(yul::AssemblyStack::Language::Ewasm); + stack.translate(yul::YulStack::Language::Ewasm); stack.optimize(); if (m_options.compiler.outputs.ewasmIR) @@ -1103,10 +1103,10 @@ void CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul: serr() << "No binary representation found." << endl; } - solAssert(_targetMachine == yul::AssemblyStack::Machine::Ewasm || _targetMachine == yul::AssemblyStack::Machine::EVM, ""); + solAssert(_targetMachine == yul::YulStack::Machine::Ewasm || _targetMachine == yul::YulStack::Machine::EVM, ""); if ( - (_targetMachine == yul::AssemblyStack::Machine::EVM && m_options.compiler.outputs.asm_) || - (_targetMachine == yul::AssemblyStack::Machine::Ewasm && m_options.compiler.outputs.ewasm) + (_targetMachine == yul::YulStack::Machine::EVM && m_options.compiler.outputs.asm_) || + (_targetMachine == yul::YulStack::Machine::Ewasm && m_options.compiler.outputs.ewasm) ) { sout() << endl << "Text representation:" << endl; diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 951731825..b7ab158f9 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -90,7 +90,7 @@ private: /// @returns the full object with library placeholder hints in hex. static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); - void assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine); + void assemble(yul::YulStack::Language _language, yul::YulStack::Machine _targetMachine); void outputCompilationResults(); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 4cc346737..1a3fbed0e 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -1132,8 +1132,8 @@ void CommandLineParser::processArgs() } // switch to assembly mode - using Input = yul::AssemblyStack::Language; - using Machine = yul::AssemblyStack::Machine; + using Input = yul::YulStack::Language; + using Machine = yul::YulStack::Machine; m_options.assembly.inputLanguage = m_args.count(g_strYul) ? Input::Yul : (m_args.count(g_strStrictAssembly) ? Input::StrictAssembly : Input::Assembly); if (m_args.count(g_strMachine)) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 9723d5e06..108a16cd2 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -190,8 +190,8 @@ struct CommandLineOptions struct { - yul::AssemblyStack::Machine targetMachine = yul::AssemblyStack::Machine::EVM; - yul::AssemblyStack::Language inputLanguage = yul::AssemblyStack::Language::StrictAssembly; + yul::YulStack::Machine targetMachine = yul::YulStack::Machine::EVM; + yul::YulStack::Language inputLanguage = yul::YulStack::Language::StrictAssembly; } assembly; struct diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index f0e5e455f..314a0d760 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -26,7 +26,7 @@ #include -#include +#include #include #include @@ -56,11 +56,11 @@ std::optional parseAndReturnFirstError( string const& _source, bool _assemble = false, bool _allowWarnings = true, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly, - AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM + YulStack::Language _language = YulStack::Language::Assembly, + YulStack::Machine _machine = YulStack::Machine::EVM ) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), _language, solidity::frontend::OptimiserSettings::none(), @@ -103,24 +103,24 @@ bool successParse( string const& _source, bool _assemble = false, bool _allowWarnings = true, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly, - AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM + YulStack::Language _language = YulStack::Language::Assembly, + YulStack::Machine _machine = YulStack::Machine::EVM ) { return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language, _machine); } -bool successAssemble(string const& _source, bool _allowWarnings = true, AssemblyStack::Language _language = AssemblyStack::Language::Assembly) +bool successAssemble(string const& _source, bool _allowWarnings = true, YulStack::Language _language = YulStack::Language::Assembly) { return - successParse(_source, true, _allowWarnings, _language, AssemblyStack::Machine::EVM); + successParse(_source, true, _allowWarnings, _language, YulStack::Machine::EVM); } Error expectError( std::string const& _source, bool _assemble, bool _allowWarnings = false, - AssemblyStack::Language _language = AssemblyStack::Language::Assembly + YulStack::Language _language = YulStack::Language::Assembly ) { @@ -131,9 +131,9 @@ Error expectError( void parsePrintCompare(string const& _source, bool _canWarn = false) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::Assembly, + YulStack::Language::Assembly, OptimiserSettings::none(), DebugInfoSelection::None() ); @@ -157,7 +157,7 @@ do \ } while(0) #define CHECK_ERROR(text, assemble, typ, substring, warnings) \ -CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, AssemblyStack::Language::Assembly) +CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, YulStack::Language::Assembly) #define CHECK_PARSE_ERROR(text, type, substring) \ CHECK_ERROR(text, false, type, substring, false) @@ -169,13 +169,13 @@ CHECK_ERROR(text, false, type, substring, false) CHECK_ERROR(text, true, type, substring, false) #define CHECK_STRICT_ERROR(text, type, substring) \ -CHECK_ERROR_LANG(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) +CHECK_ERROR_LANG(text, false, type, substring, false, YulStack::Language::StrictAssembly) #define CHECK_STRICT_WARNING(text, type, substring) \ -CHECK_ERROR(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) +CHECK_ERROR(text, false, type, substring, false, YulStack::Language::StrictAssembly) #define SUCCESS_STRICT(text) \ -do { successParse((text), false, false, AssemblyStack::Language::StrictAssembly); } while (false) +do { successParse((text), false, false, YulStack::Language::StrictAssembly); } while (false) BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) @@ -221,9 +221,9 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n"; - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::Assembly, + YulStack::Language::Assembly, OptimiserSettings::none(), DebugInfoSelection::None() ); diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 7ff226a69..70d0b2a6e 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -97,9 +97,9 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( else if (forceEnableOptimizer) optimiserSettings = OptimiserSettings::full(); - yul::AssemblyStack asmStack( + yul::YulStack asmStack( m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, + yul::YulStack::Language::StrictAssembly, optimiserSettings, DebugInfoSelection::All() ); @@ -109,7 +109,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( try { asmStack.optimize(); - obj = move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + obj = move(*asmStack.assemble(yul::YulStack::Machine::EVM).bytecode); obj.link(_libraryAddresses); break; } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 202addffa..dfa495152 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -30,7 +30,7 @@ #include #include -#include +#include namespace solidity::frontend::test { diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index fc9be2001..f40f422a8 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,9 +55,9 @@ Dialect const& defaultDialect(bool _yul) pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - _yul ? AssemblyStack::Language::Yul : AssemblyStack::Language::StrictAssembly, + _yul ? YulStack::Language::Yul : YulStack::Language::StrictAssembly, solidity::test::CommonOptions::get().optimize ? solidity::frontend::OptimiserSettings::standard() : solidity::frontend::OptimiserSettings::minimal(), diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index 679493f35..c1f6ae5df 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -51,9 +51,9 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::none(); settings.runYulOptimiser = false; settings.optimizeStackAllocation = m_stackOpt; - AssemblyStack stack( + YulStack stack( EVMVersion{}, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, settings, DebugInfoSelection::All() ); diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index 17b7f1868..34a714404 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -80,9 +80,9 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _ bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_stack = AssemblyStack( + m_stack = YulStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index eba82beee..d54b8732a 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -20,12 +20,12 @@ #include -#include +#include namespace solidity::yul { struct Object; -class AssemblyStack; +class YulStack; } namespace solidity::yul::test @@ -48,7 +48,7 @@ private: std::string interpret(); std::shared_ptr m_object; - AssemblyStack m_stack; + YulStack m_stack; }; } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f22870952..dcb7158c2 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include #include @@ -63,9 +63,9 @@ ObjectCompilerTest::ObjectCompilerTest(string const& _filename): TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + YulStack stack( EVMVersion(), - m_wasm ? AssemblyStack::Language::Ewasm : AssemblyStack::Language::StrictAssembly, + m_wasm ? YulStack::Language::Ewasm : YulStack::Language::StrictAssembly, OptimiserSettings::preset(m_optimisationPreset), DebugInfoSelection::All() ); @@ -80,7 +80,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li if (m_wasm) { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::Ewasm); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::Ewasm); solAssert(obj.bytecode, ""); m_obtainedResult = "Text:\n" + obj.assembly + "\n"; @@ -88,7 +88,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li } else { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::EVM); solAssert(obj.bytecode, ""); solAssert(obj.sourceMappings, ""); diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 1d6f0bed3..bee0025af 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -58,9 +58,9 @@ pair parse(string const& _source) { try { - AssemblyStack asmStack( + YulStack asmStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); @@ -180,9 +180,9 @@ BOOST_AUTO_TEST_CASE(to_string) } )"; expectation = boost::replace_all_copy(expectation, "\t", " "); - AssemblyStack asmStack( + YulStack asmStack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 3e7de4d17..3c890972e 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -65,9 +65,9 @@ TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _li bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( + YulStack stack( solidity::test::CommonOptions::get().evmVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::All() ); diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index b4be5a83a..22b3bd835 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -233,23 +233,23 @@ BOOST_AUTO_TEST_CASE(via_ir_options) BOOST_AUTO_TEST_CASE(assembly_mode_options) { - static vector, AssemblyStack::Machine, AssemblyStack::Language>> const allowedCombinations = { - {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::StrictAssembly}, - {{"--machine=ewasm", "--strict-assembly"}, AssemblyStack::Machine::Ewasm, AssemblyStack::Language::Ewasm}, - {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--yul-dialect=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--machine=evm", "--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, - {{"--machine=evm", "--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, - {{"--machine=evm", "--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, - {{"--assemble"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Assembly}, - {{"--yul"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::Yul}, - {{"--strict-assembly"}, AssemblyStack::Machine::EVM, AssemblyStack::Language::StrictAssembly}, + static vector, YulStack::Machine, YulStack::Language>> const allowedCombinations = { + {{"--machine=ewasm", "--yul-dialect=ewasm", "--assemble"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--yul"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=ewasm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=ewasm", "--yul-dialect=evm", "--assemble"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--yul"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--yul-dialect=evm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::StrictAssembly}, + {{"--machine=ewasm", "--strict-assembly"}, YulStack::Machine::Ewasm, YulStack::Language::Ewasm}, + {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--yul"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--machine=evm", "--assemble"}, YulStack::Machine::EVM, YulStack::Language::Assembly}, + {{"--machine=evm", "--yul"}, YulStack::Machine::EVM, YulStack::Language::Yul}, + {{"--machine=evm", "--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, + {{"--assemble"}, YulStack::Machine::EVM, YulStack::Language::Assembly}, + {{"--yul"}, YulStack::Machine::EVM, YulStack::Language::Yul}, + {{"--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, }; for (auto const& [assemblyOptions, expectedMachine, expectedLanguage]: allowedCombinations) @@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "--ewasm-ir", }; commandLine += assemblyOptions; - if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + if (expectedLanguage == YulStack::Language::StrictAssembly || expectedLanguage == YulStack::Language::Ewasm) commandLine += vector{ "--optimize", "--optimize-runs=1000", @@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.compiler.outputs.irOptimized = true; expectedOptions.compiler.outputs.ewasm = true; expectedOptions.compiler.outputs.ewasmIR = true; - if (expectedLanguage == AssemblyStack::Language::StrictAssembly || expectedLanguage == AssemblyStack::Language::Ewasm) + if (expectedLanguage == YulStack::Language::StrictAssembly || expectedLanguage == YulStack::Language::Ewasm) { expectedOptions.optimizer.enabled = true; expectedOptions.optimizer.yulSteps = "agf"; diff --git a/test/tools/ossfuzz/SolidityEvmoneInterface.h b/test/tools/ossfuzz/SolidityEvmoneInterface.h index da0fed58f..734caf560 100644 --- a/test/tools/ossfuzz/SolidityEvmoneInterface.h +++ b/test/tools/ossfuzz/SolidityEvmoneInterface.h @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/test/tools/ossfuzz/YulEvmoneInterface.cpp b/test/tools/ossfuzz/YulEvmoneInterface.cpp index e21a881c6..2d7ddccc1 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.cpp +++ b/test/tools/ossfuzz/YulEvmoneInterface.cpp @@ -35,7 +35,7 @@ bytes YulAssembler::assemble() if (m_optimiseYul) m_stack.optimize(); - return m_stack.assemble(AssemblyStack::Machine::EVM).bytecode->bytecode; + return m_stack.assemble(YulStack::Machine::EVM).bytecode->bytecode; } evmc::result YulEvmoneUtility::deployCode(bytes const& _input, EVMHost& _host) diff --git a/test/tools/ossfuzz/YulEvmoneInterface.h b/test/tools/ossfuzz/YulEvmoneInterface.h index 0b3384940..709e5475f 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.h +++ b/test/tools/ossfuzz/YulEvmoneInterface.h @@ -19,7 +19,7 @@ #include -#include +#include #include @@ -37,7 +37,7 @@ public: ): m_stack( _version, - solidity::yul::AssemblyStack::Language::StrictAssembly, + solidity::yul::YulStack::Language::StrictAssembly, _optSettings, langutil::DebugInfoSelection::All() ), @@ -46,7 +46,7 @@ public: {} solidity::bytes assemble(); private: - solidity::yul::AssemblyStack m_stack; + solidity::yul::YulStack m_stack; std::string m_yulProgram; bool m_optimiseYul; }; diff --git a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp index 7142ec23c..a353ca10b 100644 --- a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include #include @@ -37,9 +37,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); string input(reinterpret_cast(_data), _size); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), langutil::DebugInfoSelection::All() ); @@ -49,7 +49,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) try { - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); + MachineAssemblyObject obj = stack.assemble(YulStack::Machine::EVM); solAssert(obj.bytecode, ""); } catch (StackTooDeepError const&) diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index dce6c6162..dd66102c1 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -60,9 +60,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp index 05a0179bf..94a837d5a 100644 --- a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include #include @@ -38,9 +38,9 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) YulStringRepository::reset(); string input(reinterpret_cast(_data), _size); - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/yulProtoFuzzer.cpp b/test/tools/ossfuzz/yulProtoFuzzer.cpp index ca15b3769..74183aa3f 100644 --- a/test/tools/ossfuzz/yulProtoFuzzer.cpp +++ b/test/tools/ossfuzz/yulProtoFuzzer.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include @@ -61,10 +61,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) YulStringRepository::reset(); - // AssemblyStack entry point - AssemblyStack stack( + // YulStack entry point + YulStack stack( version, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 4cafccb28..c3ad9c9f3 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -26,7 +26,7 @@ #include -#include +#include #include #include @@ -60,10 +60,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) YulStringRepository::reset(); - // AssemblyStack entry point - AssemblyStack stack( + // YulStack entry point + YulStack stack( version, - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::full(), DebugInfoSelection::All() ); diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index 570299768..fd43d222d 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -56,9 +56,9 @@ namespace pair, shared_ptr> parse(string const& _source) { - AssemblyStack stack( + YulStack stack( langutil::EVMVersion(), - AssemblyStack::Language::StrictAssembly, + YulStack::Language::StrictAssembly, solidity::frontend::OptimiserSettings::none(), DebugInfoSelection::Default() ); From a608bc12f78c5eca014719cfb18c018fa0286a24 Mon Sep 17 00:00:00 2001 From: Tharun K <53267275+tharun634@users.noreply.github.com> Date: Thu, 7 Apr 2022 18:02:06 +0530 Subject: [PATCH 43/51] Moved `docs.sh` script to `docs/` --- .circleci/config.yml | 2 +- docs/contributing.rst | 2 +- {scripts => docs}/docs.sh | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {scripts => docs}/docs.sh (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index bcfc99cd7..1b6fc6b9d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -987,7 +987,7 @@ jobs: - run: *setup_prerelease_commit_hash - run: name: Build documentation - command: ./scripts/docs.sh + command: ./docs/docs.sh - store_artifacts: path: docs/_build/html/ destination: docs-html diff --git a/docs/contributing.rst b/docs/contributing.rst index ba5a0a0f8..0c2ee9124 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -475,7 +475,7 @@ For example ``pragma solidity >=0.4.0 <0.9.0;``. Running Documentation Tests --------------------------- -Make sure your contributions pass our documentation tests by running ``./scripts/docs.sh`` that installs dependencies +Make sure your contributions pass our documentation tests by running ``./docs/docs.sh`` that installs dependencies needed for documentation and checks for any problems such as broken links or syntax issues. Solidity Language Design diff --git a/scripts/docs.sh b/docs/docs.sh similarity index 100% rename from scripts/docs.sh rename to docs/docs.sh From efd0024853f1867c6703621aeddc28614b896d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 Apr 2022 20:40:17 +0200 Subject: [PATCH 44/51] docs.sh: Make the script independent of the working directory --- docs/docs.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docs.sh b/docs/docs.sh index 30bb96f61..ec3f3899a 100755 --- a/docs/docs.sh +++ b/docs/docs.sh @@ -27,7 +27,9 @@ #------------------------------------------------------------------------------ set -e -cd docs + +script_dir="$(dirname "$0")" + +cd "${script_dir}" pip3 install -r requirements.txt --upgrade --upgrade-strategy eager sphinx-build -nW -b html -d _build/doctrees . _build/html -cd .. From 23e77e9a8483b8a865fe419b5d5af7c7687384e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 Apr 2022 20:40:52 +0200 Subject: [PATCH 45/51] docs.sh: Fail on all errors, includding undefined variables and failures in piped commands --- docs/docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs.sh b/docs/docs.sh index ec3f3899a..f2c566761 100755 --- a/docs/docs.sh +++ b/docs/docs.sh @@ -26,7 +26,7 @@ # (c) 2016 solidity contributors. #------------------------------------------------------------------------------ -set -e +set -euo pipefail script_dir="$(dirname "$0")" From 9867fbcfb0a19dc5ad328c2778848888bf12da56 Mon Sep 17 00:00:00 2001 From: "sourabh.xyz" Date: Sat, 9 Apr 2022 13:17:07 +0530 Subject: [PATCH 46/51] Update inheritance.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed typo 'owned' → 'Owned'. --- docs/contracts/inheritance.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 25838f3c5..a33a36d27 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -76,9 +76,9 @@ Details are given in the following example. } - // Multiple inheritance is possible. Note that `owned` is + // Multiple inheritance is possible. Note that `Owned` is // also a base class of `Destructible`, yet there is only a single - // instance of `owned` (as for virtual inheritance in C++). + // instance of `Owned` (as for virtual inheritance in C++). contract Named is Owned, Destructible { constructor(bytes32 name) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); From f9fa76c9d3177c629c0f94094867f81cbd8f50cc Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Mon, 20 Dec 2021 13:59:30 +0100 Subject: [PATCH 47/51] smt encode call --- Changelog.md | 1 + libsolidity/formal/SMTEncoder.cpp | 11 ++++++++ .../abi/abi_encode_call_simple.sol | 26 ------------------- .../abi/abi_encode_call_simple_1.sol | 22 ++++++++++++++++ .../abi/abi_encode_call_simple_2.sol | 21 +++++++++++++++ .../abi/abi_encode_call_simple_3.sol | 21 +++++++++++++++ .../abi/abi_encode_call_simple_4.sol | 25 ++++++++++++++++++ .../abi/abi_encode_call_simple_5.sol | 25 ++++++++++++++++++ 8 files changed, 126 insertions(+), 26 deletions(-) delete mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol create mode 100644 test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol diff --git a/Changelog.md b/Changelog.md index 203081823..26ba5965d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: * Peephole Optimizer: Remove operations without side effects before simple terminations. * Assembly-Json: Export: Include source list in `sourceList` field. * Commandline Interface: option ``--pretty-json`` works also with the following options: ``--abi``, ``--asm-json``, ``--ast-compact-json``, ``--devdoc``, ``--storage-layout``, ``--userdoc``. + * SMTChecker: Support ``abi.encodeCall`` taking into account the called selector. Bugfixes: diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 7cc978d28..8c5dd647e 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -586,6 +586,16 @@ bool SMTEncoder::visit(FunctionCall const& _funCall) arg->accept(*this); return false; } + else if (funType.kind() == FunctionType::Kind::ABIEncodeCall) + { + auto fun = _funCall.arguments().front(); + createExpr(*fun); + auto const* functionType = dynamic_cast(fun->annotation().type); + if (functionType->hasDeclaration()) + defineExpr(*fun, functionType->externalIdentifier()); + return true; + } + // We do not really need to visit the expression in a wrap/unwrap no-op call, // so we just ignore the function call expression to avoid "unsupported" warnings. else if ( @@ -1323,6 +1333,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) auto const& exprType = memberExpr->annotation().type; solAssert(exprType, ""); + if (exprType->category() == Type::Category::Magic) { if (auto const* identifier = dynamic_cast(memberExpr)) diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol deleted file mode 100644 index 72fd4290c..000000000 --- a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple.sol +++ /dev/null @@ -1,26 +0,0 @@ -contract C { - function callMeMaybe(uint a, uint b, uint[] memory c) external {} - - function abiEncodeSimple(uint x, uint y, uint z, uint[] memory a, uint[] memory b) public view { - require(x == y); - bytes memory b1 = abi.encodeCall(this.callMeMaybe, (x, z, a)); - bytes memory b2 = abi.encodeCall(this.callMeMaybe, (y, z, a)); - assert(b1.length == b2.length); - - bytes memory b3 = abi.encodeCall(this.callMeMaybe, (y, z, b)); - assert(b1.length == b3.length); // should fail - } -} -// ==== -// SMTEngine: all -// SMTIgnoreCex: yes -// ---- -// Warning 6031: (233-249): Internal error: Expression undefined for SMT solver. -// Warning 6031: (298-314): Internal error: Expression undefined for SMT solver. -// Warning 6031: (398-414): Internal error: Expression undefined for SMT solver. -// Warning 1218: (330-360): CHC: Error trying to invoke SMT solver. -// Warning 1218: (430-460): CHC: Error trying to invoke SMT solver. -// Warning 6328: (330-360): CHC: Assertion violation might happen here. -// Warning 6328: (430-460): CHC: Assertion violation might happen here. -// Warning 4661: (330-360): BMC: Assertion violation happens here. -// Warning 4661: (430-460): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol new file mode 100644 index 000000000..be0bc4769 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_1.sol @@ -0,0 +1,22 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public view { + require(x == y); + function (uint, uint) external f = this.callMeMaybe; + bytes memory b1 = abi.encodeCall(f, (x, z)); + bytes memory b2 = abi.encodeCall(f, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(this.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold, but we don't encode the length computation precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (354-359): CHC: Out of bounds access happens here. +// Warning 6368: (363-368): CHC: Out of bounds access happens here. +// Warning 6328: (451-481): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol new file mode 100644 index 000000000..c6044a8cd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_2.sol @@ -0,0 +1,21 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public view { + require(x == y); + bytes memory b1 = abi.encodeCall(this.callMeMaybe, (x, z)); + bytes memory b2 = abi.encodeCall(this.callMeMaybe, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(this.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (329-334): CHC: Out of bounds access happens here. +// Warning 6368: (338-343): CHC: Out of bounds access happens here. +// Warning 6328: (426-456): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol new file mode 100644 index 000000000..a3b20f7f7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_3.sol @@ -0,0 +1,21 @@ +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(C.callMeMaybe, (x, z)); + bytes memory b2 = abi.encodeCall(C.callMeMaybe, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(C.callMeMaybe, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (323-328): CHC: Out of bounds access happens here. +// Warning 6368: (332-337): CHC: Out of bounds access happens here. +// Warning 6328: (417-447): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol new file mode 100644 index 000000000..6f0128f8a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_4.sol @@ -0,0 +1,25 @@ +abstract contract D { + function no(uint a, uint b) external virtual; +} + +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(D.no, (x, z)); + bytes memory b2 = abi.encodeCall(D.no, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(D.no, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (377-382): CHC: Out of bounds access happens here. +// Warning 6368: (386-391): CHC: Out of bounds access happens here. +// Warning 6328: (462-492): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol new file mode 100644 index 000000000..98e42ad9f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/abi/abi_encode_call_simple_5.sol @@ -0,0 +1,25 @@ +abstract contract D { + function no(uint a, uint b) external virtual; +} + +contract C { + function callMeMaybe(uint a, uint b) external {} + + function abiEncodeSimple(D d, uint x, uint y, uint z) public pure { + require(x == y); + bytes memory b1 = abi.encodeCall(d.no, (x, z)); + bytes memory b2 = abi.encodeCall(d.no, (y, z)); + assert(b1.length == b2.length); // should hold + assert(b1[0] == b2[0]); // should hold + + bytes memory b3 = abi.encodeCall(d.no, (3, z)); + assert(b1.length == b3.length); // should hold but we don't compute the length precisely + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6368: (382-387): CHC: Out of bounds access happens here. +// Warning 6368: (391-396): CHC: Out of bounds access happens here. +// Warning 6328: (467-497): CHC: Assertion violation happens here. From 6931c939e086a3ddc6056ca3adc7566267359870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 11 Apr 2022 15:54:56 +0200 Subject: [PATCH 48/51] CI: Make osx test jobs handle dependency installation even if b_osx does not install them --- .circleci/config.yml | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1b6fc6b9d..7875197f7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -249,8 +249,20 @@ defaults: - restore_cache: keys: - dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - - attach_workspace: - at: . + # DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh + # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. + - run: + name: Install build dependencies + command: ./.circleci/osx_install_dependencies.sh + - save_cache: + key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + paths: + - /usr/local/bin + - /usr/local/sbin + - /usr/local/lib + - /usr/local/include + - /usr/local/Cellar + - /usr/local/Homebrew # -------------------------------------------------------------------------- # Base Image Templates @@ -900,23 +912,9 @@ jobs: MAKEFLAGS: -j10 steps: - checkout - - restore_cache: - keys: - - dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - # DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh - # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. - - run: - name: Install build dependencies - command: ./.circleci/osx_install_dependencies.sh - - save_cache: - key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - paths: - - /usr/local/bin - - /usr/local/sbin - - /usr/local/lib - - /usr/local/include - - /usr/local/Cellar - - /usr/local/Homebrew + - when: + condition: true + <<: *steps_install_dependencies_osx - run: *run_build - store_artifacts: *artifacts_solc - store_artifacts: *artifact_solidity_upgrade @@ -940,6 +938,8 @@ jobs: - when: condition: true <<: *steps_install_dependencies_osx + - attach_workspace: + at: . - run: *run_soltest - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -952,6 +952,8 @@ jobs: - when: condition: true <<: *steps_install_dependencies_osx + - attach_workspace: + at: . - run: *run_cmdline_tests - store_artifacts: *artifacts_test_results - gitter_notify_failure_unless_pr From 4ac46ebf40cf0e019224af6ba45ded71038959a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 11 Apr 2022 14:06:42 +0200 Subject: [PATCH 49/51] Disable dependency caching on macOS in CI --- .circleci/config.yml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7875197f7..a75e372ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -246,23 +246,11 @@ defaults: - steps_install_dependencies_osx: &steps_install_dependencies_osx steps: - - restore_cache: - keys: - - dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - # DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh - # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. + # FIXME: We used to cache dependencies on macOS but now it takes longer than just installing + # them each time. See https://github.com/ethereum/solidity/issues/12925. - run: name: Install build dependencies command: ./.circleci/osx_install_dependencies.sh - - save_cache: - key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - paths: - - /usr/local/bin - - /usr/local/sbin - - /usr/local/lib - - /usr/local/include - - /usr/local/Cellar - - /usr/local/Homebrew # -------------------------------------------------------------------------- # Base Image Templates From e30d0a61992e423250b76855e1e3ef6088ed6c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 19 Jan 2022 18:17:38 +0100 Subject: [PATCH 50/51] externalTests: Allow adding extra settings to presets --- test/externalTests/common.sh | 45 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index 9c68c8828..c510ebc92 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -198,6 +198,8 @@ function force_truffle_compiler_settings local solc_path="$3" local preset="$4" local evm_version="${5:-"$CURRENT_EVM_VERSION"}" + local extra_settings="$6" + local extra_optimizer_settings="$7" [[ $binary_type == native || $binary_type == solcjs ]] || assertFail @@ -209,14 +211,14 @@ function force_truffle_compiler_settings echo "Binary type: $binary_type" echo "Compiler path: $solc_path" echo "Settings preset: ${preset}" - echo "Settings: $(settings_from_preset "$preset" "$evm_version")" + echo "Settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")" echo "EVM version: $evm_version" echo "Compiler version: ${SOLCVERSION_SHORT}" echo "Compiler version (full): ${SOLCVERSION}" echo "-------------------------------------" local compiler_settings gas_reporter_settings - compiler_settings=$(truffle_compiler_settings "$solc_path" "$preset" "$evm_version") + compiler_settings=$(truffle_compiler_settings "$solc_path" "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings") gas_reporter_settings=$(eth_gas_reporter_settings "$preset") { @@ -307,19 +309,21 @@ function force_hardhat_compiler_settings local preset="$2" local config_var_name="$3" local evm_version="${4:-"$CURRENT_EVM_VERSION"}" + local extra_settings="$5" + local extra_optimizer_settings="$6" printLog "Configuring Hardhat..." echo "-------------------------------------" echo "Config file: ${config_file}" echo "Settings preset: ${preset}" - echo "Settings: $(settings_from_preset "$preset" "$evm_version")" + echo "Settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")" echo "EVM version: ${evm_version}" echo "Compiler version: ${SOLCVERSION_SHORT}" echo "Compiler version (full): ${SOLCVERSION}" echo "-------------------------------------" local compiler_settings gas_reporter_settings - compiler_settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version") + compiler_settings=$(hardhat_compiler_settings "$SOLCVERSION_SHORT" "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings") gas_reporter_settings=$(eth_gas_reporter_settings "$preset") if [[ $config_file == *\.js ]]; then [[ $config_var_name == "" ]] || assertFail @@ -372,17 +376,22 @@ function settings_from_preset { local preset="$1" local evm_version="$2" + local extra_settings="$3" + local extra_optimizer_settings="$4" [[ " ${AVAILABLE_PRESETS[*]} " == *" $preset "* ]] || assertFail + [[ $extra_settings == "" ]] || extra_settings="${extra_settings}, " + [[ $extra_optimizer_settings == "" ]] || extra_optimizer_settings="${extra_optimizer_settings}, " + case "$preset" in # NOTE: Remember to update `parallelism` of `t_ems_ext` job in CI config if you add/remove presets - legacy-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: false}}" ;; - ir-no-optimize) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: false}}" ;; - legacy-optimize-evm-only) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: true, details: {yul: false}}}" ;; - ir-optimize-evm-only) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: true, details: {yul: false}}}" ;; - legacy-optimize-evm+yul) echo "{evmVersion: '${evm_version}', viaIR: false, optimizer: {enabled: true, details: {yul: true}}}" ;; - ir-optimize-evm+yul) echo "{evmVersion: '${evm_version}', viaIR: true, optimizer: {enabled: true, details: {yul: true}}}" ;; + legacy-no-optimize) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: false}}" ;; + ir-no-optimize) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: false}}" ;; + legacy-optimize-evm-only) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: false}}}" ;; + ir-optimize-evm-only) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: false}}}" ;; + legacy-optimize-evm+yul) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: false, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: true}}}" ;; + ir-optimize-evm+yul) echo "{${extra_settings}evmVersion: '${evm_version}', viaIR: true, optimizer: {${extra_optimizer_settings}enabled: true, details: {yul: true}}}" ;; *) fail "Unknown settings preset: '${preset}'." ;; @@ -419,11 +428,13 @@ function truffle_compiler_settings local solc_path="$1" local preset="$2" local evm_version="$3" + local extra_settings="$4" + local extra_optimizer_settings="$5" echo "{" echo " solc: {" echo " version: \"${solc_path}\"," - echo " settings: $(settings_from_preset "$preset" "$evm_version")" + echo " settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")" echo " }" echo "}" } @@ -468,10 +479,12 @@ function hardhat_compiler_settings { local solc_version="$1" local preset="$2" local evm_version="$3" + local extra_settings="$4" + local extra_optimizer_settings="$5" echo "{" echo " version: '${solc_version}'," - echo " settings: $(settings_from_preset "$preset" "$evm_version")" + echo " settings: $(settings_from_preset "$preset" "$evm_version" "$extra_settings" "$extra_optimizer_settings")" echo "}" } @@ -506,9 +519,11 @@ function truffle_run_test local compile_only_presets="$5" local compile_fn="$6" local test_fn="$7" + local extra_settings="$8" + local extra_optimizer_settings="$9" truffle_clean - force_truffle_compiler_settings "$config_file" "$binary_type" "$solc_path" "$preset" + force_truffle_compiler_settings "$config_file" "$binary_type" "$solc_path" "$preset" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings" compile_and_run_test compile_fn test_fn truffle_verify_compiler_version "$preset" "$compile_only_presets" } @@ -520,9 +535,11 @@ function hardhat_run_test local compile_fn="$4" local test_fn="$5" local config_var_name="$6" + local extra_settings="$7" + local extra_optimizer_settings="$8" hardhat_clean - force_hardhat_compiler_settings "$config_file" "$preset" "$config_var_name" + force_hardhat_compiler_settings "$config_file" "$preset" "$config_var_name" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings" compile_and_run_test compile_fn test_fn hardhat_verify_compiler_version "$preset" "$compile_only_presets" } From 8e085432bb7e9e6b1f4992d43a24fbf8dcf179b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 19 Jan 2022 19:24:24 +0100 Subject: [PATCH 51/51] External test for Brink --- .circleci/config.yml | 8 ++++ test/externalTests.sh | 1 + test/externalTests/brink.sh | 83 +++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100755 test/externalTests/brink.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index a75e372ae..d42021373 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -582,6 +582,12 @@ defaults: binary_type: native nodejs_version: '16' resource_class: medium + - job_native_test_ext_brink: &job_native_test_ext_brink + <<: *workflow_ubuntu2004_static + name: t_native_test_ext_brink + project: brink + binary_type: native + nodejs_version: '16' - job_ems_test_ext_colony: &job_ems_test_ext_colony <<: *workflow_emscripten name: t_ems_test_ext_colony @@ -1470,6 +1476,7 @@ workflows: - t_ems_ext: *job_native_test_ext_uniswap - t_ems_ext: *job_native_test_ext_prb_math - t_ems_ext: *job_native_test_ext_elementfi + - t_ems_ext: *job_native_test_ext_brink - c_ext_benchmarks: <<: *workflow_trigger_on_tags @@ -1487,6 +1494,7 @@ workflows: - t_native_test_ext_uniswap - t_native_test_ext_prb_math - t_native_test_ext_elementfi + - t_native_test_ext_brink # Windows build and tests - b_win: *workflow_trigger_on_tags diff --git a/test/externalTests.sh b/test/externalTests.sh index 89e7515d4..8c581a165 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -50,3 +50,4 @@ printTask "Running external tests..." "${REPO_ROOT}/test/externalTests/uniswap.sh" "$@" "${REPO_ROOT}/test/externalTests/prb-math.sh" "$@" "${REPO_ROOT}/test/externalTests/elementfi.sh" "$@" +"${REPO_ROOT}/test/externalTests/brink.sh" "$@" diff --git a/test/externalTests/brink.sh b/test/externalTests/brink.sh new file mode 100755 index 000000000..1513350bb --- /dev/null +++ b/test/externalTests/brink.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# 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 +# +# (c) 2022 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +source scripts/common.sh +source test/externalTests/common.sh + +REPO_ROOT=$(realpath "$(dirname "$0")/../..") + +verify_input "$@" +BINARY_TYPE="$1" +BINARY_PATH="$2" +SELECTED_PRESETS="$3" + +function compile_fn { yarn compile; } +function test_fn { SNAPSHOT_UPDATE=1 npx --no hardhat test; } + +function brink_test +{ + local repo="https://github.com/brinktrade/brink-core" + local ref_type=branch + local ref=master + local config_file="hardhat.config.js" + local config_var="" + local extra_settings="metadata: {bytecodeHash: 'none'}" + local extra_optimizer_settings="runs: 800" + + local compile_only_presets=( + #ir-no-optimize # Compilation fails with "YulException: Variable var_signature_127_offset is 2 slot(s) too deep inside the stack." + #ir-optimize-evm-only # Compilation fails with "YulException: Variable var_signature_127_offset is 2 slot(s) too deep inside the stack." + ir-optimize-evm+yul # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10. + legacy-optimize-evm+yul # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10. + legacy-no-optimize # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10. + legacy-optimize-evm-only # Lots of test failures. Tests depend on constants.js, which seems to be calculated specifically for 0.8.10. + ) + local settings_presets=( + "${compile_only_presets[@]}" + ) + + [[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}") + print_presets_or_exit "$SELECTED_PRESETS" + + setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" + download_project "$repo" "$ref_type" "$ref" "$DIR" + + neutralize_package_lock + neutralize_package_json_hooks + force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" + force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings" + yarn install + yarn add hardhat-gas-reporter + + # TODO: Remove when https://github.com/brinktrade/brink-core/issues/48 is fixed. + yarn add chai + + replace_version_pragmas + + for preset in $SELECTED_PRESETS; do + hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var" "$extra_settings" "$extra_optimizer_settings" + store_benchmark_report hardhat brink "$repo" "$preset" + done +} + +external_test Brink brink_test