mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6036 from ethereum/soltest-hexliterals
[soltest] Add support for left-aligned hex literals
This commit is contained in:
commit
773a3ff880
@ -22,7 +22,7 @@ Bugfixes:
|
|||||||
|
|
||||||
|
|
||||||
Build System:
|
Build System:
|
||||||
|
* Soltest: Add support for left-aligned, padded hex literals.
|
||||||
|
|
||||||
### 0.5.4 (2019-02-12)
|
### 0.5.4 (2019-02-12)
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ contract C {
|
|||||||
function h() public payable returns (uint) {
|
function h() public payable returns (uint) {
|
||||||
return f();
|
return f();
|
||||||
}
|
}
|
||||||
|
function x(bytes32 b) public returns (bytes32) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f() -> 1
|
// f() -> 1
|
||||||
@ -15,3 +18,4 @@ contract C {
|
|||||||
// h(), 1 ether -> 1
|
// h(), 1 ether -> 1
|
||||||
// j() -> FAILURE
|
// j() -> FAILURE
|
||||||
// i() # Does not exist. # -> FAILURE # Reverts. #
|
// i() # Does not exist. # -> FAILURE # Reverts. #
|
||||||
|
// x(bytes32): 0x31 -> 0x31
|
||||||
|
@ -137,7 +137,7 @@ string TestFileParser::parseFunctionSignature()
|
|||||||
|
|
||||||
u256 TestFileParser::parseFunctionCallValue()
|
u256 TestFileParser::parseFunctionCallValue()
|
||||||
{
|
{
|
||||||
u256 value = convertNumber(parseNumber());
|
u256 value = convertNumber(parseDecimalNumber());
|
||||||
expect(Token::Ether);
|
expect(Token::Ether);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -203,16 +203,23 @@ tuple<bytes, ABIType, string> TestFileParser::parseABITypeLiteral()
|
|||||||
abiType = ABIType{ABIType::SignedDec, ABIType::AlignRight, 32};
|
abiType = ABIType{ABIType::SignedDec, ABIType::AlignRight, 32};
|
||||||
expect(Token::Sub);
|
expect(Token::Sub);
|
||||||
rawString += formatToken(Token::Sub);
|
rawString += formatToken(Token::Sub);
|
||||||
string parsed = parseNumber();
|
string parsed = parseDecimalNumber();
|
||||||
rawString += parsed;
|
rawString += parsed;
|
||||||
number = convertNumber(parsed) * -1;
|
number = convertNumber(parsed) * -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (accept(Token::Number))
|
if (accept(Token::HexNumber))
|
||||||
|
{
|
||||||
|
abiType = ABIType{ABIType::Hex, ABIType::AlignLeft, 32};
|
||||||
|
string parsed = parseHexNumber();
|
||||||
|
rawString += parsed;
|
||||||
|
return make_tuple(convertHexNumber(parsed), abiType, rawString);
|
||||||
|
}
|
||||||
|
else if (accept(Token::Number))
|
||||||
{
|
{
|
||||||
abiType = ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32};
|
abiType = ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32};
|
||||||
string parsed = parseNumber();
|
string parsed = parseDecimalNumber();
|
||||||
rawString += parsed;
|
rawString += parsed;
|
||||||
number = convertNumber(parsed);
|
number = convertNumber(parsed);
|
||||||
}
|
}
|
||||||
@ -263,16 +270,24 @@ string TestFileParser::parseComment()
|
|||||||
return string{};
|
return string{};
|
||||||
}
|
}
|
||||||
|
|
||||||
string TestFileParser::parseNumber()
|
string TestFileParser::parseDecimalNumber()
|
||||||
{
|
{
|
||||||
string literal = m_scanner.currentLiteral();
|
string literal = m_scanner.currentLiteral();
|
||||||
expect(Token::Number);
|
expect(Token::Number);
|
||||||
return literal;
|
return literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string TestFileParser::parseHexNumber()
|
||||||
|
{
|
||||||
|
string literal = m_scanner.currentLiteral();
|
||||||
|
expect(Token::HexNumber);
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
u256 TestFileParser::convertNumber(string const& _literal)
|
u256 TestFileParser::convertNumber(string const& _literal)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
return u256{_literal};
|
return u256{_literal};
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (std::exception const&)
|
||||||
@ -281,6 +296,26 @@ u256 TestFileParser::convertNumber(string const& _literal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes TestFileParser::convertHexNumber(string const& _literal)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_literal.size() % 2)
|
||||||
|
{
|
||||||
|
throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes result = fromHex(_literal);
|
||||||
|
return result + bytes(32 - result.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception const&)
|
||||||
|
{
|
||||||
|
throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestFileParser::Scanner::readStream(istream& _stream)
|
void TestFileParser::Scanner::readStream(istream& _stream)
|
||||||
{
|
{
|
||||||
std::string line;
|
std::string line;
|
||||||
@ -348,7 +383,16 @@ void TestFileParser::Scanner::scanNextToken()
|
|||||||
token = selectToken(detectedToken.first, detectedToken.second);
|
token = selectToken(detectedToken.first, detectedToken.second);
|
||||||
}
|
}
|
||||||
else if (langutil::isDecimalDigit(current()))
|
else if (langutil::isDecimalDigit(current()))
|
||||||
token = selectToken(Token::Number, scanNumber());
|
{
|
||||||
|
if (current() == '0' && peek() == 'x')
|
||||||
|
{
|
||||||
|
advance();
|
||||||
|
advance();
|
||||||
|
token = selectToken(Token::HexNumber, "0x" + scanHexNumber());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
token = selectToken(Token::Number, scanDecimalNumber());
|
||||||
|
}
|
||||||
else if (langutil::isWhiteSpace(current()))
|
else if (langutil::isWhiteSpace(current()))
|
||||||
token = selectToken(Token::Whitespace);
|
token = selectToken(Token::Whitespace);
|
||||||
else if (isEndOfLine())
|
else if (isEndOfLine())
|
||||||
@ -385,7 +429,7 @@ string TestFileParser::Scanner::scanIdentifierOrKeyword()
|
|||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
string TestFileParser::Scanner::scanNumber()
|
string TestFileParser::Scanner::scanDecimalNumber()
|
||||||
{
|
{
|
||||||
string number;
|
string number;
|
||||||
number += current();
|
number += current();
|
||||||
@ -396,3 +440,15 @@ string TestFileParser::Scanner::scanNumber()
|
|||||||
}
|
}
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string TestFileParser::Scanner::scanHexNumber()
|
||||||
|
{
|
||||||
|
string number;
|
||||||
|
number += current();
|
||||||
|
while (langutil::isHexDigit(peek()))
|
||||||
|
{
|
||||||
|
advance();
|
||||||
|
number += current();
|
||||||
|
}
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
@ -56,6 +56,7 @@ namespace test
|
|||||||
/* Literals & identifier */ \
|
/* Literals & identifier */ \
|
||||||
T(Comment, "#", 0) \
|
T(Comment, "#", 0) \
|
||||||
T(Number, "number", 0) \
|
T(Number, "number", 0) \
|
||||||
|
T(HexNumber, "hex_number", 0) \
|
||||||
T(Identifier, "identifier", 0) \
|
T(Identifier, "identifier", 0) \
|
||||||
/* type keywords */ \
|
/* type keywords */ \
|
||||||
K(Ether, "ether", 0) \
|
K(Ether, "ether", 0) \
|
||||||
@ -102,10 +103,10 @@ struct ABIType
|
|||||||
{
|
{
|
||||||
UnsignedDec,
|
UnsignedDec,
|
||||||
SignedDec,
|
SignedDec,
|
||||||
|
Hex,
|
||||||
Failure,
|
Failure,
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Align
|
enum Align
|
||||||
{
|
{
|
||||||
AlignLeft,
|
AlignLeft,
|
||||||
@ -113,7 +114,7 @@ struct ABIType
|
|||||||
};
|
};
|
||||||
|
|
||||||
Type type = ABIType::None;
|
Type type = ABIType::None;
|
||||||
Align align = Align::AlignRight;
|
Align align = ABIType::AlignRight;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -290,7 +291,8 @@ private:
|
|||||||
|
|
||||||
std::string scanComment();
|
std::string scanComment();
|
||||||
std::string scanIdentifierOrKeyword();
|
std::string scanIdentifierOrKeyword();
|
||||||
std::string scanNumber();
|
std::string scanDecimalNumber();
|
||||||
|
std::string scanHexNumber();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using TokenDesc = std::pair<Token, std::string>;
|
using TokenDesc = std::pair<Token, std::string>;
|
||||||
@ -357,13 +359,21 @@ private:
|
|||||||
/// # A nice comment. #
|
/// # A nice comment. #
|
||||||
std::string parseComment();
|
std::string parseComment();
|
||||||
|
|
||||||
/// Parses the current number literal.
|
/// Parses the current decimal number literal.
|
||||||
std::string parseNumber();
|
std::string parseDecimalNumber();
|
||||||
|
|
||||||
/// Tries to convert \param _literal to `uint256` and throws if
|
/// Parses the current hex number literal.
|
||||||
/// conversion fails.
|
std::string parseHexNumber();
|
||||||
|
|
||||||
|
/// Tries to convert \param _literal to right-aligned, padded `u256`
|
||||||
|
/// representation of the decimal number literal.
|
||||||
|
/// Throws if conversion fails.
|
||||||
u256 convertNumber(std::string const& _literal);
|
u256 convertNumber(std::string const& _literal);
|
||||||
|
|
||||||
|
/// Tries to convert \param _literal to left-aligned, padded `bytes`
|
||||||
|
/// representation of the hex literal. Throws if conversion fails.
|
||||||
|
bytes convertHexNumber(std::string const& _literal);
|
||||||
|
|
||||||
/// A scanner instance
|
/// A scanner instance
|
||||||
Scanner m_scanner;
|
Scanner m_scanner;
|
||||||
};
|
};
|
||||||
|
@ -305,6 +305,38 @@ BOOST_AUTO_TEST_CASE(call_arguments_tuple)
|
|||||||
testFunctionCall(calls.at(1), Mode::SingleLine, "f((uint8),uint8)", false);
|
testFunctionCall(calls.at(1), Mode::SingleLine, "f((uint8),uint8)", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_arguments_left_aligned)
|
||||||
|
{
|
||||||
|
char const* source = R"(
|
||||||
|
// f(bytes32, bytes32): 0x6161, 0x420000EF -> 1
|
||||||
|
// g(bytes32, bytes32): 0x0616, 0x0042EF00 -> 1
|
||||||
|
)";
|
||||||
|
auto const calls = parse(source);
|
||||||
|
BOOST_REQUIRE_EQUAL(calls.size(), 2);
|
||||||
|
testFunctionCall(
|
||||||
|
calls.at(0),
|
||||||
|
Mode::SingleLine,
|
||||||
|
"f(bytes32,bytes32)",
|
||||||
|
false,
|
||||||
|
fmt::encodeArgs(
|
||||||
|
u256("0x6161000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
u256("0x420000EF00000000000000000000000000000000000000000000000000000000")
|
||||||
|
),
|
||||||
|
fmt::encodeArgs(1)
|
||||||
|
);
|
||||||
|
testFunctionCall(
|
||||||
|
calls.at(1),
|
||||||
|
Mode::SingleLine,
|
||||||
|
"g(bytes32,bytes32)",
|
||||||
|
false,
|
||||||
|
fmt::encodeArgs(
|
||||||
|
u256("0x0616000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
u256("0x0042EF0000000000000000000000000000000000000000000000000000000000")
|
||||||
|
),
|
||||||
|
fmt::encodeArgs(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(call_arguments_tuple_of_tuples)
|
BOOST_AUTO_TEST_CASE(call_arguments_tuple_of_tuples)
|
||||||
{
|
{
|
||||||
char const* source = R"(
|
char const* source = R"(
|
||||||
@ -546,6 +578,14 @@ BOOST_AUTO_TEST_CASE(call_ether_type_invalid)
|
|||||||
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_hex_number_invalid)
|
||||||
|
{
|
||||||
|
char const* source = R"(
|
||||||
|
// f(bytes32, bytes32): 0x616, 0x042 -> 1
|
||||||
|
)";
|
||||||
|
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(call_arguments_colon)
|
BOOST_AUTO_TEST_CASE(call_arguments_colon)
|
||||||
{
|
{
|
||||||
char const* source = R"(
|
char const* source = R"(
|
||||||
|
@ -144,6 +144,13 @@ string TestFunctionCall::formatBytesParameters(bytes const& _bytes, ParameterLis
|
|||||||
else
|
else
|
||||||
resultStream << fromBigEndian<u256>(byteRange);
|
resultStream << fromBigEndian<u256>(byteRange);
|
||||||
break;
|
break;
|
||||||
|
case ABIType::Hex:
|
||||||
|
soltestAssert(param.abiType.align == ABIType::AlignLeft, "Hex numbers must be left-aligned.");
|
||||||
|
byteRange.erase(
|
||||||
|
std::remove(byteRange.begin(), byteRange.end(), 0), byteRange.end()
|
||||||
|
);
|
||||||
|
resultStream << toHex(byteRange, HexPrefix::Add);
|
||||||
|
break;
|
||||||
case ABIType::Failure:
|
case ABIType::Failure:
|
||||||
break;
|
break;
|
||||||
case ABIType::None:
|
case ABIType::None:
|
||||||
|
@ -75,6 +75,34 @@ BOOST_AUTO_TEST_CASE(format_signed_singleline)
|
|||||||
BOOST_REQUIRE_EQUAL(test.format(), "// f(int8): -1 -> -1");
|
BOOST_REQUIRE_EQUAL(test.format(), "// f(int8): -1 -> -1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(format_hex_singleline)
|
||||||
|
{
|
||||||
|
bytes result = fromHex("0x31");
|
||||||
|
bytes expectedBytes = result + bytes(32 - result.size(), 0);
|
||||||
|
ABIType abiType{ABIType::Hex, ABIType::AlignLeft, 32};
|
||||||
|
Parameter param{expectedBytes, "0x31", abiType, FormatInfo{}};
|
||||||
|
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
|
||||||
|
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
|
||||||
|
FunctionCall call{"f(bytes32)", 0, arguments, expectations};
|
||||||
|
TestFunctionCall test{call};
|
||||||
|
|
||||||
|
BOOST_REQUIRE_EQUAL(test.format(), "// f(bytes32): 0x31 -> 0x31");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(format_hex_right_align)
|
||||||
|
{
|
||||||
|
bytes result = fromHex("0x31");
|
||||||
|
bytes expectedBytes = result + bytes(32 - result.size(), 0);
|
||||||
|
ABIType abiType{ABIType::Hex, ABIType::AlignRight, 32};
|
||||||
|
Parameter param{expectedBytes, "0x31", abiType, FormatInfo{}};
|
||||||
|
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
|
||||||
|
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
|
||||||
|
FunctionCall call{"f(bytes32)", 0, arguments, expectations};
|
||||||
|
TestFunctionCall test{call};
|
||||||
|
|
||||||
|
BOOST_REQUIRE_THROW(test.format(), runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(format_empty_byte_range)
|
BOOST_AUTO_TEST_CASE(format_empty_byte_range)
|
||||||
{
|
{
|
||||||
bytes expectedBytes;
|
bytes expectedBytes;
|
||||||
|
Loading…
Reference in New Issue
Block a user