Restructures alignment and bytes utils in isoltest.

This commit is contained in:
Erik Kundt 2019-07-04 15:23:47 +02:00
parent de34f7712d
commit d06be2c53f
6 changed files with 115 additions and 94 deletions

View File

@ -163,3 +163,36 @@ string BytesUtils::formatString(bytes const& _bytes) 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));
}
}

View File

@ -77,6 +77,22 @@ public:
/// string representation of a byte array which is assumed to hold
/// a string value.
std::string formatString(bytes const& _bytes) const;
/// 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;
};
}

View File

@ -135,6 +135,13 @@ struct FormatInfo
*/
struct Parameter
{
enum Alignment
{
Left,
Right,
None,
};
/// ABI encoded / decoded `bytes` of values.
/// These `bytes` are used to pass values to function calls
/// and also to store expected return vales. These are
@ -151,6 +158,8 @@ struct Parameter
/// 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;
};
using ParameterList = std::vector<Parameter>;

View File

@ -38,45 +38,6 @@ using namespace dev::solidity::test;
using namespace std;
using namespace soltest;
namespace
{
enum class DeclaredAlignment
{
Left,
Right,
None,
};
inline bytes alignLeft(bytes _bytes)
{
return std::move(_bytes) + bytes(32 - _bytes.size(), 0);
}
inline bytes alignRight(bytes _bytes)
{
return bytes(32 - _bytes.size(), 0) + std::move(_bytes);
}
inline bytes applyAlign(DeclaredAlignment _alignment, ABIType& _abiType, bytes _converted)
{
if (_alignment != DeclaredAlignment::None)
_abiType.alignDeclared = true;
switch (_alignment)
{
case DeclaredAlignment::Left:
_abiType.align = ABIType::AlignLeft;
return alignLeft(std::move(_converted));
case DeclaredAlignment::Right:
_abiType.align = ABIType::AlignRight;
return alignRight(std::move(_converted));
default:
_abiType.align = ABIType::AlignRight;
return alignRight(std::move(_converted));
}
}
}
char TestFileParser::Scanner::peek() const noexcept
{
if (std::distance(m_char, m_line.end()) < 2)
@ -248,112 +209,124 @@ Parameter TestFileParser::parseParameter()
if (accept(Token::Newline, true))
parameter.format.newline = true;
auto literal = parseABITypeLiteral();
parameter.rawBytes = get<0>(literal);
parameter.abiType = get<1>(literal);
parameter.rawString = get<2>(literal);
return parameter;
}
tuple<bytes, ABIType, string> TestFileParser::parseABITypeLiteral()
{
ABIType abiType{ABIType::None, ABIType::AlignNone, 0};
DeclaredAlignment alignment{DeclaredAlignment::None};
bytes result{toBigEndian(u256{0})};
string rawString;
bool isSigned = false;
if (accept(Token::Left, true))
{
rawString += formatToken(Token::Left);
parameter.rawString += formatToken(Token::Left);
expect(Token::LParen);
rawString += formatToken(Token::LParen);
alignment = DeclaredAlignment::Left;
parameter.rawString += formatToken(Token::LParen);
parameter.alignment = Parameter::Alignment::Left;
}
if (accept(Token::Right, true))
{
rawString += formatToken(Token::Right);
parameter.rawString += formatToken(Token::Right);
expect(Token::LParen);
rawString += formatToken(Token::LParen);
alignment = DeclaredAlignment::Right;
parameter.rawString += formatToken(Token::LParen);
parameter.alignment = Parameter::Alignment::Right;
}
try
{
if (accept(Token::Sub, true))
{
rawString += formatToken(Token::Sub);
parameter.rawString += formatToken(Token::Sub);
isSigned = true;
}
if (accept(Token::Boolean))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid boolean literal.");
abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32};
parameter.abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32};
string parsed = parseBoolean();
rawString += parsed;
result = applyAlign(alignment, abiType, BytesUtils().convertBoolean(parsed));
parameter.rawString += parsed;
parameter.rawBytes = BytesUtils().applyAlign(
parameter.alignment,
parameter.abiType,
BytesUtils().convertBoolean(parsed)
);
}
else if (accept(Token::HexNumber))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid hex number literal.");
abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32};
parameter.abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32};
string parsed = parseHexNumber();
rawString += parsed;
result = applyAlign(alignment, abiType, BytesUtils().convertHexNumber(parsed));
parameter.rawString += parsed;
parameter.rawBytes = BytesUtils().applyAlign(
parameter.alignment,
parameter.abiType,
BytesUtils().convertHexNumber(parsed)
);
}
else if (accept(Token::Hex, true))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid hex string literal.");
if (alignment != DeclaredAlignment::None)
if (parameter.alignment != Parameter::Alignment::None)
throw Error(Error::Type::ParserError, "Hex string literals cannot be aligned or padded.");
string parsed = parseString();
rawString += "hex\"" + parsed + "\"";
result = BytesUtils().convertHexNumber(parsed);
abiType = ABIType{ABIType::HexString, ABIType::AlignNone, result.size()};
parameter.rawString += "hex\"" + parsed + "\"";
parameter.rawBytes = BytesUtils().convertHexNumber(parsed);
parameter.abiType = ABIType{
ABIType::HexString, ABIType::AlignNone, parameter.rawBytes.size()
};
}
else if (accept(Token::String))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid string literal.");
if (alignment != DeclaredAlignment::None)
if (parameter.alignment != Parameter::Alignment::None)
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
abiType = ABIType{ABIType::String, ABIType::AlignLeft, 32};
parameter.abiType = ABIType{ABIType::String, ABIType::AlignLeft, 32};
string parsed = parseString();
rawString += "\"" + parsed + "\"";
result = applyAlign(DeclaredAlignment::Left, abiType, BytesUtils().convertString(parsed));
parameter.rawString += "\"" + parsed + "\"";
parameter.rawBytes = BytesUtils().applyAlign(
Parameter::Alignment::Left,
parameter.abiType,
BytesUtils().convertString(parsed)
);
}
else if (accept(Token::Number))
{
auto type = isSigned ? ABIType::SignedDec : ABIType::UnsignedDec;
abiType = ABIType{type, ABIType::AlignRight, 32};
parameter.abiType = ABIType{type, ABIType::AlignRight, 32};
string parsed = parseDecimalNumber();
rawString += parsed;
parameter.rawString += parsed;
if (isSigned)
parsed = "-" + parsed;
result = applyAlign(alignment, abiType, BytesUtils().convertNumber(parsed));
parameter.rawBytes = BytesUtils().applyAlign(
parameter.alignment,
parameter.abiType,
BytesUtils().convertNumber(parsed)
);
}
else if (accept(Token::Failure, true))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid failure literal.");
abiType = ABIType{ABIType::Failure, ABIType::AlignRight, 0};
result = bytes{};
parameter.abiType = ABIType{ABIType::Failure, ABIType::AlignRight, 0};
parameter.rawBytes = bytes{};
}
if (alignment != DeclaredAlignment::None)
if (parameter.alignment != Parameter::Alignment::None)
{
expect(Token::RParen);
rawString += formatToken(Token::RParen);
parameter.rawString += formatToken(Token::RParen);
}
return make_tuple(result, abiType, rawString);
}
catch (std::exception const&)
{
throw Error(Error::Type::ParserError, "Literal encoding invalid.");
}
return parameter;
}
string TestFileParser::parseIdentifierOrTuple()

