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 <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -141,22 +142,28 @@ string BytesUtils::formatHexString(bytes const& _bytes) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatString(bytes const& _bytes) const
|
string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
os << "\"";
|
os << "\"";
|
||||||
bool expectZeros = false;
|
for (size_t i = 0; i < min(_cutOff, _bytes.size()); ++i)
|
||||||
for (auto const& v: _bytes)
|
|
||||||
{
|
{
|
||||||
if (expectZeros && v != 0)
|
auto const v = _bytes[i];
|
||||||
return {};
|
switch (v)
|
||||||
if (v == 0) expectZeros = true;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (!isprint(v) || v == '"')
|
case '\0':
|
||||||
return {};
|
os << "\\0";
|
||||||
os << v;
|
break;
|
||||||
|
case '\n':
|
||||||
|
os << "\\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isprint(v))
|
||||||
|
os << v;
|
||||||
|
else
|
||||||
|
os << "\\x" << setw(2) << setfill('0') << hex << v;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os << "\"";
|
os << "\"";
|
||||||
|
@ -76,7 +76,12 @@ public:
|
|||||||
/// Converts \param _bytes to a soltest-compliant and human-readable
|
/// Converts \param _bytes to a soltest-compliant and human-readable
|
||||||
/// string representation of a byte array which is assumed to hold
|
/// string representation of a byte array which is assumed to hold
|
||||||
/// a string value.
|
/// 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
|
/// Left-aligns and pads given _bytes and returns a new
|
||||||
/// bytes array.
|
/// bytes array.
|
||||||
|
@ -300,8 +300,8 @@ Parameter TestFileParser::parseParameter()
|
|||||||
if (parameter.alignment != Parameter::Alignment::None)
|
if (parameter.alignment != Parameter::Alignment::None)
|
||||||
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
|
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
|
||||||
|
|
||||||
parameter.abiType = ABIType{ABIType::String, ABIType::AlignLeft, 32};
|
|
||||||
string parsed = parseString();
|
string parsed = parseString();
|
||||||
|
parameter.abiType = {ABIType::String, ABIType::AlignLeft, parsed.size()};
|
||||||
parameter.rawString += "\"" + parsed + "\"";
|
parameter.rawString += "\"" + parsed + "\"";
|
||||||
parameter.rawBytes = BytesUtils().applyAlign(
|
parameter.rawBytes = BytesUtils().applyAlign(
|
||||||
Parameter::Alignment::Left,
|
Parameter::Alignment::Left,
|
||||||
@ -589,8 +589,70 @@ string TestFileParser::Scanner::scanString()
|
|||||||
|
|
||||||
while (current() != '\"')
|
while (current() != '\"')
|
||||||
{
|
{
|
||||||
str += current();
|
if (current() == '\\')
|
||||||
advance();
|
{
|
||||||
|
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;
|
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 scanDecimalNumber();
|
||||||
std::string scanHexNumber();
|
std::string scanHexNumber();
|
||||||
std::string scanString();
|
std::string scanString();
|
||||||
|
char scanHexPart();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using TokenDesc = std::pair<Token, std::string>;
|
using TokenDesc = std::pair<Token, std::string>;
|
||||||
|
|
||||||
/// Advances current position in the input stream.
|
/// Advances current position in the input stream.
|
||||||
void advance()
|
void advance(unsigned n = 1)
|
||||||
{
|
{
|
||||||
solAssert(m_char != m_line.end(), "Cannot advance beyond end.");
|
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.
|
/// 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)
|
BOOST_AUTO_TEST_CASE(call_arguments_hex_string)
|
||||||
{
|
{
|
||||||
char const* source = R"(
|
char const* source = R"(
|
||||||
|
@ -261,7 +261,7 @@ string TestFunctionCall::formatBytesRange(
|
|||||||
os << BytesUtils().formatHexString(_bytes);
|
os << BytesUtils().formatHexString(_bytes);
|
||||||
break;
|
break;
|
||||||
case ABIType::String:
|
case ABIType::String:
|
||||||
os << BytesUtils().formatString(_bytes);
|
os << BytesUtils().formatString(_bytes, _abiType.size);
|
||||||
break;
|
break;
|
||||||
case ABIType::Failure:
|
case ABIType::Failure:
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user