mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7080 from ethereum/isoltest-string-parse-print-fix
[isoltest] Fixes parsing and printing strings with *basic* escape sequences in it.
This commit is contained in:
commit
a640ca6fe7
@ -22,6 +22,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
@ -141,22 +142,28 @@ string BytesUtils::formatHexString(bytes const& _bytes) const
|
||||
return os.str();
|
||||
}
|
||||
|
||||
string BytesUtils::formatString(bytes const& _bytes) const
|
||||
string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const
|
||||
{
|
||||
stringstream os;
|
||||
|
||||
os << "\"";
|
||||
bool expectZeros = false;
|
||||
for (auto const& v: _bytes)
|
||||
for (size_t i = 0; i < min(_cutOff, _bytes.size()); ++i)
|
||||
{
|
||||
if (expectZeros && v != 0)
|
||||
return {};
|
||||
if (v == 0) expectZeros = true;
|
||||
else
|
||||
auto const v = _bytes[i];
|
||||
switch (v)
|
||||
{
|
||||
if (!isprint(v) || v == '"')
|
||||
return {};
|
||||
case '\0':
|
||||
os << "\\0";
|
||||
break;
|
||||
case '\n':
|
||||
os << "\\n";
|
||||
break;
|
||||
default:
|
||||
if (isprint(v))
|
||||
os << v;
|
||||
else
|
||||
os << "\\x" << setw(2) << setfill('0') << hex << v;
|
||||
|
||||
}
|
||||
}
|
||||
os << "\"";
|
||||
|
@ -76,7 +76,12 @@ public:
|
||||
/// Converts \param _bytes to a soltest-compliant and human-readable
|
||||
/// string representation of a byte array which is assumed to hold
|
||||
/// a string value.
|
||||
std::string formatString(bytes const& _bytes) const;
|
||||
std::string formatString(bytes const& _bytes, size_t _cutOff) const;
|
||||
|
||||
std::string formatString(bytes const& _bytes) const
|
||||
{
|
||||
return formatString(_bytes, _bytes.size());
|
||||
}
|
||||
|
||||
/// Left-aligns and pads given _bytes and returns a new
|
||||
/// bytes array.
|
||||
|
@ -300,8 +300,8 @@ Parameter TestFileParser::parseParameter()
|
||||
if (parameter.alignment != Parameter::Alignment::None)
|
||||
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
|
||||
|
||||
parameter.abiType = ABIType{ABIType::String, ABIType::AlignLeft, 32};
|
||||
string parsed = parseString();
|
||||
parameter.abiType = {ABIType::String, ABIType::AlignLeft, parsed.size()};
|
||||
parameter.rawString += "\"" + parsed + "\"";
|
||||
parameter.rawBytes = BytesUtils().applyAlign(
|
||||
Parameter::Alignment::Left,
|
||||
@ -588,9 +588,71 @@ string TestFileParser::Scanner::scanString()
|
||||
advance();
|
||||
|
||||
while (current() != '\"')
|
||||
{
|
||||
if (current() == '\\')
|
||||
{
|
||||
advance();
|
||||
switch (current())
|
||||
{
|
||||
case '\\':
|
||||
str += current();
|
||||
advance();
|
||||
break;
|
||||
case 'n':
|
||||
str += '\n';
|
||||
advance();
|
||||
break;
|
||||
case 'r':
|
||||
str += '\r';
|
||||
advance();
|
||||
break;
|
||||
case 't':
|
||||
str += '\t';
|
||||
advance();
|
||||
break;
|
||||
case '0':
|
||||
str += '\0';
|
||||
advance();
|
||||
break;
|
||||
case 'x':
|
||||
str += scanHexPart();
|
||||
break;
|
||||
default:
|
||||
throw Error(Error::Type::ParserError, "Invalid or escape sequence found in string literal.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str += current();
|
||||
advance();
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
char TestFileParser::Scanner::scanHexPart()
|
||||
{
|
||||
advance(); // skip 'x'
|
||||
|
||||
char value{};
|
||||
if (isdigit(current()))
|
||||
value = current() - '0';
|
||||
else if (tolower(current()) >= 'a' && tolower(current()) <= 'f')
|
||||
value = tolower(current()) - 'a' + 10;
|
||||
else
|
||||
throw Error(Error::Type::ParserError, "\\x used with no following hex digits.");
|
||||
|
||||
advance();
|
||||
if (current() == '"')
|
||||
return value;
|
||||
|
||||
value <<= 4;
|
||||
if (isdigit(current()))
|
||||
value |= current() - '0';
|
||||
else if (tolower(current()) >= 'a' && tolower(current()) <= 'f')
|
||||
value |= tolower(current()) - 'a' + 10;
|
||||
|
||||
advance();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -89,15 +89,16 @@ private:
|
||||
std::string scanDecimalNumber();
|
||||
std::string scanHexNumber();
|
||||
std::string scanString();
|
||||
char scanHexPart();
|
||||
|
||||
private:
|
||||
using TokenDesc = std::pair<Token, std::string>;
|
||||
|
||||
/// Advances current position in the input stream.
|
||||
void advance()
|
||||
void advance(unsigned n = 1)
|
||||
{
|
||||
solAssert(m_char != m_line.end(), "Cannot advance beyond end.");
|
||||
++m_char;
|
||||
m_char = std::next(m_char, n);
|
||||
}
|
||||
|
||||
/// Returns the current character or '\0' if at end of input.
|
||||
|
@ -310,6 +310,50 @@ BOOST_AUTO_TEST_CASE(call_arguments_bool)
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scanner_hex_values)
|
||||
{
|
||||
char const* source = R"(
|
||||
// f(uint256): "\x20\x00\xFf" ->
|
||||
)";
|
||||
auto const calls = parse(source);
|
||||
BOOST_REQUIRE_EQUAL(calls.size(), 1);
|
||||
testFunctionCall(calls.at(0), Mode::SingleLine, "f(uint256)", false, fmt::encodeArgs(string("\x20\x00\xff", 3)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scanner_hex_values_invalid1)
|
||||
{
|
||||
char const* source = R"(
|
||||
// f(uint256): "\x" ->
|
||||
)";
|
||||
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scanner_hex_values_invalid2)
|
||||
{
|
||||
char const* source = R"(
|
||||
// f(uint256): "\x1" ->
|
||||
)";
|
||||
auto const calls = parse(source);
|
||||
BOOST_REQUIRE_EQUAL(calls.size(), 1);
|
||||
testFunctionCall(calls.at(0), Mode::SingleLine, "f(uint256)", false, fmt::encodeArgs(string("\x1", 1)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scanner_hex_values_invalid3)
|
||||
{
|
||||
char const* source = R"(
|
||||
// f(uint256): "\xZ" ->
|
||||
)";
|
||||
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scanner_hex_values_invalid4)
|
||||
{
|
||||
char const* source = R"(
|
||||
// f(uint256): "\xZZ" ->
|
||||
)";
|
||||
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(call_arguments_hex_string)
|
||||
{
|
||||
char const* source = R"(
|
||||
|
@ -261,7 +261,7 @@ string TestFunctionCall::formatBytesRange(
|
||||
os << BytesUtils().formatHexString(_bytes);
|
||||
break;
|
||||
case ABIType::String:
|
||||
os << BytesUtils().formatString(_bytes);
|
||||
os << BytesUtils().formatString(_bytes, _abiType.size);
|
||||
break;
|
||||
case ABIType::Failure:
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user