Merge pull request #6120 from ethereum/soltest-hex-strings

[soltest] Add support for hex string literals
This commit is contained in:
chriseth 2019-03-13 14:12:32 +01:00 committed by GitHub
commit 2896d6176b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 4 deletions

View File

@ -70,6 +70,7 @@ Bugfixes:
Build System:
* Soltest: Add support for left-aligned, padded hex literals.
* Soltest: Add support for left-aligned, unpadded hex string literals.
* Soltest: Add support for right-aligned, padded boolean literals.
### 0.5.4 (2019-02-12)

View File

@ -17,6 +17,9 @@ contract C {
function k(bytes32 b) public returns (bytes32) {
return b;
}
function s() public returns (uint256) {
return msg.data.length;
}
}
// ----
// f() -> 2
@ -25,3 +28,4 @@ contract C {
// i() -> FAILURE
// j(bool): true -> false
// k(bytes32): 0x31 -> 0x31
// s(): hex"4200ef" -> 7

View File

@ -284,6 +284,17 @@ tuple<bytes, ABIType, string> TestFileParser::parseABITypeLiteral()
rawString += parsed;
result = applyAlign(alignment, abiType, convertHexNumber(parsed));
}
else if (accept(Token::Hex, true))
{
if (isSigned)
throw Error(Error::Type::ParserError, "Invalid hex string literal.");
if (alignment != DeclaredAlignment::None)
throw Error(Error::Type::ParserError, "Hex string literals cannot be aligned or padded.");
string parsed = parseHexNumber();
rawString += parsed;
result = convertHexString(parsed);
abiType = ABIType{ABIType::HexString, ABIType::AlignNone, result.size()};
}
else if (accept(Token::Number))
{
auto type = isSigned ? ABIType::SignedDec : ABIType::UnsignedDec;
@ -310,7 +321,7 @@ tuple<bytes, ABIType, string> TestFileParser::parseABITypeLiteral()
}
catch (std::exception const&)
{
throw Error(Error::Type::ParserError, "Number encoding invalid.");
throw Error(Error::Type::ParserError, "Literal encoding invalid.");
}
}
@ -425,6 +436,21 @@ bytes TestFileParser::convertHexNumber(string const& _literal)
}
}
bytes TestFileParser::convertHexString(string const& _literal)
{
try
{
if (_literal.size() % 2)
throw Error(Error::Type::ParserError, "Hex string encoding invalid.");
else
return fromHex(_literal);
}
catch (std::exception const&)
{
throw Error(Error::Type::ParserError, "Hex string encoding invalid.");
}
}
void TestFileParser::Scanner::readStream(istream& _stream)
{
std::string line;
@ -435,6 +461,8 @@ void TestFileParser::Scanner::readStream(istream& _stream)
void TestFileParser::Scanner::scanNextToken()
{
using namespace langutil;
// Make code coverage happy.
assert(formatToken(Token::NUM_TOKENS) == "");
@ -444,6 +472,7 @@ void TestFileParser::Scanner::scanNextToken()
if (_literal == "ether") return TokenDesc{Token::Ether, _literal};
if (_literal == "left") return TokenDesc{Token::Left, _literal};
if (_literal == "right") return TokenDesc{Token::Right, _literal};
if (_literal == "hex") return TokenDesc{Token::Hex, _literal};
if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal};
return TokenDesc{Token::Identifier, _literal};
};
@ -495,13 +524,18 @@ void TestFileParser::Scanner::scanNextToken()
case ']':
token = selectToken(Token::RBrack);
break;
case '\"':
advance();
token = selectToken(Token::HexNumber, scanHexNumber());
advance();
break;
default:
if (langutil::isIdentifierStart(current()))
if (isIdentifierStart(current()))
{
TokenDesc detectedToken = detectKeyword(scanIdentifierOrKeyword());
token = selectToken(detectedToken.first, detectedToken.second);
}
else if (langutil::isDecimalDigit(current()))
else if (isDecimalDigit(current()))
{
if (current() == '0' && peek() == 'x')
{
@ -512,7 +546,7 @@ void TestFileParser::Scanner::scanNextToken()
else
token = selectToken(Token::Number, scanDecimalNumber());
}
else if (langutil::isWhiteSpace(current()))
else if (isWhiteSpace(current()))
token = selectToken(Token::Whitespace);
else if (isEndOfLine())
token = selectToken(Token::EOS);

View File

@ -60,6 +60,7 @@ namespace test
T(Identifier, "identifier", 0) \
/* type keywords */ \
K(Ether, "ether", 0) \
K(Hex, "hex", 0) \
K(Boolean, "boolean", 0) \
/* special keywords */ \
K(Left, "left", 0) \
@ -108,6 +109,7 @@ struct ABIType
SignedDec,
Boolean,
Hex,
HexString,
Failure,
None
};
@ -385,6 +387,10 @@ private:
/// representation of the hex literal. Throws if conversion fails.
bytes convertHexNumber(std::string const& _literal);
/// Tries to convert \param _literal to left-aligned, unpadded `bytes`
/// representation of the hex string literal. Throws if conversion fails.
bytes convertHexString(std::string const& _literal);
/// A scanner instance
Scanner m_scanner;
};

