From a7a8ba73f9d75f18b6ca22f92aaeb3bcf0f8c811 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sun, 21 Jul 2019 15:46:03 +0200 Subject: [PATCH 1/5] [isoltest] Moves error types to dedicated file. --- test/TestCase.h | 8 -- test/libsolidity/util/SoltestErrors.h | 128 +++++++++++++++++++++++ test/libsolidity/util/TestFunctionCall.h | 91 +--------------- test/libyul/YulOptimizerTest.cpp | 1 + 4 files changed, 130 insertions(+), 98 deletions(-) create mode 100644 test/libsolidity/util/SoltestErrors.h diff --git a/test/TestCase.h b/test/TestCase.h index 4a9c728ca..7b3ab5fa6 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -35,14 +35,6 @@ namespace solidity namespace test { -#define soltestAssert(CONDITION, DESCRIPTION) \ - do \ - { \ - if (!(CONDITION)) \ - BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \ - } \ - while (false) - /** * Common superclass of anything that can be run via isoltest. */ diff --git a/test/libsolidity/util/SoltestErrors.h b/test/libsolidity/util/SoltestErrors.h new file mode 100644 index 000000000..23b5afc42 --- /dev/null +++ b/test/libsolidity/util/SoltestErrors.h @@ -0,0 +1,128 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +#define soltestAssert(CONDITION, DESCRIPTION) \ + do \ + { \ + if (!(CONDITION)) \ + BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \ + } \ + while (false) + + +/** + * Representation of a notice, warning or error that can occur while + * formatting and therefore updating an interactive function call test. + */ +struct FormatError +{ + enum Type + { + Notice, + Warning, + Error + }; + + explicit FormatError(Type _type, std::string _message): + type(_type), + message(std::move(_message)) + {} + + Type type; + std::string message; +}; +using FormatErrors = std::vector; + +/** + * Utility class that collects notices, warnings and errors and is able + * to format them for ANSI colorized output during the interactive update + * process in isoltest. + * Its purpose is to help users of isoltest to automatically + * update test files and always keep track of what is happening. + */ +class ErrorReporter +{ +public: + explicit ErrorReporter() {} + + /// Adds a new FormatError of type Notice with the given message. + void notice(std::string _notice) + { + m_errors.push_back(FormatError{FormatError::Notice, std::move(_notice)}); + } + + /// Adds a new FormatError of type Warning with the given message. + void warning(std::string _warning) + { + m_errors.push_back(FormatError{FormatError::Warning, std::move(_warning)}); + } + + /// Adds a new FormatError of type Error with the given message. + void error(std::string _error) + { + m_errors.push_back(FormatError{FormatError::Error, std::move(_error)}); + } + + /// Prints all errors depending on their type using ANSI colorized output. + /// It will be used to print notices, warnings and errors during the + /// interactive update process. + std::string format(std::string const& _linePrefix, bool _formatted) + { + std::stringstream os; + for (auto const& error: m_errors) + { + switch (error.type) + { + case FormatError::Notice: + + break; + case FormatError::Warning: + AnsiColorized( + os, + _formatted, + {formatting::YELLOW} + ) << _linePrefix << "Warning: " << error.message << std::endl; + break; + case FormatError::Error: + AnsiColorized( + os, + _formatted, + {formatting::RED} + ) << _linePrefix << "Error: " << error.message << std::endl; + break; + } + } + return os.str(); + } + +private: + FormatErrors m_errors; +}; + +} +} +} diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index 1d8bc9e98..9a49777fd 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -15,7 +15,7 @@ #pragma once #include -#include +#include #include #include @@ -37,95 +37,6 @@ namespace solidity namespace test { -/** - * Representation of a notice, warning or error that can occur while - * formatting and therefore updating an interactive function call test. - */ -struct FormatError -{ - enum Type - { - Notice, - Warning, - Error - }; - - explicit FormatError(Type _type, std::string _message): - type(_type), - message(std::move(_message)) - {} - - Type type; - std::string message; -}; -using FormatErrors = std::vector; - -/** - * Utility class that collects notices, warnings and errors and is able - * to format them for ANSI colorized output during the interactive update - * process in isoltest. - * Its purpose is to help users of isoltest to automatically - * update test files and always keep track of what is happening. - */ -class ErrorReporter -{ -public: - explicit ErrorReporter() {} - - /// Adds a new FormatError of type Notice with the given message. - void notice(std::string _notice) - { - m_errors.push_back(FormatError{FormatError::Notice, std::move(_notice)}); - } - - /// Adds a new FormatError of type Warning with the given message. - void warning(std::string _warning) - { - m_errors.push_back(FormatError{FormatError::Warning, std::move(_warning)}); - } - - /// Adds a new FormatError of type Error with the given message. - void error(std::string _error) - { - m_errors.push_back(FormatError{FormatError::Error, std::move(_error)}); - } - - /// Prints all errors depending on their type using ANSI colorized output. - /// It will be used to print notices, warnings and errors during the - /// interactive update process. - std::string format(std::string const& _linePrefix, bool _formatted) - { - std::stringstream os; - for (auto const& error: m_errors) - { - switch (error.type) - { - case FormatError::Notice: - - break; - case FormatError::Warning: - AnsiColorized( - os, - _formatted, - {formatting::YELLOW} - ) << _linePrefix << "Warning: " << error.message << std::endl; - break; - case FormatError::Error: - AnsiColorized( - os, - _formatted, - {formatting::RED} - ) << _linePrefix << "Error: " << error.message << std::endl; - break; - } - } - return os.str(); - } - -private: - FormatErrors m_errors; -}; - /** * Represents a function call and the result it returned. It stores the call * representation itself, the actual byte result (if any) and a string representation diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 074b9b02a..019536b9d 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -17,6 +17,7 @@ #include +#include #include #include From d9b98bf7aff2d60b85a9c0bf538758f9dc9fc8ba Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sun, 21 Jul 2019 15:58:12 +0200 Subject: [PATCH 2/5] [isoltest] Cleans up BytesUtils. --- test/libsolidity/util/BytesUtils.cpp | 100 ++++++++++++----------- test/libsolidity/util/BytesUtils.h | 52 ++++++------ test/libsolidity/util/TestFileParser.cpp | 19 ++--- 3 files changed, 87 insertions(+), 84 deletions(-) diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index 6c37511a0..99be5ae21 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -17,6 +17,8 @@ #include +#include + #include #include @@ -34,6 +36,40 @@ using namespace dev::solidity::test; using namespace std; using namespace soltest; +bytes BytesUtils::alignLeft(bytes _bytes) +{ + soltestAssert(_bytes.size() <= 32, ""); + size_t size = _bytes.size(); + return std::move(_bytes) + bytes(32 - size, 0); +} + +bytes BytesUtils::alignRight(bytes _bytes) +{ + soltestAssert(_bytes.size() <= 32, ""); + return bytes(32 - _bytes.size(), 0) + std::move(_bytes); +} + +bytes BytesUtils::applyAlign( + Parameter::Alignment _alignment, + ABIType& _abiType, + bytes _bytes +) +{ + if (_alignment != Parameter::Alignment::None) + _abiType.alignDeclared = true; + + switch (_alignment) + { + case Parameter::Alignment::Left: + _abiType.align = ABIType::AlignLeft; + return alignLeft(std::move(_bytes)); + case Parameter::Alignment::Right: + default: + _abiType.align = ABIType::AlignRight; + return alignRight(std::move(_bytes)); + } +} + bytes BytesUtils::convertBoolean(string const& _literal) { if (_literal == "true") @@ -83,10 +119,21 @@ bytes BytesUtils::convertString(string const& _literal) } } -string BytesUtils::formatUnsigned(bytes const& _bytes) const +string BytesUtils::formatUnsigned(bytes const& _bytes) { stringstream os; + soltestAssert(!_bytes.empty() && _bytes.size() <= 32, ""); + + return fromBigEndian(_bytes).str(); +} + +string BytesUtils::formatSigned(bytes const& _bytes) +{ + stringstream os; + + soltestAssert(!_bytes.empty() && _bytes.size() <= 32, ""); + if (*_bytes.begin() & 0x80) os << u2s(fromBigEndian(_bytes)); else @@ -95,19 +142,7 @@ string BytesUtils::formatUnsigned(bytes const& _bytes) const return os.str(); } -string BytesUtils::formatSigned(bytes const& _bytes) const -{ - stringstream os; - - if (*_bytes.begin() & 0x80) - os << u2s(fromBigEndian(_bytes)); - else - os << fromBigEndian(_bytes); - - return os.str(); -} - -string BytesUtils::formatBoolean(bytes const& _bytes) const +string BytesUtils::formatBoolean(bytes const& _bytes) { stringstream os; u256 result = fromBigEndian(_bytes); @@ -122,7 +157,7 @@ string BytesUtils::formatBoolean(bytes const& _bytes) const return os.str(); } -string BytesUtils::formatHex(bytes const& _bytes) const +string BytesUtils::formatHex(bytes const& _bytes) { stringstream os; @@ -133,7 +168,7 @@ string BytesUtils::formatHex(bytes const& _bytes) const return os.str(); } -string BytesUtils::formatHexString(bytes const& _bytes) const +string BytesUtils::formatHexString(bytes const& _bytes) { stringstream os; @@ -170,36 +205,3 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const return os.str(); } - -bytes BytesUtils::alignLeft(bytes _bytes) const -{ - return std::move(_bytes) + bytes(32 - _bytes.size(), 0); -} - -bytes BytesUtils::alignRight(bytes _bytes) const -{ - return bytes(32 - _bytes.size(), 0) + std::move(_bytes); -} - -bytes BytesUtils::applyAlign( - Parameter::Alignment _alignment, - ABIType& _abiType, - bytes _bytes -) const -{ - if (_alignment != Parameter::Alignment::None) - _abiType.alignDeclared = true; - - switch (_alignment) - { - case Parameter::Alignment::Left: - _abiType.align = ABIType::AlignLeft; - return alignLeft(std::move(_bytes)); - case Parameter::Alignment::Right: - _abiType.align = ABIType::AlignRight; - return alignRight(std::move(_bytes)); - default: - _abiType.align = ABIType::AlignRight; - return alignRight(std::move(_bytes)); - } -} diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index 8b2792692..e22df452a 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -32,46 +32,64 @@ namespace test class BytesUtils { public: + /// Left-aligns and pads given _bytes and returns a new + /// bytes array. + static bytes alignLeft(bytes _bytes); + + /// Right-aligns and pads given _bytes and returns a new + /// bytes array. + static bytes alignRight(bytes _bytes); + + /// Applies given _alignment to _bytes and returns a new + /// bytes array. + /// TODO: Remove abiType reference from parameter list + /// and return the new alignment instead. + static bytes applyAlign( + Parameter::Alignment _alignment, + ABIType& _abiType, + bytes _bytes + ); + /// Tries to convert \param _literal to an unpadded `bytes` /// representation of the boolean number literal. Throws if conversion fails. - bytes convertBoolean(std::string const& _literal); + static bytes convertBoolean(std::string const& _literal); /// Tries to convert \param _literal to an unpadded `bytes` /// representation of the decimal number literal. Throws if conversion fails. - bytes convertNumber(std::string const& _literal); + static bytes convertNumber(std::string const& _literal); /// Tries to convert \param _literal to an unpadded `bytes` /// representation of the hex literal. Throws if conversion fails. - bytes convertHexNumber(std::string const& _literal); + static bytes convertHexNumber(std::string const& _literal); /// Tries to convert \param _literal to an unpadded `bytes` /// representation of the string literal. Throws if conversion fails. - bytes convertString(std::string const& _literal); + static bytes convertString(std::string const& _literal); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// an unsigned value. - std::string formatUnsigned(bytes const& _bytes) const; + static std::string formatUnsigned(bytes const& _bytes); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// a signed value. - std::string formatSigned(bytes const& _bytes) const; + static std::string formatSigned(bytes const& _bytes); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// a boolean value. - std::string formatBoolean(bytes const& _bytes) const; + static std::string formatBoolean(bytes const& _bytes); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// a hex value. - std::string formatHex(bytes const& _bytes) const; + static std::string formatHex(bytes const& _bytes); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// a hexString value. - std::string formatHexString(bytes const& _bytes) const; + static std::string formatHexString(bytes const& _bytes); /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold @@ -82,22 +100,6 @@ public: { return formatString(_bytes, _bytes.size()); } - - /// Left-aligns and pads given _bytes and returns a new - /// bytes array. - bytes alignLeft(bytes _bytes) const; - - /// Right-aligns and pads given _bytes and returns a new - /// bytes array. - bytes alignRight(bytes _bytes) const; - - /// Applies given _alignment to _bytes and returns a new - /// bytes array. - bytes applyAlign( - Parameter::Alignment _alignment, - ABIType& _abiType, - bytes _bytes - ) const; }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 898fafa4f..a5cb14139 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -236,7 +236,6 @@ Parameter TestFileParser::parseParameter() } bool isSigned = false; - if (accept(Token::Left, true)) { parameter.rawString += formatToken(Token::Left); @@ -267,10 +266,10 @@ Parameter TestFileParser::parseParameter() parameter.abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32}; string parsed = parseBoolean(); parameter.rawString += parsed; - parameter.rawBytes = BytesUtils().applyAlign( + parameter.rawBytes = BytesUtils::applyAlign( parameter.alignment, parameter.abiType, - BytesUtils().convertBoolean(parsed) + BytesUtils::convertBoolean(parsed) ); } else if (accept(Token::HexNumber)) @@ -281,10 +280,10 @@ Parameter TestFileParser::parseParameter() parameter.abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32}; string parsed = parseHexNumber(); parameter.rawString += parsed; - parameter.rawBytes = BytesUtils().applyAlign( + parameter.rawBytes = BytesUtils::applyAlign( parameter.alignment, parameter.abiType, - BytesUtils().convertHexNumber(parsed) + BytesUtils::convertHexNumber(parsed) ); } else if (accept(Token::Hex, true)) @@ -296,7 +295,7 @@ Parameter TestFileParser::parseParameter() string parsed = parseString(); parameter.rawString += "hex\"" + parsed + "\""; - parameter.rawBytes = BytesUtils().convertHexNumber(parsed); + parameter.rawBytes = BytesUtils::convertHexNumber(parsed); parameter.abiType = ABIType{ ABIType::HexString, ABIType::AlignNone, parameter.rawBytes.size() }; @@ -311,10 +310,10 @@ Parameter TestFileParser::parseParameter() string parsed = parseString(); parameter.abiType = {ABIType::String, ABIType::AlignLeft, parsed.size()}; parameter.rawString += "\"" + parsed + "\""; - parameter.rawBytes = BytesUtils().applyAlign( + parameter.rawBytes = BytesUtils::applyAlign( Parameter::Alignment::Left, parameter.abiType, - BytesUtils().convertString(parsed) + BytesUtils::convertString(parsed) ); } else if (accept(Token::Number)) @@ -327,10 +326,10 @@ Parameter TestFileParser::parseParameter() if (isSigned) parsed = "-" + parsed; - parameter.rawBytes = BytesUtils().applyAlign( + parameter.rawBytes = BytesUtils::applyAlign( parameter.alignment, parameter.abiType, - BytesUtils().convertNumber(parsed) + BytesUtils::convertNumber(parsed) ); } else if (accept(Token::Failure, true)) From 07051f41d2e830c428a136c0359357a9810f8b93 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sun, 21 Jul 2019 16:04:04 +0200 Subject: [PATCH 3/5] [isoltest] Reworks (default) ABI Type construction. --- test/libsolidity/util/SoltestTypes.h | 26 +++++++++++++++++++++--- test/libsolidity/util/TestFileParser.cpp | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 36586d8ee..075c051a9 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -14,6 +14,7 @@ #pragma once +#include #include #include @@ -111,9 +112,23 @@ struct ABIType AlignRight, AlignNone, }; + + explicit ABIType( + Type _type, + Align _align = ABIType::AlignRight, + size_t _size = 32 + ): type(_type), align(_align), size(_size) {} + + ABIType(ABIType const& _other): + type(_other.type), + align(_other.align), + size(_other.size), + alignDeclared(_other.alignDeclared) + {} + Type type = ABIType::None; Align align = ABIType::AlignRight; - size_t size = 0; + size_t size = 32; bool alignDeclared = false; }; @@ -130,7 +145,7 @@ struct FormatInfo * Parameter abstraction used for the encoding and decoding of * function parameter and expectation / return value lists. * A parameter list is usually a comma-separated list of literals. - * It should not be possible to call create a parameter holding + * It should not be possible to create a parameter holding * an identifier, but if so, the ABI type would be invalid. */ struct Parameter @@ -154,12 +169,17 @@ struct Parameter /// Types that were used to encode `rawBytes`. Expectations /// are usually comma separated literals. Their type is auto- /// detected and retained in order to format them later on. - ABIType abiType; + ABIType abiType = ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; /// Format info attached to the parameter. It handles newlines given /// in the declaration of it. FormatInfo format; /// Stores the parsed alignment, which can be either left(...) or right(...). Alignment alignment = Alignment::None; + /// Compares _bytes to the bytes stored in this object. + bool matchesBytes(bytes const& _bytes) const + { + return rawBytes == _bytes; + } }; using ParameterList = std::vector; diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index a5cb14139..64f4bc135 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -234,6 +234,7 @@ Parameter TestFileParser::parseParameter() parameter.format.newline = true; m_lineNumber++; } + parameter.abiType = ABIType{ABIType::None, ABIType::AlignNone, 0}; bool isSigned = false; if (accept(Token::Left, true)) From 38285b33d7ec6683f26c53ed21ff0533658538ea Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sun, 21 Jul 2019 16:15:02 +0200 Subject: [PATCH 4/5] [isoltest] Prints bytes result if expectations mismatch. --- test/libsolidity/util/BytesUtils.cpp | 21 +++++++++++++++++++++ test/libsolidity/util/BytesUtils.h | 5 +++++ test/libsolidity/util/TestFunctionCall.cpp | 11 +++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index 99be5ae21..b1ce6f8e2 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -205,3 +205,24 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const return os.str(); } + +string BytesUtils::formatRawBytes(bytes const& _bytes) +{ + if (_bytes.empty()) + return "[]"; + + stringstream os; + auto it = _bytes.begin(); + for (size_t i = 0; i < _bytes.size(); i += 32) + { + bytes byteRange{it, it + 32}; + + os << " " << byteRange; + + it += 32; + if (it != _bytes.end()) + os << endl; + } + + return os.str(); +} diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index e22df452a..fc77442cf 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -91,6 +91,11 @@ public: /// a hexString value. static std::string formatHexString(bytes const& _bytes); + /// Returns a string representation of given _bytes. Adds a newline + /// every 32 bytes to increase readability. + /// Used to print returned bytes from function calls to the commandline. + static std::string formatRawBytes(bytes const& _bytes); + /// Converts \param _bytes to a soltest-compliant and human-readable /// string representation of a byte array which is assumed to hold /// a string value. diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 7f495451a..4f401bf1f 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -218,6 +218,7 @@ string TestFunctionCall::formatBytesParameters( " does not match the one inferred from ABI." ); } + _errorReporter.warning("The call to \"" + _signature + "\" returned \n" + BytesUtils::formatRawBytes(_bytes)); /// Prints obtained result if it does not match the expectation /// and prints the expected result otherwise. @@ -289,14 +290,12 @@ string TestFunctionCall::formatRawParameters( stringstream os; for (auto const& param: _params) { - if (param.format.newline) - os << endl << _linePrefix << "// "; - os << param.rawString; - if (¶m != &_params.back()) + if (!param.rawString.empty()) { if (param.format.newline) - os << ","; - else + os << endl << _linePrefix << "// "; + os << param.rawString; + if (¶m != &_params.back()) os << ", "; } } From f914415fb2130e5e6e16bc74afef5a124550fab8 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Sun, 21 Jul 2019 16:58:48 +0200 Subject: [PATCH 5/5] [isoltest] Updates type retrieval from ABI and its formatting. --- test/libsolidity/semanticTests/smoke_test.sol | 59 +++- test/libsolidity/util/BytesUtils.cpp | 75 ++++- test/libsolidity/util/BytesUtils.h | 29 +- test/libsolidity/util/ContractABIUtils.cpp | 293 +++++++++++++++--- test/libsolidity/util/ContractABIUtils.h | 37 ++- test/libsolidity/util/TestFileParser.cpp | 2 +- test/libsolidity/util/TestFunctionCall.cpp | 145 ++------- 7 files changed, 472 insertions(+), 168 deletions(-) diff --git a/test/libsolidity/semanticTests/smoke_test.sol b/test/libsolidity/semanticTests/smoke_test.sol index 46b44fbb8..07cd79d0f 100644 --- a/test/libsolidity/semanticTests/smoke_test.sol +++ b/test/libsolidity/semanticTests/smoke_test.sol @@ -1,5 +1,17 @@ +pragma experimental ABIEncoderV2; + contract C { + struct S { + uint a; + uint b; + } + struct T { + uint a; + uint b; + string s; + } uint public state = 0; + bool[2][] flags; constructor(uint _state) public payable { state = _state; } @@ -12,6 +24,9 @@ contract C { function f() payable public returns (uint) { return 2; } + function f(uint a) public returns (uint, uint) { + return (a, a); + } function g() public returns (uint, uint) { return (2, 3); } @@ -42,6 +57,36 @@ contract C { function q(uint a) public returns (uint d) { return a * 7; } + function r() public returns (bool[3] memory) { + return [true, false, true]; + } + function s() public returns (uint[2] memory, uint) { + return ([uint(123), 456], 789); + } + function t1() public returns (S memory) { + return S(23, 42); + } + function t2() public returns (T memory) { + return T(23, 42, "any"); + } + function u() public returns (T[2] memory) { + return [T(23, 42, "any"), T(555, 666, "any")]; + } + function v() public returns (bool[2][] memory) { + return flags; + } + function w1() public returns (string[1] memory) { + return ["any"]; + } + function w2() public returns (string[2] memory) { + return ["any", "any"]; + } + function w3() public returns (string[3] memory) { + return ["any", "any", "any"]; + } + function x() public returns (string[2] memory, string[3] memory) { + return (["any", "any"], ["any", "any", "any"]); + } } // ---- // constructor(), 2 ether: 3 -> @@ -50,17 +95,27 @@ contract C { // _() -> FAILURE // e(uint256): 4 // f() -> 2 +// f(uint256): 3 -> 3, 3 // f(), 1 ether -> 2 // g() -> 2, 3 +// g1() -> FAILURE // h(uint256,uint256): 1, -2 -> 3 // j(bool): true -> false // k(bytes32): 0x10 -> 0x10, 0x10 -// l(): hex"4200ef" -> 7 +// l(): hex"4200efef" -> 8 // m(bytes): 32, 32, 0x20 -> 32, 32, 0x20 // m(bytes): 32, 3, hex"AB33BB" -> 32, 3, left(0xAB33BB) // m(bytes): 32, 3, hex"AB33FF" -> 32, 3, hex"ab33ff0000000000000000000000000000000000000000000000000000000000" // n() -> 0x20, 3, "any" // o() -> 0x40, 0x80, 3, "any", 3, "any" // p() -> 0x60, 0x2a, 0xa0, 3, "any", 3, "any" -// q(uint256): 0 -> 0 // q(uint256): 99 -> 693 +// r() -> true, false, true +// s() -> 123, 456, 789 +// t1() -> 23, 42 +// t2() -> 0x20, 23, 42, 0x60, 3, "any" +// v() -> 32, 0 +// w1() -> 0x20, 0x20, 3, "any" +// w2() -> 0x20, 0x40, 0x80, 3, "any", 3, "any" +// w3() -> 0x20, 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any" +// x() -> 0x40, 0x0100, 0x40, 0x80, 3, "any", 3, "any", 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any" diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index b1ce6f8e2..dd2a5166b 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -177,7 +177,7 @@ string BytesUtils::formatHexString(bytes const& _bytes) return os.str(); } -string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const +string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) { stringstream os; @@ -226,3 +226,76 @@ string BytesUtils::formatRawBytes(bytes const& _bytes) return os.str(); } + +string BytesUtils::formatBytes( + bytes const& _bytes, + ABIType const& _abiType +) +{ + stringstream os; + + switch (_abiType.type) + { + case ABIType::UnsignedDec: + // Check if the detected type was wrong and if this could + // be signed. If an unsigned was detected in the expectations, + // but the actual result returned a signed, it would be formatted + // incorrectly. + if (*_bytes.begin() & 0x80) + os << formatSigned(_bytes); + else + os << formatUnsigned(_bytes); + break; + case ABIType::SignedDec: + os << formatSigned(_bytes); + break; + case ABIType::Boolean: + os << formatBoolean(_bytes); + break; + case ABIType::Hex: + os << formatHex(_bytes); + break; + case ABIType::HexString: + os << formatHexString(_bytes); + break; + case ABIType::String: + os << formatString(_bytes); + break; + case ABIType::Failure: + break; + case ABIType::None: + break; + } + return os.str(); +} + +string BytesUtils::formatBytesRange( + bytes _bytes, + dev::solidity::test::ParameterList const& _parameters, + bool _highlight +) +{ + stringstream os; + auto it = _bytes.begin(); + + for (auto const& parameter: _parameters) + { + bytes byteRange{it, it + static_cast(parameter.abiType.size)}; + + if (!parameter.matchesBytes(byteRange)) + AnsiColorized( + os, + _highlight, + {dev::formatting::RED_BACKGROUND} + ) << formatBytes(byteRange, parameter.abiType); + else + os << parameter.rawString; + + + it += static_cast(parameter.abiType.size); + if (¶meter != &_parameters.back()) + os << ", "; + } + return os.str(); +} + diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index fc77442cf..93ba06cd6 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -91,20 +91,33 @@ public: /// a hexString value. static std::string formatHexString(bytes const& _bytes); + /// Converts \param _bytes to a soltest-compliant and human-readable + /// string representation of a byte array which is assumed to hold + /// a string value. + static std::string formatString(bytes const& _bytes, size_t _cutOff); + + static std::string formatString(bytes const& _bytes) + { + return formatString(_bytes, _bytes.size()); + } + /// Returns a string representation of given _bytes. Adds a newline /// every 32 bytes to increase readability. /// Used to print returned bytes from function calls to the commandline. static std::string formatRawBytes(bytes const& _bytes); - /// Converts \param _bytes to a soltest-compliant and human-readable - /// string representation of a byte array which is assumed to hold - /// a string value. - std::string formatString(bytes const& _bytes, size_t _cutOff) const; + /// Formats given _bytes with type information passed in _abiType. + static std::string formatBytes(bytes const& _bytes, ABIType const& _abiType); - std::string formatString(bytes const& _bytes) const - { - return formatString(_bytes, _bytes.size()); - } + /// Formats given _bytes with type information passed in _abiTypes. + /// Prints obtained result if it does not match the expectation + /// and prints the expected result otherwise. + /// Highlights parameter only if it does not match. + static std::string formatBytesRange( + bytes _bytes, + ParameterList const& _parameters, + bool _highlight + ); }; } diff --git a/test/libsolidity/util/ContractABIUtils.cpp b/test/libsolidity/util/ContractABIUtils.cpp index ec9cbcba0..eb40f31dd 100644 --- a/test/libsolidity/util/ContractABIUtils.cpp +++ b/test/libsolidity/util/ContractABIUtils.cpp @@ -17,12 +17,18 @@ #include +#include + #include #include +#include +#include +#include #include #include +#include #include #include @@ -33,55 +39,266 @@ using namespace dev::solidity::test; using namespace std; using namespace soltest; -dev::solidity::test::ParameterList ContractABIUtils::parametersFromJson( - Json::Value const& _contractABI, - string const& _functionName -) const +namespace { - ParameterList abiParams; + +using ParameterList = dev::solidity::test::ParameterList; + +size_t arraySize(string const& _arrayType) +{ + auto leftBrack = _arrayType.find("["); + auto rightBrack = _arrayType.rfind("]"); + + soltestAssert( + leftBrack != string::npos && + rightBrack != string::npos && + rightBrack == _arrayType.size() - 1 && + leftBrack < rightBrack, + "" + ); + + string size = _arrayType.substr(leftBrack + 1, rightBrack - leftBrack - 1); + + return static_cast(stoi(size)); +} + +bool isBool(string const& _type) +{ + return _type == "bool"; +} + +bool isUint(string const& _type) +{ + return regex_match(_type, regex{"uint\\d*"}); +} + +bool isInt(string const& _type) +{ + return regex_match(_type, regex{"int\\d*"}); +} + +bool isFixedBytes(string const& _type) +{ + return regex_match(_type, regex{"bytes\\d+"}); +} + +bool isBytes(string const& _type) +{ + return regex_match(_type, regex{"\\bbytes\\b"}); +} + +bool isString(string const& _type) +{ + return _type == "string"; +} + +bool isFixedBoolArray(string const& _type) +{ + return regex_match(_type, regex{"bool\\[\\d+\\]"}); +} + +bool isFixedUintArray(string const& _type) +{ + return regex_match(_type, regex{"uint\\d*\\[\\d+\\]"}); +} + +bool isFixedIntArray(string const& _type) +{ + return regex_match(_type, regex{"int\\d*\\[\\d+\\]"}); +} + +bool isFixedStringArray(string const& _type) +{ + return regex_match(_type, regex{"string\\[\\d+\\]"}); +} + +bool isTuple(string const& _type) +{ + return _type == "tuple"; +} + +bool isFixedTupleArray(string const& _type) +{ + return regex_match(_type, regex{"tuple\\[\\d+\\]"}); +} + +string functionSignatureFromABI(Json::Value const& _functionABI) +{ + auto inputs = _functionABI["inputs"]; + string signature = {_functionABI["name"].asString() + "("}; + size_t parameterCount = 0; + + for (auto const& input: inputs) + { + parameterCount++; + signature += input["type"].asString(); + if (parameterCount < inputs.size()) + signature += ","; + } + + return signature + ")"; +} + +} + +boost::optional ContractABIUtils::parametersFromJsonOutputs( + ErrorReporter& _errorReporter, + Json::Value const& _contractABI, + string const& _functionSignature +) +{ + if (!_contractABI) + return boost::none; + for (auto const& function: _contractABI) - if (function["name"] == _functionName) + if (_functionSignature == functionSignatureFromABI(function)) + { + ParameterList inplaceTypeParams; + ParameterList dynamicTypeParams; + ParameterList finalParams; + for (auto const& output: function["outputs"]) { - auto types = fromTypeName(output["type"].asString()); - for (auto const& type: types) - abiParams.push_back(Parameter{bytes(), "", type, FormatInfo{}}); - } + string type = output["type"].asString(); - return abiParams; + ABITypes inplaceTypes; + ABITypes dynamicTypes; + + if (appendTypesFromName(output, inplaceTypes, dynamicTypes)) + { + for (auto const& type: inplaceTypes) + inplaceTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}}); + for (auto const& type: dynamicTypes) + dynamicTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}}); + } + else + { + _errorReporter.warning( + "Could not convert \"" + type + + "\" to internal ABI type representation. Falling back to default encoding." + ); + return boost::none; + } + + finalParams += inplaceTypeParams; + + inplaceTypeParams.clear(); + } + return boost::optional(finalParams + dynamicTypeParams); + } + + return boost::none; } -std::vector ContractABIUtils::fromTypeName(string const& _type) const +bool ContractABIUtils::appendTypesFromName( + Json::Value const& _functionOutput, + ABITypes& _inplaceTypes, + ABITypes& _dynamicTypes, + bool _isCompoundType +) { - static regex s_boolType{"(bool)"}; - static regex s_uintType{"(uint\\d*)"}; - static regex s_intType{"(int\\d*)"}; - static regex s_bytesType{"(bytes\\d+)"}; - static regex s_dynBytesType{"(\\bbytes\\b)"}; - static regex s_stringType{"(string)"}; + string type = _functionOutput["type"].asString(); + if (isBool(type)) + _inplaceTypes.push_back(ABIType{ABIType::Boolean}); + else if (isUint(type)) + _inplaceTypes.push_back(ABIType{ABIType::UnsignedDec}); + else if (isInt(type)) + _inplaceTypes.push_back(ABIType{ABIType::SignedDec}); + else if (isFixedBytes(type)) + _inplaceTypes.push_back(ABIType{ABIType::Hex}); + else if (isString(type)) + { + _inplaceTypes.push_back(ABIType{ABIType::Hex}); - vector abiTypes; - if (regex_match(_type, s_boolType)) - abiTypes.push_back(ABIType{ABIType::Boolean, ABIType::AlignRight, 32}); - else if (regex_match(_type, s_uintType)) - abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}); - else if (regex_match(_type, s_intType)) - abiTypes.push_back(ABIType{ABIType::SignedDec, ABIType::AlignRight, 32}); - else if (regex_match(_type, s_bytesType)) - abiTypes.push_back(ABIType{ABIType::Hex, ABIType::AlignRight, 32}); - else if (regex_match(_type, s_dynBytesType)) - { - abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}); - abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}); - abiTypes.push_back(ABIType{ABIType::HexString, ABIType::AlignLeft, 32}); + if (_isCompoundType) + _dynamicTypes.push_back(ABIType{ABIType::Hex}); + + _dynamicTypes.push_back(ABIType{ABIType::UnsignedDec}); + _dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft}); } - else if (regex_match(_type, s_stringType)) + else if (isTuple(type)) { - abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}); - abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32}); - abiTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft, 32}); + ABITypes inplaceTypes; + ABITypes dynamicTypes; + + for (auto const& component: _functionOutput["components"]) + appendTypesFromName(component, inplaceTypes, dynamicTypes, true); + _dynamicTypes += inplaceTypes + dynamicTypes; + } + else if (isFixedBoolArray(type)) + _inplaceTypes += vector(arraySize(type), ABIType{ABIType::Boolean}); + else if (isFixedUintArray(type)) + _inplaceTypes += vector(arraySize(type), ABIType{ABIType::UnsignedDec}); + else if (isFixedIntArray(type)) + _inplaceTypes += vector(arraySize(type), ABIType{ABIType::SignedDec}); + else if (isFixedStringArray(type)) + { + _inplaceTypes.push_back(ABIType{ABIType::Hex}); + + _dynamicTypes += vector(arraySize(type), ABIType{ABIType::Hex}); + + for (size_t i = 0; i < arraySize(type); i++) + { + _dynamicTypes.push_back(ABIType{ABIType::UnsignedDec}); + _dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft}); + } + } + else if (isBytes(type)) + return false; + else if (isFixedTupleArray(type)) + return false; + else + return false; + + return true; +} + +void ContractABIUtils::overwriteParameters( + ErrorReporter& _errorReporter, + dev::solidity::test::ParameterList& _targetParameters, + dev::solidity::test::ParameterList const& _sourceParameters +) +{ + boost::for_each( + _sourceParameters, + _targetParameters, + boost::bind( + [&](Parameter _a, Parameter& _b) -> void + { + if ( + _a.abiType.size != _b.abiType.size || + _a.abiType.type != _b.abiType.type + ) + { + _errorReporter.warning("Type or size of parameter(s) does not match."); + _b = _a; + } + }, + _1, + _2 + ) + ); +} + +dev::solidity::test::ParameterList ContractABIUtils::preferredParameters( + ErrorReporter& _errorReporter, + dev::solidity::test::ParameterList const& _targetParameters, + dev::solidity::test::ParameterList const& _sourceParameters, + bytes const& _bytes +) +{ + if (_targetParameters.size() != _sourceParameters.size()) + { + auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; }; + size_t encodingSize = accumulate(_targetParameters.begin(), _targetParameters.end(), size_t{0}, sizeFold); + + _errorReporter.warning( + "Encoding does not match byte range. The call returned " + + to_string(_bytes.size()) + " bytes, but " + + to_string(encodingSize) + " bytes were expected." + ); + return _sourceParameters; } else - abiTypes.push_back(ABIType{ABIType::None, ABIType::AlignRight, 0}); - return abiTypes; + return _targetParameters; } diff --git a/test/libsolidity/util/ContractABIUtils.h b/test/libsolidity/util/ContractABIUtils.h index cafd63bac..13b58699b 100644 --- a/test/libsolidity/util/ContractABIUtils.h +++ b/test/libsolidity/util/ContractABIUtils.h @@ -16,6 +16,8 @@ #include +#include + #include #include @@ -27,6 +29,8 @@ namespace solidity namespace test { +using ABITypes = std::vector; + /** * Utility class that aids conversions from contract ABI types stored in a * Json value to the internal ABIType representation of isoltest. @@ -36,10 +40,30 @@ class ContractABIUtils public: /// Parses and translates Solidity's ABI types as Json string into /// a list of internal type representations of isoltest. - ParameterList parametersFromJson( + /// Creates parameters from Contract ABI and is used to generate values for + /// auto-correction during interactive update routine. + static boost::optional parametersFromJsonOutputs( + ErrorReporter& _errorReporter, Json::Value const& _contractABI, - std::string const& _functionName - ) const; + std::string const& _functionSignature + ); + + /// Overwrites _targetParameters if ABI types or sizes given + /// by _sourceParameters do not match. + static void overwriteParameters( + ErrorReporter& _errorReporter, + ParameterList& _targetParameters, + ParameterList const& _sourceParameters + ); + + /// If parameter count does not match, take types defined _sourceParameters + /// and create a warning if so. + static ParameterList preferredParameters( + ErrorReporter& _errorReporter, + ParameterList const& _targetParameters, + ParameterList const& _sourceParameters, + bytes const& _bytes + ); private: /// Parses and translates a single type and returns a list of @@ -51,7 +75,12 @@ private: /// `string` -> [`Unsigned`, `Unsigned`, `String`] /// `bytes` -> [`Unsigned`, `Unsigned`, `HexString`] /// ... - std::vector fromTypeName(std::string const& _type) const; + static bool appendTypesFromName( + Json::Value const& _functionOutput, + ABITypes& _inplaceTypes, + ABITypes& _dynamicTypes, + bool _isCompoundType = false + ); }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 64f4bc135..bd283960e 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -309,7 +309,7 @@ Parameter TestFileParser::parseParameter() throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded."); string parsed = parseString(); - parameter.abiType = {ABIType::String, ABIType::AlignLeft, parsed.size()}; + parameter.abiType = ABIType{ABIType::String, ABIType::AlignLeft, parsed.size()}; parameter.rawString += "\"" + parsed + "\""; parameter.rawBytes = BytesUtils::applyAlign( Parameter::Alignment::Left, diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 4f401bf1f..d22c3b3f0 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -98,7 +99,6 @@ string TestFunctionCall::format( string result; if (!_renderResult) { - bytes output = m_call.expectations.rawBytes(); bool const isFailure = m_call.expectations.failure; result = isFailure ? failure : @@ -154,130 +154,49 @@ string TestFunctionCall::formatBytesParameters( ErrorReporter& _errorReporter, bytes const& _bytes, string const& _signature, - dev::solidity::test::ParameterList const& _params, + dev::solidity::test::ParameterList const& _parameters, bool _highlight ) const { using ParameterList = dev::solidity::test::ParameterList; stringstream os; - string functionName{_signature.substr(0, _signature.find("("))}; - - /// Create parameters from Contract ABI. Used to generate values for - /// auto-correction during interactive update routine. - ParameterList abiParams = ContractABIUtils().parametersFromJson(m_contractABI, functionName); - - /// If parameter count does not match, take types defined by ABI, but only - /// if the contract ABI is defined (needed for format tests where the actual - /// result does not matter). - ParameterList preferredParams; - if (m_contractABI && (_params.size() != abiParams.size())) - { - auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; }; - size_t encodingSize = std::accumulate(_params.begin(), _params.end(), size_t{0}, sizeFold); - - _errorReporter.warning( - "Encoding does not match byte range. The call returned " + - to_string(_bytes.size()) + " bytes, but " + - to_string(encodingSize) + " bytes were expected." - ); - preferredParams = abiParams; - } - else - preferredParams = _params; - - /// If output is empty, do not format anything. if (_bytes.empty()) return {}; - /// Format output bytes with the given parameters. ABI type takes precedence if: - /// - size of ABI type is greater - /// - given expected type does not match and needs to be overridden in order - /// to generate a valid output of the parameter - auto it = _bytes.begin(); - auto abiParam = abiParams.begin(); - size_t paramIndex = 1; - for (auto const& param: preferredParams) - { - size_t size = param.abiType.size; - if (m_contractABI) - size = std::max((*abiParam).abiType.size, param.abiType.size); - - long offset = static_cast(size); - auto offsetIter = it + offset; - bytes byteRange{it, offsetIter}; - - /// Override type with ABI type if given one does not match. - auto type = param.abiType; - if (m_contractABI) - if ((*abiParam).abiType.type > param.abiType.type) - { - type = (*abiParam).abiType; - _errorReporter.warning( - "Type of parameter " + to_string(paramIndex) + - " does not match the one inferred from ABI." - ); - } _errorReporter.warning("The call to \"" + _signature + "\" returned \n" + BytesUtils::formatRawBytes(_bytes)); - /// Prints obtained result if it does not match the expectation - /// and prints the expected result otherwise. - /// Highlights parameter only if it does not match. - if (byteRange != param.rawBytes) - AnsiColorized( - os, - _highlight, - {dev::formatting::RED_BACKGROUND} - ) << formatBytesRange(byteRange, type); - else - os << param.rawString; + boost::optional abiParams = ContractABIUtils::parametersFromJsonOutputs( + _errorReporter, + m_contractABI, + _signature + ); - if (abiParam != abiParams.end()) - abiParam++; - - it += offset; - paramIndex++; - if (¶m != &preferredParams.back()) - os << ", "; - } - return os.str(); -} - -string TestFunctionCall::formatBytesRange( - bytes const& _bytes, - ABIType const& _abiType -) const -{ - stringstream os; - - switch (_abiType.type) + if (abiParams) { - case ABIType::UnsignedDec: - // Check if the detected type was wrong and if this could - // be signed. If an unsigned was detected in the expectations, - // but the actual result returned a signed, it would be formatted - // incorrectly. - os << BytesUtils().formatUnsigned(_bytes); - break; - case ABIType::SignedDec: - os << BytesUtils().formatSigned(_bytes); - break; - case ABIType::Boolean: - os << BytesUtils().formatBoolean(_bytes); - break; - case ABIType::Hex: - os << BytesUtils().formatHex(_bytes); - break; - case ABIType::HexString: - os << BytesUtils().formatHexString(_bytes); - break; - case ABIType::String: - os << BytesUtils().formatString(_bytes, _abiType.size); - break; - case ABIType::Failure: - break; - case ABIType::None: - break; + boost::optional preferredParams = ContractABIUtils::preferredParameters( + _errorReporter, + _parameters, + abiParams.get(), + _bytes + ); + + if (preferredParams) + { + ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get()); + os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight); + } + } + else + { + ParameterList defaultParameters; + fill_n( + back_inserter(defaultParameters), + ceil(_bytes.size() / 32), + Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}} + ); + ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters); + os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight); } return os.str(); } @@ -289,7 +208,6 @@ string TestFunctionCall::formatRawParameters( { stringstream os; for (auto const& param: _params) - { if (!param.rawString.empty()) { if (param.format.newline) @@ -298,7 +216,6 @@ string TestFunctionCall::formatRawParameters( if (¶m != &_params.back()) os << ", "; } - } return os.str(); }