diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a70a92639..e276af521 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -108,6 +108,7 @@ detect_stray_source_files("${libsolidity_sources}" "libsolidity/") set(libsolidity_util_sources libsolidity/util/BytesUtils.cpp + libsolidity/util/BytesUtilsTests.cpp libsolidity/util/BytesUtils.h libsolidity/util/ContractABIUtils.cpp libsolidity/util/ContractABIUtils.h diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index c3044d151..1b7ca107f 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -17,18 +17,13 @@ // SPDX-License-Identifier: GPL-3.0 #include - #include #include -#include - #include -#include #include -#include #include #include #include @@ -96,6 +91,22 @@ bytes BytesUtils::convertNumber(string const& _literal) } } +bytes BytesUtils::convertFixedPoint(string const& _literal, size_t& o_fractionalDigits) +{ + size_t dotPos = _literal.find('.'); + string valueInteger = _literal.substr(0, dotPos); + string valueFraction = _literal.substr(dotPos + 1); + o_fractionalDigits = valueFraction.length(); + try + { + return util::toBigEndian(u256(valueInteger + valueFraction)); + } + catch (std::exception const&) + { + BOOST_THROW_EXCEPTION(TestParserError("Number encoding invalid.")); + } +} + bytes BytesUtils::convertHexNumber(string const& _literal) { try @@ -206,6 +217,28 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) return os.str(); } +std::string BytesUtils::formatFixedPoint(bytes const& _bytes, bool _signed, size_t _fractionalDigits) +{ + string decimal; + bool negative = false; + if (_signed) + { + s256 signedValue{u2s(fromBigEndian(_bytes))}; + negative = (signedValue < 0); + decimal = signedValue.str(); + } + else + decimal = fromBigEndian(_bytes).str(); + if (_fractionalDigits > 0) + { + size_t numDigits = decimal.length() - (negative ? 1 : 0); + if (_fractionalDigits > numDigits) + decimal.insert(negative ? 1 : 0, string(_fractionalDigits - numDigits, '0')); + decimal.insert(decimal.length() - _fractionalDigits, "."); + } + return decimal; +} + string BytesUtils::formatRawBytes( bytes const& _bytes, solidity::frontend::test::ParameterList const& _parameters, @@ -296,8 +329,11 @@ string BytesUtils::formatBytes( case ABIType::String: os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes)); break; - case ABIType::Failure: + case ABIType::UnsignedFixedPoint: + case ABIType::SignedFixedPoint: + os << formatFixedPoint(_bytes, _abiType.type == ABIType::SignedFixedPoint, _abiType.fractionalDigits); break; + case ABIType::Failure: case ABIType::None: break; } diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h index 5ec9f3271..a5a020821 100644 --- a/test/libsolidity/util/BytesUtils.h +++ b/test/libsolidity/util/BytesUtils.h @@ -54,6 +54,10 @@ public: /// representation of the decimal number literal. Throws if conversion fails. static bytes convertNumber(std::string const& _literal); + /// Tries to convert \param _literal to an unpadded `bytes` + /// representation of the decimal number literal. Throws if conversion fails. + static bytes convertFixedPoint(std::string const& _literal, size_t& o_fractionalDigits); + /// Tries to convert \param _literal to an unpadded `bytes` /// representation of the hex literal. Throws if conversion fails. static bytes convertHexNumber(std::string const& _literal); @@ -98,6 +102,10 @@ public: return formatString(_bytes, _bytes.size()); } + /// Converts \param _bytes to a soltest-compliant and human-readable + /// decimal string representation of a byte array. Format of \param _bytes is binary. + static std::string formatFixedPoint(bytes const& _bytes, bool _signed, size_t _fractionalDigits); + /// Used to print returned bytes from function calls to the commandline. /// Returns a string representation of given _bytes in ranges of 32 bytes. /// If _withSignature is true, the first 4 bytes will be formatted separately. diff --git a/test/libsolidity/util/BytesUtilsTests.cpp b/test/libsolidity/util/BytesUtilsTests.cpp new file mode 100644 index 000000000..54d53e839 --- /dev/null +++ b/test/libsolidity/util/BytesUtilsTests.cpp @@ -0,0 +1,82 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::test; + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(BytesUtilsTest) + +BOOST_AUTO_TEST_CASE(format_fixed) +{ + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{0}), true, 2), + ".00" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{1}), true, 2), + ".01" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{123}), true, 2), + "1.23" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-1}), true, 2), + "-.01" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-12}), true, 2), + "-.12" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-123}), true, 2), + "-1.23" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-1234}), true, 2), + "-12.34" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-12345}), true, 2), + "-123.45" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-123456}), true, 2), + "-1234.56" + ); + BOOST_CHECK_EQUAL( + BytesUtils::formatFixedPoint(toBigEndian(u256{-1234567}), true, 2), + "-12345.67" + ); +} + + + +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/libsolidity/util/ContractABIUtils.cpp b/test/libsolidity/util/ContractABIUtils.cpp index 2703ff441..3ffebbf10 100644 --- a/test/libsolidity/util/ContractABIUtils.cpp +++ b/test/libsolidity/util/ContractABIUtils.cpp @@ -20,6 +20,8 @@ #include +#include +#include #include #include @@ -124,6 +126,21 @@ bool isFixedTupleArray(string const& _type) return regex_match(_type, regex{"tuple\\[\\d+\\]"}); } +optional isFixedPoint(string const& type) +{ + optional fixedPointType; + smatch matches; + if (regex_match(type, matches, regex{"(u?)fixed(\\d+)x(\\d+)"})) + { + ABIType abiType(ABIType::SignedFixedPoint); + if (matches[1].str() == "u") + abiType.type = ABIType::UnsignedFixedPoint; + abiType.fractionalDigits = static_cast(std::stoi(matches[3].str())); + fixedPointType = abiType; + } + return fixedPointType; +} + string functionSignatureFromABI(Json::Value const& _functionABI) { auto inputs = _functionABI["inputs"]; @@ -245,6 +262,8 @@ bool ContractABIUtils::appendTypesFromName( _dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft}); } } + else if (optional fixedPointType = isFixedPoint(type)) + _inplaceTypes.push_back(*fixedPointType); else if (isBytes(type)) return false; else if (isFixedTupleArray(type)) @@ -270,7 +289,8 @@ void ContractABIUtils::overwriteParameters( { if ( _a.abiType.size != _b.abiType.size || - _a.abiType.type != _b.abiType.type + _a.abiType.type != _b.abiType.type || + _a.abiType.fractionalDigits != _b.abiType.fractionalDigits ) { _errorReporter.warning("Type or size of parameter(s) does not match."); diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index f1b284135..bd954f042 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -107,7 +107,9 @@ struct ABIType SignedDec, Hex, HexString, - String + String, + UnsignedFixedPoint, + SignedFixedPoint }; enum Align { @@ -125,6 +127,9 @@ struct ABIType Type type = ABIType::None; Align align = ABIType::AlignRight; size_t size = 32; + + size_t fractionalDigits = 0; + bool alignDeclared = false; }; diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 169a5a5cf..65a7e9c0f 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -407,11 +407,17 @@ Parameter TestFileParser::parseParameter() if (isSigned) parsed = "-" + parsed; - parameter.rawBytes = BytesUtils::applyAlign( - parameter.alignment, - parameter.abiType, - BytesUtils::convertNumber(parsed) - ); + if (parsed.find('.') == string::npos) + parameter.rawBytes = BytesUtils::applyAlign( + parameter.alignment, + parameter.abiType, + BytesUtils::convertNumber(parsed) + ); + else + { + parameter.abiType.type = isSigned ? ABIType::SignedFixedPoint : ABIType::UnsignedFixedPoint; + parameter.rawBytes = BytesUtils::convertFixedPoint(parsed, parameter.abiType.fractionalDigits); + } } else if (accept(Token::Failure, true)) { @@ -667,7 +673,7 @@ string TestFileParser::Scanner::scanDecimalNumber() { string number; number += current(); - while (langutil::isDecimalDigit(peek())) + while (langutil::isDecimalDigit(peek()) || '.' == peek()) { advance(); number += current(); diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 65a7bd69d..0f28f3b99 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index c54a440ac..8b4ed5019 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -16,10 +16,12 @@ #include #include +#include #include #include #include +#include #include @@ -109,12 +111,6 @@ private: bool failure = false ) const; - /// Formats a given _bytes applying the _abiType. - std::string formatBytesRange( - bytes const& _bytes, - ABIType const& _abiType - ) const; - /// Formats a FAILURE plus additional parameters, if e.g. a revert message was returned. std::string formatFailure( ErrorReporter& _errorReporter,