View File

@ -310,6 +310,40 @@ BOOST_AUTO_TEST_CASE(call_arguments_bool)
);
}
BOOST_AUTO_TEST_CASE(call_arguments_hex_string)
{
char const* source = R"(
// f(bytes): hex"4200ef" -> hex"ab0023"
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"f(bytes)",
false,
fromHex("4200ef"),
fromHex("ab0023")
);
}
BOOST_AUTO_TEST_CASE(call_arguments_hex_string_lowercase)
{
char const* source = R"(
// f(bytes): hex"4200ef" -> hex"23ef00"
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"f(bytes)",
false,
fromHex("4200EF"),
fromHex("23EF00")
);
}
BOOST_AUTO_TEST_CASE(call_arguments_tuple)
{
char const* source = R"(
@ -564,6 +598,22 @@ BOOST_AUTO_TEST_CASE(call_builtin_right_decimal)
);
}
BOOST_AUTO_TEST_CASE(call_arguments_hex_string_left_align)
{
char const* source = R"(
// f(bytes): left(hex"4200ef") ->
)";
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
}
BOOST_AUTO_TEST_CASE(call_arguments_hex_string_right_align)
{
char const* source = R"(
// f(bytes): right(hex"4200ef") ->
)";
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
}
BOOST_AUTO_TEST_CASE(call_newline_invalid)
{
char const* source = R"(

View File

@ -164,6 +164,9 @@ string TestFunctionCall::formatBytesParameters(bytes const& _bytes, dev::solidit
case ABIType::Hex:
resultStream << toHex(byteRange, HexPrefix::Add);
break;
case ABIType::HexString:
resultStream << "hex\"" << toHex(byteRange) << "\"";
break;
case ABIType::Failure:
break;
case ABIType::None:

View File

@ -138,6 +138,19 @@ BOOST_AUTO_TEST_CASE(format_hex_singleline)
BOOST_REQUIRE_EQUAL(test.format("", true), "// f(bytes32): 0x31 -> 0x3200000000000000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(format_hex_string_singleline)
{
bytes expectedBytes = fromHex("4200ef");
ABIType abiType{ABIType::HexString, ABIType::AlignLeft, 3};
Parameter param{expectedBytes, "hex\"4200ef\"", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(string)", 0, arguments, expectations};
TestFunctionCall test{call};
BOOST_REQUIRE_EQUAL(test.format(), "// f(string): hex\"4200ef\" -> hex\"4200ef\"");
}
BOOST_AUTO_TEST_CASE(format_bool_true_singleline)
{
bytes expectedBytes = toBigEndian(u256{true});