View File

@ -146,9 +146,7 @@ private:
/// appends it to the internal `bytes` buffer of the parameter. It can also
/// store newlines found in the source, that are needed to
/// format input and output of the interactive update.
Parameter parseParameter();
/// Parses and converts the current literal to its byte representation and
/// Parses and converts the current literal to its byte representation and
/// preserves the chosen ABI type, as well as a raw, unformatted string representation
/// of this literal.
/// Based on the type information retrieved, the driver of this parser may format arguments,
@ -157,7 +155,7 @@ private:
/// Returns invalid ABI type for empty literal. This is needed in order
/// to detect empty expectations. Throws a ParserError if data is encoded incorrectly or
/// if data type is not supported.
std::tuple<bytes, ABIType, std::string> parseABITypeLiteral();
Parameter parseParameter();
/// Recursively parses an identifier or a tuple definition that contains identifiers
/// and / or parentheses like `((uint, uint), (uint, (uint, uint)), uint)`.

View File

@ -199,14 +199,6 @@ private:
std::string const& _linePrefix = ""
) const;
/// 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 choosePreferredParameters(
ParameterList const& _parsedParamters,
ParameterList const& _abiParameters
) const;
/// Compares raw expectations (which are converted to a byte representation before),
/// and also the expected transaction status of the function call to the actual test results.
bool matchesExpectation() const;