mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[isoltest] Add support for fixed point types.
This commit is contained in:
parent
fac1c81ab4
commit
088ebb4078
@ -461,6 +461,7 @@ TestCase::TestResult SemanticTest::runTest(
|
|||||||
test.setSideEffects(move(effects));
|
test.setSideEffects(move(effects));
|
||||||
|
|
||||||
success &= test.call().expectedSideEffects == test.call().actualSideEffects;
|
success &= test.call().expectedSideEffects == test.call().actualSideEffects;
|
||||||
|
success &= test.checkFixedPointTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_testCaseWantsYulRun && _isYulRun)
|
if (!m_testCaseWantsYulRun && _isYulRun)
|
||||||
|
@ -96,6 +96,25 @@ bytes BytesUtils::convertNumber(string const& _literal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes BytesUtils::convertFixedPoint(string const& _literal, size_t *_fixedPointN)
|
||||||
|
{
|
||||||
|
solAssert(_fixedPointN != nullptr, "");
|
||||||
|
solAssert(_literal.find('.') != string::npos, "");
|
||||||
|
string v_integer{_literal};
|
||||||
|
string v_fraction;
|
||||||
|
v_integer = _literal.substr(0, _literal.find('.'));
|
||||||
|
v_fraction = {_literal.substr(_literal.find('.') + 1)};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
*_fixedPointN = v_fraction.length();
|
||||||
|
return util::fromHex(util::toHex(u256{v_integer + v_fraction}));
|
||||||
|
}
|
||||||
|
catch (std::exception const&)
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(TestParserError("Number encoding invalid."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bytes BytesUtils::convertHexNumber(string const& _literal)
|
bytes BytesUtils::convertHexNumber(string const& _literal)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -206,6 +225,19 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BytesUtils::formatFixedPoint(bytes const& _bytes, size_t _fixedPointM, size_t _fixedPointN)
|
||||||
|
{
|
||||||
|
solAssert(_fixedPointM != 0, "");
|
||||||
|
u256 max{boost::multiprecision::pow(u256{2}, static_cast<unsigned>(_fixedPointM))};
|
||||||
|
solAssert(u256{_bytes} >= max, "");
|
||||||
|
stringstream os;
|
||||||
|
os << u256{_bytes};
|
||||||
|
string value{os.str()};
|
||||||
|
if (_fixedPointN > 0)
|
||||||
|
value.insert(value.length() - _fixedPointN, ".");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
string BytesUtils::formatRawBytes(
|
string BytesUtils::formatRawBytes(
|
||||||
bytes const& _bytes,
|
bytes const& _bytes,
|
||||||
solidity::frontend::test::ParameterList const& _parameters,
|
solidity::frontend::test::ParameterList const& _parameters,
|
||||||
@ -296,6 +328,9 @@ string BytesUtils::formatBytes(
|
|||||||
case ABIType::String:
|
case ABIType::String:
|
||||||
os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes));
|
os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes));
|
||||||
break;
|
break;
|
||||||
|
case ABIType::FixedPointType:
|
||||||
|
os << formatFixedPoint(_bytes, _abiType.M, _abiType.N);
|
||||||
|
break;
|
||||||
case ABIType::Failure:
|
case ABIType::Failure:
|
||||||
break;
|
break;
|
||||||
case ABIType::None:
|
case ABIType::None:
|
||||||
|
@ -54,6 +54,10 @@ public:
|
|||||||
/// representation of the decimal number literal. Throws if conversion fails.
|
/// representation of the decimal number literal. Throws if conversion fails.
|
||||||
static 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 decimal number literal. Throws if conversion fails.
|
||||||
|
static bytes convertFixedPoint(std::string const& _literal, size_t *_fixedPointN);
|
||||||
|
|
||||||
/// Tries to convert \param _literal to an unpadded `bytes`
|
/// Tries to convert \param _literal to an unpadded `bytes`
|
||||||
/// representation of the hex literal. Throws if conversion fails.
|
/// representation of the hex literal. Throws if conversion fails.
|
||||||
static bytes convertHexNumber(std::string const& _literal);
|
static bytes convertHexNumber(std::string const& _literal);
|
||||||
@ -98,6 +102,11 @@ public:
|
|||||||
return formatString(_bytes, _bytes.size());
|
return formatString(_bytes, _bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts \param _bytes to a soltest-compliant and human-readable
|
||||||
|
/// string representation of a byte array which is assumed to hold
|
||||||
|
/// a hexString value.
|
||||||
|
static std::string formatFixedPoint(bytes const& _bytes, size_t _fixedPointM, size_t _fixedPointN);
|
||||||
|
|
||||||
/// Used to print returned bytes from function calls to the commandline.
|
/// Used to print returned bytes from function calls to the commandline.
|
||||||
/// Returns a string representation of given _bytes in ranges of 32 bytes.
|
/// Returns a string representation of given _bytes in ranges of 32 bytes.
|
||||||
/// If _withSignature is true, the first 4 bytes will be formatted separately.
|
/// If _withSignature is true, the first 4 bytes will be formatted separately.
|
||||||
|
@ -114,6 +114,11 @@ bool isFixedStringArray(string const& _type)
|
|||||||
return regex_match(_type, regex{"string\\[\\d+\\]"});
|
return regex_match(_type, regex{"string\\[\\d+\\]"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFixedPointType(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"fixed.*"});
|
||||||
|
}
|
||||||
|
|
||||||
bool isTuple(string const& _type)
|
bool isTuple(string const& _type)
|
||||||
{
|
{
|
||||||
return _type == "tuple";
|
return _type == "tuple";
|
||||||
@ -245,6 +250,23 @@ bool ContractABIUtils::appendTypesFromName(
|
|||||||
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
|
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (isFixedPointType(type))
|
||||||
|
{
|
||||||
|
vector<string> fixedTypeParameter;
|
||||||
|
solAssert(boost::starts_with(type, "fixed"), "");
|
||||||
|
string fixedTypeParameterString{type.substr(5)};
|
||||||
|
solAssert(fixedTypeParameterString.find('x') != string::npos, "");
|
||||||
|
boost::split(fixedTypeParameter, fixedTypeParameterString, boost::is_any_of("x"));
|
||||||
|
solAssert(fixedTypeParameter.size() == 2, "");
|
||||||
|
ABIType abiType{ABIType::FixedPointType};
|
||||||
|
abiType.M = static_cast<unsigned>(std::stoi(fixedTypeParameter[0]));
|
||||||
|
abiType.N = static_cast<unsigned>(std::stoi(fixedTypeParameter[1]));
|
||||||
|
solAssert(
|
||||||
|
8 <= abiType.M && abiType.M <= 256 && abiType.M % 8 == 0 && abiType.N <= 80,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
_inplaceTypes.push_back(abiType);
|
||||||
|
}
|
||||||
else if (isBytes(type))
|
else if (isBytes(type))
|
||||||
return false;
|
return false;
|
||||||
else if (isFixedTupleArray(type))
|
else if (isFixedTupleArray(type))
|
||||||
|
@ -107,7 +107,8 @@ struct ABIType
|
|||||||
SignedDec,
|
SignedDec,
|
||||||
Hex,
|
Hex,
|
||||||
HexString,
|
HexString,
|
||||||
String
|
String,
|
||||||
|
FixedPointType
|
||||||
};
|
};
|
||||||
enum Align
|
enum Align
|
||||||
{
|
{
|
||||||
@ -125,6 +126,8 @@ struct ABIType
|
|||||||
Type type = ABIType::None;
|
Type type = ABIType::None;
|
||||||
Align align = ABIType::AlignRight;
|
Align align = ABIType::AlignRight;
|
||||||
size_t size = 32;
|
size_t size = 32;
|
||||||
|
size_t M = 0;
|
||||||
|
size_t N = 0;
|
||||||
bool alignDeclared = false;
|
bool alignDeclared = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -407,11 +407,18 @@ Parameter TestFileParser::parseParameter()
|
|||||||
if (isSigned)
|
if (isSigned)
|
||||||
parsed = "-" + parsed;
|
parsed = "-" + parsed;
|
||||||
|
|
||||||
|
if (parsed.find('.') == string::npos)
|
||||||
parameter.rawBytes = BytesUtils::applyAlign(
|
parameter.rawBytes = BytesUtils::applyAlign(
|
||||||
parameter.alignment,
|
parameter.alignment,
|
||||||
parameter.abiType,
|
parameter.abiType,
|
||||||
BytesUtils::convertNumber(parsed)
|
BytesUtils::convertNumber(parsed)
|
||||||
);
|
);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parameter.abiType.type = ABIType::FixedPointType;
|
||||||
|
// note that parameter.abiType.M & parameter.abiType.N are not set here.
|
||||||
|
parameter.rawBytes = BytesUtils::convertFixedPoint(parsed, ¶meter.abiType.N);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (accept(Token::Failure, true))
|
else if (accept(Token::Failure, true))
|
||||||
{
|
{
|
||||||
@ -667,7 +674,7 @@ string TestFileParser::Scanner::scanDecimalNumber()
|
|||||||
{
|
{
|
||||||
string number;
|
string number;
|
||||||
number += current();
|
number += current();
|
||||||
while (langutil::isDecimalDigit(peek()))
|
while (langutil::isDecimalDigit(peek()) || '.' == peek())
|
||||||
{
|
{
|
||||||
advance();
|
advance();
|
||||||
number += current();
|
number += current();
|
||||||
|
@ -18,8 +18,11 @@
|
|||||||
#include <test/libsolidity/util/ContractABIUtils.h>
|
#include <test/libsolidity/util/ContractABIUtils.h>
|
||||||
|
|
||||||
#include <libsolutil/AnsiColorized.h>
|
#include <libsolutil/AnsiColorized.h>
|
||||||
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -266,6 +269,7 @@ string TestFunctionCall::formatBytesParameters(
|
|||||||
if (preferredParams)
|
if (preferredParams)
|
||||||
{
|
{
|
||||||
ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.value(), abiParams.value());
|
ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.value(), abiParams.value());
|
||||||
|
preferredParams.value().begin()->abiType = abiParams.value().begin()->abiType;
|
||||||
os << BytesUtils::formatBytesRange(_bytes, preferredParams.value(), _highlight);
|
os << BytesUtils::formatBytesRange(_bytes, preferredParams.value(), _highlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,3 +377,66 @@ bool TestFunctionCall::matchesExpectation() const
|
|||||||
{
|
{
|
||||||
return m_failure == m_call.expectations.failure && m_rawBytes == m_call.expectations.rawBytes();
|
return m_failure == m_call.expectations.failure && m_rawBytes == m_call.expectations.rawBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TestFunctionCall::checkFixedPointTypes()
|
||||||
|
{
|
||||||
|
// if constructor, we do not have any type information, because of missing signature.
|
||||||
|
if (m_call.signature.find("constructor()") != string::npos)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// check fixed point type used in return type.
|
||||||
|
ErrorReporter errorReporter;
|
||||||
|
std::optional<ParameterList> returnType
|
||||||
|
= ContractABIUtils::parametersFromJsonOutputs(errorReporter, m_contractABI, m_call.signature);
|
||||||
|
if (returnType.has_value() && !returnType.value().empty() && returnType.value().begin()->abiType.type == ABIType::FixedPointType)
|
||||||
|
if (m_call.expectations.result.begin()->abiType.N != returnType.value().begin()->abiType.N)
|
||||||
|
{
|
||||||
|
// if not matching, we reset the raw bytes of result, so that isoltest will generate the correct expectation.
|
||||||
|
m_call.expectations.result.begin()->rawBytes = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check fixed point types used in arguments.
|
||||||
|
// - extract argument types from signature
|
||||||
|
size_t ob = m_call.signature.find('(') + 1;
|
||||||
|
size_t cb = m_call.signature.find(')');
|
||||||
|
string signature = m_call.signature.substr(ob, cb - ob);
|
||||||
|
vector<string> inputTypes;
|
||||||
|
if (signature.find(',') != string::npos)
|
||||||
|
{
|
||||||
|
if (!signature.empty())
|
||||||
|
boost::split(inputTypes, signature, boost::is_any_of(","));
|
||||||
|
}
|
||||||
|
else if (!signature.empty())
|
||||||
|
inputTypes.emplace_back(signature);
|
||||||
|
|
||||||
|
if (m_call.arguments.parameters.size() != inputTypes.size())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// - iterate through arguments and check that fractional digits match with the corresponding argument type.
|
||||||
|
for (size_t i = 0; i < inputTypes.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& type = inputTypes[i];
|
||||||
|
auto& parameter = m_call.arguments.parameters[i];
|
||||||
|
if ((parameter.abiType.type == ABIType::FixedPointType || parameter.abiType.type == ABIType::UnsignedDec) && (type.find("fixed") != string::npos))
|
||||||
|
{
|
||||||
|
size_t fixedPointN;
|
||||||
|
vector<string> fixedTypeParameter;
|
||||||
|
string fixedTypeParameterString{type.substr(5)};
|
||||||
|
solAssert(fixedTypeParameterString.find('x') != string::npos, "");
|
||||||
|
boost::split(fixedTypeParameter, fixedTypeParameterString, boost::is_any_of("x"));
|
||||||
|
solAssert(fixedTypeParameter.size() == 2, "");
|
||||||
|
fixedPointN = static_cast<unsigned>(std::stoi(fixedTypeParameter[1]));
|
||||||
|
string what;
|
||||||
|
if (parameter.rawString.find('.') != string::npos)
|
||||||
|
{
|
||||||
|
what = parameter.rawString.substr(parameter.rawString.find('.') + 1);
|
||||||
|
solAssert(fixedPointN == what.length(), type + " not compatible with '" + parameter.rawString + "'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solAssert(fixedPointN == 0, type + " not compatible with '" + parameter.rawString + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -20,9 +20,11 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <libsolutil/AnsiColorized.h>
|
#include <libsolutil/AnsiColorized.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include "ContractABIUtils.h"
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -95,6 +97,8 @@ public:
|
|||||||
void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); }
|
void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); }
|
||||||
void setSideEffects(std::vector<std::string> _sideEffects) { m_call.actualSideEffects = _sideEffects; }
|
void setSideEffects(std::vector<std::string> _sideEffects) { m_call.actualSideEffects = _sideEffects; }
|
||||||
|
|
||||||
|
bool checkFixedPointTypes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Tries to format the given `bytes`, applying the detected ABI types that have be set for each parameter.
|
/// Tries to format the given `bytes`, applying the detected ABI types that have be set for each parameter.
|
||||||
/// Throws if there's a mismatch in the size of `bytes` and the desired formats that are specified
|
/// Throws if there's a mismatch in the size of `bytes` and the desired formats that are specified
|
||||||
@ -109,12 +113,6 @@ private:
|
|||||||
bool failure = false
|
bool failure = false
|
||||||
) const;
|
) 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.
|
/// Formats a FAILURE plus additional parameters, if e.g. a revert message was returned.
|
||||||
std::string formatFailure(
|
std::string formatFailure(
|
||||||
ErrorReporter& _errorReporter,
|
ErrorReporter& _errorReporter,
|
||||||
|
Loading…
Reference in New Issue
Block a user