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));
|
||||
|
||||
success &= test.call().expectedSideEffects == test.call().actualSideEffects;
|
||||
success &= test.checkFixedPointTypes();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
try
|
||||
@ -206,6 +225,19 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
|
||||
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(
|
||||
bytes const& _bytes,
|
||||
solidity::frontend::test::ParameterList const& _parameters,
|
||||
@ -296,6 +328,9 @@ string BytesUtils::formatBytes(
|
||||
case ABIType::String:
|
||||
os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes));
|
||||
break;
|
||||
case ABIType::FixedPointType:
|
||||
os << formatFixedPoint(_bytes, _abiType.M, _abiType.N);
|
||||
break;
|
||||
case ABIType::Failure:
|
||||
break;
|
||||
case ABIType::None:
|
||||
|
@ -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 *_fixedPointN);
|
||||
|
||||
/// 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,11 @@ public:
|
||||
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.
|
||||
/// Returns a string representation of given _bytes in ranges of 32 bytes.
|
||||
/// 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+\\]"});
|
||||
}
|
||||
|
||||
bool isFixedPointType(string const& _type)
|
||||
{
|
||||
return regex_match(_type, regex{"fixed.*"});
|
||||
}
|
||||
|
||||
bool isTuple(string const& _type)
|
||||
{
|
||||
return _type == "tuple";
|
||||
@ -245,6 +250,23 @@ bool ContractABIUtils::appendTypesFromName(
|
||||
_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))
|
||||
return false;
|
||||
else if (isFixedTupleArray(type))
|
||||
|
@ -107,7 +107,8 @@ struct ABIType
|
||||
SignedDec,
|
||||
Hex,
|
||||
HexString,
|
||||
String
|
||||
String,
|
||||
FixedPointType
|
||||
};
|
||||
enum Align
|
||||
{
|
||||
@ -125,6 +126,8 @@ struct ABIType
|
||||
Type type = ABIType::None;
|
||||
Align align = ABIType::AlignRight;
|
||||
size_t size = 32;
|
||||
size_t M = 0;
|
||||
size_t N = 0;
|
||||
bool alignDeclared = false;
|
||||
};
|
||||
|
||||
|
@ -407,11 +407,18 @@ 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 = 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))
|
||||
{
|
||||
@ -667,7 +674,7 @@ string TestFileParser::Scanner::scanDecimalNumber()
|
||||
{
|
||||
string number;
|
||||
number += current();
|
||||
while (langutil::isDecimalDigit(peek()))
|
||||
while (langutil::isDecimalDigit(peek()) || '.' == peek())
|
||||
{
|
||||
advance();
|
||||
number += current();
|
||||
|
@ -18,8 +18,11 @@
|
||||
#include <test/libsolidity/util/ContractABIUtils.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/split.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@ -266,6 +269,7 @@ string TestFunctionCall::formatBytesParameters(
|
||||
if (preferredParams)
|
||||
{
|
||||
ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.value(), abiParams.value());
|
||||
preferredParams.value().begin()->abiType = abiParams.value().begin()->abiType;
|
||||
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();
|
||||
}
|
||||
|
||||
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 <libsolutil/AnsiColorized.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include "ContractABIUtils.h"
|
||||
#include <iosfwd>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
@ -95,6 +97,8 @@ public:
|
||||
void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); }
|
||||
void setSideEffects(std::vector<std::string> _sideEffects) { m_call.actualSideEffects = _sideEffects; }
|
||||
|
||||
bool checkFixedPointTypes();
|
||||
|
||||
private:
|
||||
/// 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
|
||||
@ -109,12 +113,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,
|
||||
|
Loading…
Reference in New Issue
Block a user