mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7052 from ethereum/isoltest-update-improvements
[isoltest] Update improvements (support for missing types)
This commit is contained in:
commit
42290f912b
@ -35,14 +35,6 @@ namespace solidity
|
|||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
|
|
||||||
#define soltestAssert(CONDITION, DESCRIPTION) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
if (!(CONDITION)) \
|
|
||||||
BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \
|
|
||||||
} \
|
|
||||||
while (false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common superclass of anything that can be run via isoltest.
|
* Common superclass of anything that can be run via isoltest.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint a;
|
||||||
|
uint b;
|
||||||
|
}
|
||||||
|
struct T {
|
||||||
|
uint a;
|
||||||
|
uint b;
|
||||||
|
string s;
|
||||||
|
}
|
||||||
uint public state = 0;
|
uint public state = 0;
|
||||||
|
bool[2][] flags;
|
||||||
constructor(uint _state) public payable {
|
constructor(uint _state) public payable {
|
||||||
state = _state;
|
state = _state;
|
||||||
}
|
}
|
||||||
@ -12,6 +24,9 @@ contract C {
|
|||||||
function f() payable public returns (uint) {
|
function f() payable public returns (uint) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
function f(uint a) public returns (uint, uint) {
|
||||||
|
return (a, a);
|
||||||
|
}
|
||||||
function g() public returns (uint, uint) {
|
function g() public returns (uint, uint) {
|
||||||
return (2, 3);
|
return (2, 3);
|
||||||
}
|
}
|
||||||
@ -42,6 +57,36 @@ contract C {
|
|||||||
function q(uint a) public returns (uint d) {
|
function q(uint a) public returns (uint d) {
|
||||||
return a * 7;
|
return a * 7;
|
||||||
}
|
}
|
||||||
|
function r() public returns (bool[3] memory) {
|
||||||
|
return [true, false, true];
|
||||||
|
}
|
||||||
|
function s() public returns (uint[2] memory, uint) {
|
||||||
|
return ([uint(123), 456], 789);
|
||||||
|
}
|
||||||
|
function t1() public returns (S memory) {
|
||||||
|
return S(23, 42);
|
||||||
|
}
|
||||||
|
function t2() public returns (T memory) {
|
||||||
|
return T(23, 42, "any");
|
||||||
|
}
|
||||||
|
function u() public returns (T[2] memory) {
|
||||||
|
return [T(23, 42, "any"), T(555, 666, "any")];
|
||||||
|
}
|
||||||
|
function v() public returns (bool[2][] memory) {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
function w1() public returns (string[1] memory) {
|
||||||
|
return ["any"];
|
||||||
|
}
|
||||||
|
function w2() public returns (string[2] memory) {
|
||||||
|
return ["any", "any"];
|
||||||
|
}
|
||||||
|
function w3() public returns (string[3] memory) {
|
||||||
|
return ["any", "any", "any"];
|
||||||
|
}
|
||||||
|
function x() public returns (string[2] memory, string[3] memory) {
|
||||||
|
return (["any", "any"], ["any", "any", "any"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// constructor(), 2 ether: 3 ->
|
// constructor(), 2 ether: 3 ->
|
||||||
@ -50,17 +95,27 @@ contract C {
|
|||||||
// _() -> FAILURE
|
// _() -> FAILURE
|
||||||
// e(uint256): 4
|
// e(uint256): 4
|
||||||
// f() -> 2
|
// f() -> 2
|
||||||
|
// f(uint256): 3 -> 3, 3
|
||||||
// f(), 1 ether -> 2
|
// f(), 1 ether -> 2
|
||||||
// g() -> 2, 3
|
// g() -> 2, 3
|
||||||
|
// g1() -> FAILURE
|
||||||
// h(uint256,uint256): 1, -2 -> 3
|
// h(uint256,uint256): 1, -2 -> 3
|
||||||
// j(bool): true -> false
|
// j(bool): true -> false
|
||||||
// k(bytes32): 0x10 -> 0x10, 0x10
|
// k(bytes32): 0x10 -> 0x10, 0x10
|
||||||
// l(): hex"4200ef" -> 7
|
// l(): hex"4200efef" -> 8
|
||||||
// m(bytes): 32, 32, 0x20 -> 32, 32, 0x20
|
// m(bytes): 32, 32, 0x20 -> 32, 32, 0x20
|
||||||
// m(bytes): 32, 3, hex"AB33BB" -> 32, 3, left(0xAB33BB)
|
// m(bytes): 32, 3, hex"AB33BB" -> 32, 3, left(0xAB33BB)
|
||||||
// m(bytes): 32, 3, hex"AB33FF" -> 32, 3, hex"ab33ff0000000000000000000000000000000000000000000000000000000000"
|
// m(bytes): 32, 3, hex"AB33FF" -> 32, 3, hex"ab33ff0000000000000000000000000000000000000000000000000000000000"
|
||||||
// n() -> 0x20, 3, "any"
|
// n() -> 0x20, 3, "any"
|
||||||
// o() -> 0x40, 0x80, 3, "any", 3, "any"
|
// o() -> 0x40, 0x80, 3, "any", 3, "any"
|
||||||
// p() -> 0x60, 0x2a, 0xa0, 3, "any", 3, "any"
|
// p() -> 0x60, 0x2a, 0xa0, 3, "any", 3, "any"
|
||||||
// q(uint256): 0 -> 0
|
|
||||||
// q(uint256): 99 -> 693
|
// q(uint256): 99 -> 693
|
||||||
|
// r() -> true, false, true
|
||||||
|
// s() -> 123, 456, 789
|
||||||
|
// t1() -> 23, 42
|
||||||
|
// t2() -> 0x20, 23, 42, 0x60, 3, "any"
|
||||||
|
// v() -> 32, 0
|
||||||
|
// w1() -> 0x20, 0x20, 3, "any"
|
||||||
|
// w2() -> 0x20, 0x40, 0x80, 3, "any", 3, "any"
|
||||||
|
// w3() -> 0x20, 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any"
|
||||||
|
// x() -> 0x40, 0x0100, 0x40, 0x80, 3, "any", 3, "any", 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any"
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <test/libsolidity/util/BytesUtils.h>
|
#include <test/libsolidity/util/BytesUtils.h>
|
||||||
|
|
||||||
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
|
|
||||||
#include <liblangutil/Common.h>
|
#include <liblangutil/Common.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
@ -34,6 +36,40 @@ using namespace dev::solidity::test;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace soltest;
|
using namespace soltest;
|
||||||
|
|
||||||
|
bytes BytesUtils::alignLeft(bytes _bytes)
|
||||||
|
{
|
||||||
|
soltestAssert(_bytes.size() <= 32, "");
|
||||||
|
size_t size = _bytes.size();
|
||||||
|
return std::move(_bytes) + bytes(32 - size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BytesUtils::alignRight(bytes _bytes)
|
||||||
|
{
|
||||||
|
soltestAssert(_bytes.size() <= 32, "");
|
||||||
|
return bytes(32 - _bytes.size(), 0) + std::move(_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes BytesUtils::applyAlign(
|
||||||
|
Parameter::Alignment _alignment,
|
||||||
|
ABIType& _abiType,
|
||||||
|
bytes _bytes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
default:
|
||||||
|
_abiType.align = ABIType::AlignRight;
|
||||||
|
return alignRight(std::move(_bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bytes BytesUtils::convertBoolean(string const& _literal)
|
bytes BytesUtils::convertBoolean(string const& _literal)
|
||||||
{
|
{
|
||||||
if (_literal == "true")
|
if (_literal == "true")
|
||||||
@ -83,10 +119,21 @@ bytes BytesUtils::convertString(string const& _literal)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatUnsigned(bytes const& _bytes) const
|
string BytesUtils::formatUnsigned(bytes const& _bytes)
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
|
soltestAssert(!_bytes.empty() && _bytes.size() <= 32, "");
|
||||||
|
|
||||||
|
return fromBigEndian<u256>(_bytes).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string BytesUtils::formatSigned(bytes const& _bytes)
|
||||||
|
{
|
||||||
|
stringstream os;
|
||||||
|
|
||||||
|
soltestAssert(!_bytes.empty() && _bytes.size() <= 32, "");
|
||||||
|
|
||||||
if (*_bytes.begin() & 0x80)
|
if (*_bytes.begin() & 0x80)
|
||||||
os << u2s(fromBigEndian<u256>(_bytes));
|
os << u2s(fromBigEndian<u256>(_bytes));
|
||||||
else
|
else
|
||||||
@ -95,19 +142,7 @@ string BytesUtils::formatUnsigned(bytes const& _bytes) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatSigned(bytes const& _bytes) const
|
string BytesUtils::formatBoolean(bytes const& _bytes)
|
||||||
{
|
|
||||||
stringstream os;
|
|
||||||
|
|
||||||
if (*_bytes.begin() & 0x80)
|
|
||||||
os << u2s(fromBigEndian<u256>(_bytes));
|
|
||||||
else
|
|
||||||
os << fromBigEndian<u256>(_bytes);
|
|
||||||
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
string BytesUtils::formatBoolean(bytes const& _bytes) const
|
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
u256 result = fromBigEndian<u256>(_bytes);
|
u256 result = fromBigEndian<u256>(_bytes);
|
||||||
@ -122,7 +157,7 @@ string BytesUtils::formatBoolean(bytes const& _bytes) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatHex(bytes const& _bytes) const
|
string BytesUtils::formatHex(bytes const& _bytes)
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
@ -133,7 +168,7 @@ string BytesUtils::formatHex(bytes const& _bytes) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatHexString(bytes const& _bytes) const
|
string BytesUtils::formatHexString(bytes const& _bytes)
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
@ -142,7 +177,7 @@ string BytesUtils::formatHexString(bytes const& _bytes) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const
|
string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
|
||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
|
|
||||||
@ -171,35 +206,96 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff) const
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes BytesUtils::alignLeft(bytes _bytes) const
|
string BytesUtils::formatRawBytes(bytes const& _bytes)
|
||||||
{
|
{
|
||||||
return std::move(_bytes) + bytes(32 - _bytes.size(), 0);
|
if (_bytes.empty())
|
||||||
}
|
return "[]";
|
||||||
|
|
||||||
bytes BytesUtils::alignRight(bytes _bytes) const
|
stringstream os;
|
||||||
{
|
auto it = _bytes.begin();
|
||||||
return bytes(32 - _bytes.size(), 0) + std::move(_bytes);
|
for (size_t i = 0; i < _bytes.size(); i += 32)
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
bytes byteRange{it, it + 32};
|
||||||
_abiType.align = ABIType::AlignLeft;
|
|
||||||
return alignLeft(std::move(_bytes));
|
os << " " << byteRange;
|
||||||
case Parameter::Alignment::Right:
|
|
||||||
_abiType.align = ABIType::AlignRight;
|
it += 32;
|
||||||
return alignRight(std::move(_bytes));
|
if (it != _bytes.end())
|
||||||
default:
|
os << endl;
|
||||||
_abiType.align = ABIType::AlignRight;
|
|
||||||
return alignRight(std::move(_bytes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string BytesUtils::formatBytes(
|
||||||
|
bytes const& _bytes,
|
||||||
|
ABIType const& _abiType
|
||||||
|
)
|
||||||
|
{
|
||||||
|
stringstream os;
|
||||||
|
|
||||||
|
switch (_abiType.type)
|
||||||
|
{
|
||||||
|
case ABIType::UnsignedDec:
|
||||||
|
// Check if the detected type was wrong and if this could
|
||||||
|
// be signed. If an unsigned was detected in the expectations,
|
||||||
|
// but the actual result returned a signed, it would be formatted
|
||||||
|
// incorrectly.
|
||||||
|
if (*_bytes.begin() & 0x80)
|
||||||
|
os << formatSigned(_bytes);
|
||||||
|
else
|
||||||
|
os << formatUnsigned(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::SignedDec:
|
||||||
|
os << formatSigned(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::Boolean:
|
||||||
|
os << formatBoolean(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::Hex:
|
||||||
|
os << formatHex(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::HexString:
|
||||||
|
os << formatHexString(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::String:
|
||||||
|
os << formatString(_bytes);
|
||||||
|
break;
|
||||||
|
case ABIType::Failure:
|
||||||
|
break;
|
||||||
|
case ABIType::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string BytesUtils::formatBytesRange(
|
||||||
|
bytes _bytes,
|
||||||
|
dev::solidity::test::ParameterList const& _parameters,
|
||||||
|
bool _highlight
|
||||||
|
)
|
||||||
|
{
|
||||||
|
stringstream os;
|
||||||
|
auto it = _bytes.begin();
|
||||||
|
|
||||||
|
for (auto const& parameter: _parameters)
|
||||||
|
{
|
||||||
|
bytes byteRange{it, it + static_cast<long>(parameter.abiType.size)};
|
||||||
|
|
||||||
|
if (!parameter.matchesBytes(byteRange))
|
||||||
|
AnsiColorized(
|
||||||
|
os,
|
||||||
|
_highlight,
|
||||||
|
{dev::formatting::RED_BACKGROUND}
|
||||||
|
) << formatBytes(byteRange, parameter.abiType);
|
||||||
|
else
|
||||||
|
os << parameter.rawString;
|
||||||
|
|
||||||
|
|
||||||
|
it += static_cast<long>(parameter.abiType.size);
|
||||||
|
if (¶meter != &_parameters.back())
|
||||||
|
os << ", ";
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -32,72 +32,92 @@ namespace test
|
|||||||
class BytesUtils
|
class BytesUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// Left-aligns and pads given _bytes and returns a new
|
||||||
|
/// bytes array.
|
||||||
|
static bytes alignLeft(bytes _bytes);
|
||||||
|
|
||||||
|
/// Right-aligns and pads given _bytes and returns a new
|
||||||
|
/// bytes array.
|
||||||
|
static bytes alignRight(bytes _bytes);
|
||||||
|
|
||||||
|
/// Applies given _alignment to _bytes and returns a new
|
||||||
|
/// bytes array.
|
||||||
|
/// TODO: Remove abiType reference from parameter list
|
||||||
|
/// and return the new alignment instead.
|
||||||
|
static bytes applyAlign(
|
||||||
|
Parameter::Alignment _alignment,
|
||||||
|
ABIType& _abiType,
|
||||||
|
bytes _bytes
|
||||||
|
);
|
||||||
|
|
||||||
/// Tries to convert \param _literal to an unpadded `bytes`
|
/// Tries to convert \param _literal to an unpadded `bytes`
|
||||||
/// representation of the boolean number literal. Throws if conversion fails.
|
/// representation of the boolean number literal. Throws if conversion fails.
|
||||||
bytes convertBoolean(std::string const& _literal);
|
static bytes convertBoolean(std::string const& _literal);
|
||||||
|
|
||||||
/// Tries to convert \param _literal to an unpadded `bytes`
|
/// Tries to convert \param _literal to an unpadded `bytes`
|
||||||
/// representation of the decimal number literal. Throws if conversion fails.
|
/// representation of the decimal number literal. Throws if conversion fails.
|
||||||
bytes convertNumber(std::string const& _literal);
|
static bytes convertNumber(std::string const& _literal);
|
||||||
|
|
||||||
/// Tries to convert \param _literal to an unpadded `bytes`
|
/// Tries to convert \param _literal to an unpadded `bytes`
|
||||||
/// representation of the hex literal. Throws if conversion fails.
|
/// representation of the hex literal. Throws if conversion fails.
|
||||||
bytes convertHexNumber(std::string const& _literal);
|
static bytes convertHexNumber(std::string const& _literal);
|
||||||
|
|
||||||
/// Tries to convert \param _literal to an unpadded `bytes`
|
/// Tries to convert \param _literal to an unpadded `bytes`
|
||||||
/// representation of the string literal. Throws if conversion fails.
|
/// representation of the string literal. Throws if conversion fails.
|
||||||
bytes convertString(std::string const& _literal);
|
static bytes convertString(std::string const& _literal);
|
||||||
|
|
||||||
/// 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
|
||||||
/// an unsigned value.
|
/// an unsigned value.
|
||||||
std::string formatUnsigned(bytes const& _bytes) const;
|
static std::string formatUnsigned(bytes const& _bytes);
|
||||||
|
|
||||||
/// 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 signed value.
|
/// a signed value.
|
||||||
std::string formatSigned(bytes const& _bytes) const;
|
static std::string formatSigned(bytes const& _bytes);
|
||||||
|
|
||||||
/// 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 boolean value.
|
/// a boolean value.
|
||||||
std::string formatBoolean(bytes const& _bytes) const;
|
static std::string formatBoolean(bytes const& _bytes);
|
||||||
|
|
||||||
/// 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 hex value.
|
/// a hex value.
|
||||||
std::string formatHex(bytes const& _bytes) const;
|
static std::string formatHex(bytes const& _bytes);
|
||||||
|
|
||||||
/// 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 hexString value.
|
/// a hexString value.
|
||||||
std::string formatHexString(bytes const& _bytes) const;
|
static std::string formatHexString(bytes const& _bytes);
|
||||||
|
|
||||||
/// 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, size_t _cutOff) const;
|
static std::string formatString(bytes const& _bytes, size_t _cutOff);
|
||||||
|
|
||||||
std::string formatString(bytes const& _bytes) const
|
static std::string formatString(bytes const& _bytes)
|
||||||
{
|
{
|
||||||
return formatString(_bytes, _bytes.size());
|
return formatString(_bytes, _bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Left-aligns and pads given _bytes and returns a new
|
/// Returns a string representation of given _bytes. Adds a newline
|
||||||
/// bytes array.
|
/// every 32 bytes to increase readability.
|
||||||
bytes alignLeft(bytes _bytes) const;
|
/// Used to print returned bytes from function calls to the commandline.
|
||||||
|
static std::string formatRawBytes(bytes const& _bytes);
|
||||||
|
|
||||||
/// Right-aligns and pads given _bytes and returns a new
|
/// Formats given _bytes with type information passed in _abiType.
|
||||||
/// bytes array.
|
static std::string formatBytes(bytes const& _bytes, ABIType const& _abiType);
|
||||||
bytes alignRight(bytes _bytes) const;
|
|
||||||
|
|
||||||
/// Applies given _alignment to _bytes and returns a new
|
/// Formats given _bytes with type information passed in _abiTypes.
|
||||||
/// bytes array.
|
/// Prints obtained result if it does not match the expectation
|
||||||
bytes applyAlign(
|
/// and prints the expected result otherwise.
|
||||||
Parameter::Alignment _alignment,
|
/// Highlights parameter only if it does not match.
|
||||||
ABIType& _abiType,
|
static std::string formatBytesRange(
|
||||||
bytes _bytes
|
bytes _bytes,
|
||||||
) const;
|
ParameterList const& _parameters,
|
||||||
|
bool _highlight
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,18 @@
|
|||||||
|
|
||||||
#include <test/libsolidity/util/ContractABIUtils.h>
|
#include <test/libsolidity/util/ContractABIUtils.h>
|
||||||
|
|
||||||
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
|
|
||||||
#include <liblangutil/Common.h>
|
#include <liblangutil/Common.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/assign/list_of.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/range/algorithm_ext/for_each.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <numeric>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@ -33,55 +39,266 @@ using namespace dev::solidity::test;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace soltest;
|
using namespace soltest;
|
||||||
|
|
||||||
dev::solidity::test::ParameterList ContractABIUtils::parametersFromJson(
|
namespace
|
||||||
Json::Value const& _contractABI,
|
|
||||||
string const& _functionName
|
|
||||||
) const
|
|
||||||
{
|
{
|
||||||
ParameterList abiParams;
|
|
||||||
|
using ParameterList = dev::solidity::test::ParameterList;
|
||||||
|
|
||||||
|
size_t arraySize(string const& _arrayType)
|
||||||
|
{
|
||||||
|
auto leftBrack = _arrayType.find("[");
|
||||||
|
auto rightBrack = _arrayType.rfind("]");
|
||||||
|
|
||||||
|
soltestAssert(
|
||||||
|
leftBrack != string::npos &&
|
||||||
|
rightBrack != string::npos &&
|
||||||
|
rightBrack == _arrayType.size() - 1 &&
|
||||||
|
leftBrack < rightBrack,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
string size = _arrayType.substr(leftBrack + 1, rightBrack - leftBrack - 1);
|
||||||
|
|
||||||
|
return static_cast<size_t>(stoi(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBool(string const& _type)
|
||||||
|
{
|
||||||
|
return _type == "bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUint(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"uint\\d*"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInt(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"int\\d*"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedBytes(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"bytes\\d+"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBytes(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"\\bbytes\\b"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isString(string const& _type)
|
||||||
|
{
|
||||||
|
return _type == "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedBoolArray(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"bool\\[\\d+\\]"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedUintArray(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"uint\\d*\\[\\d+\\]"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedIntArray(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"int\\d*\\[\\d+\\]"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedStringArray(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"string\\[\\d+\\]"});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTuple(string const& _type)
|
||||||
|
{
|
||||||
|
return _type == "tuple";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFixedTupleArray(string const& _type)
|
||||||
|
{
|
||||||
|
return regex_match(_type, regex{"tuple\\[\\d+\\]"});
|
||||||
|
}
|
||||||
|
|
||||||
|
string functionSignatureFromABI(Json::Value const& _functionABI)
|
||||||
|
{
|
||||||
|
auto inputs = _functionABI["inputs"];
|
||||||
|
string signature = {_functionABI["name"].asString() + "("};
|
||||||
|
size_t parameterCount = 0;
|
||||||
|
|
||||||
|
for (auto const& input: inputs)
|
||||||
|
{
|
||||||
|
parameterCount++;
|
||||||
|
signature += input["type"].asString();
|
||||||
|
if (parameterCount < inputs.size())
|
||||||
|
signature += ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<dev::solidity::test::ParameterList> ContractABIUtils::parametersFromJsonOutputs(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
Json::Value const& _contractABI,
|
||||||
|
string const& _functionSignature
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!_contractABI)
|
||||||
|
return boost::none;
|
||||||
|
|
||||||
for (auto const& function: _contractABI)
|
for (auto const& function: _contractABI)
|
||||||
if (function["name"] == _functionName)
|
if (_functionSignature == functionSignatureFromABI(function))
|
||||||
|
{
|
||||||
|
ParameterList inplaceTypeParams;
|
||||||
|
ParameterList dynamicTypeParams;
|
||||||
|
ParameterList finalParams;
|
||||||
|
|
||||||
for (auto const& output: function["outputs"])
|
for (auto const& output: function["outputs"])
|
||||||
{
|
{
|
||||||
auto types = fromTypeName(output["type"].asString());
|
string type = output["type"].asString();
|
||||||
for (auto const& type: types)
|
|
||||||
abiParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
|
|
||||||
}
|
|
||||||
|
|
||||||
return abiParams;
|
ABITypes inplaceTypes;
|
||||||
|
ABITypes dynamicTypes;
|
||||||
|
|
||||||
|
if (appendTypesFromName(output, inplaceTypes, dynamicTypes))
|
||||||
|
{
|
||||||
|
for (auto const& type: inplaceTypes)
|
||||||
|
inplaceTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
|
||||||
|
for (auto const& type: dynamicTypes)
|
||||||
|
dynamicTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_errorReporter.warning(
|
||||||
|
"Could not convert \"" + type +
|
||||||
|
"\" to internal ABI type representation. Falling back to default encoding."
|
||||||
|
);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalParams += inplaceTypeParams;
|
||||||
|
|
||||||
|
inplaceTypeParams.clear();
|
||||||
|
}
|
||||||
|
return boost::optional<ParameterList>(finalParams + dynamicTypeParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ABIType> ContractABIUtils::fromTypeName(string const& _type) const
|
bool ContractABIUtils::appendTypesFromName(
|
||||||
|
Json::Value const& _functionOutput,
|
||||||
|
ABITypes& _inplaceTypes,
|
||||||
|
ABITypes& _dynamicTypes,
|
||||||
|
bool _isCompoundType
|
||||||
|
)
|
||||||
{
|
{
|
||||||
static regex s_boolType{"(bool)"};
|
string type = _functionOutput["type"].asString();
|
||||||
static regex s_uintType{"(uint\\d*)"};
|
if (isBool(type))
|
||||||
static regex s_intType{"(int\\d*)"};
|
_inplaceTypes.push_back(ABIType{ABIType::Boolean});
|
||||||
static regex s_bytesType{"(bytes\\d+)"};
|
else if (isUint(type))
|
||||||
static regex s_dynBytesType{"(\\bbytes\\b)"};
|
_inplaceTypes.push_back(ABIType{ABIType::UnsignedDec});
|
||||||
static regex s_stringType{"(string)"};
|
else if (isInt(type))
|
||||||
|
_inplaceTypes.push_back(ABIType{ABIType::SignedDec});
|
||||||
|
else if (isFixedBytes(type))
|
||||||
|
_inplaceTypes.push_back(ABIType{ABIType::Hex});
|
||||||
|
else if (isString(type))
|
||||||
|
{
|
||||||
|
_inplaceTypes.push_back(ABIType{ABIType::Hex});
|
||||||
|
|
||||||
vector<ABIType> abiTypes;
|
if (_isCompoundType)
|
||||||
if (regex_match(_type, s_boolType))
|
_dynamicTypes.push_back(ABIType{ABIType::Hex});
|
||||||
abiTypes.push_back(ABIType{ABIType::Boolean, ABIType::AlignRight, 32});
|
|
||||||
else if (regex_match(_type, s_uintType))
|
_dynamicTypes.push_back(ABIType{ABIType::UnsignedDec});
|
||||||
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
|
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
|
||||||
else if (regex_match(_type, s_intType))
|
|
||||||
abiTypes.push_back(ABIType{ABIType::SignedDec, ABIType::AlignRight, 32});
|
|
||||||
else if (regex_match(_type, s_bytesType))
|
|
||||||
abiTypes.push_back(ABIType{ABIType::Hex, ABIType::AlignRight, 32});
|
|
||||||
else if (regex_match(_type, s_dynBytesType))
|
|
||||||
{
|
|
||||||
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
|
|
||||||
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
|
|
||||||
abiTypes.push_back(ABIType{ABIType::HexString, ABIType::AlignLeft, 32});
|
|
||||||
}
|
}
|
||||||
else if (regex_match(_type, s_stringType))
|
else if (isTuple(type))
|
||||||
{
|
{
|
||||||
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
|
ABITypes inplaceTypes;
|
||||||
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
|
ABITypes dynamicTypes;
|
||||||
abiTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft, 32});
|
|
||||||
|
for (auto const& component: _functionOutput["components"])
|
||||||
|
appendTypesFromName(component, inplaceTypes, dynamicTypes, true);
|
||||||
|
_dynamicTypes += inplaceTypes + dynamicTypes;
|
||||||
|
}
|
||||||
|
else if (isFixedBoolArray(type))
|
||||||
|
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::Boolean});
|
||||||
|
else if (isFixedUintArray(type))
|
||||||
|
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::UnsignedDec});
|
||||||
|
else if (isFixedIntArray(type))
|
||||||
|
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::SignedDec});
|
||||||
|
else if (isFixedStringArray(type))
|
||||||
|
{
|
||||||
|
_inplaceTypes.push_back(ABIType{ABIType::Hex});
|
||||||
|
|
||||||
|
_dynamicTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::Hex});
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arraySize(type); i++)
|
||||||
|
{
|
||||||
|
_dynamicTypes.push_back(ABIType{ABIType::UnsignedDec});
|
||||||
|
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isBytes(type))
|
||||||
|
return false;
|
||||||
|
else if (isFixedTupleArray(type))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContractABIUtils::overwriteParameters(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
dev::solidity::test::ParameterList& _targetParameters,
|
||||||
|
dev::solidity::test::ParameterList const& _sourceParameters
|
||||||
|
)
|
||||||
|
{
|
||||||
|
boost::for_each(
|
||||||
|
_sourceParameters,
|
||||||
|
_targetParameters,
|
||||||
|
boost::bind<void>(
|
||||||
|
[&](Parameter _a, Parameter& _b) -> void
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
_a.abiType.size != _b.abiType.size ||
|
||||||
|
_a.abiType.type != _b.abiType.type
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_errorReporter.warning("Type or size of parameter(s) does not match.");
|
||||||
|
_b = _a;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_1,
|
||||||
|
_2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev::solidity::test::ParameterList ContractABIUtils::preferredParameters(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
dev::solidity::test::ParameterList const& _targetParameters,
|
||||||
|
dev::solidity::test::ParameterList const& _sourceParameters,
|
||||||
|
bytes const& _bytes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_targetParameters.size() != _sourceParameters.size())
|
||||||
|
{
|
||||||
|
auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; };
|
||||||
|
size_t encodingSize = accumulate(_targetParameters.begin(), _targetParameters.end(), size_t{0}, sizeFold);
|
||||||
|
|
||||||
|
_errorReporter.warning(
|
||||||
|
"Encoding does not match byte range. The call returned " +
|
||||||
|
to_string(_bytes.size()) + " bytes, but " +
|
||||||
|
to_string(encodingSize) + " bytes were expected."
|
||||||
|
);
|
||||||
|
return _sourceParameters;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
abiTypes.push_back(ABIType{ABIType::None, ABIType::AlignRight, 0});
|
return _targetParameters;
|
||||||
return abiTypes;
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include <test/libsolidity/util/SoltestTypes.h>
|
#include <test/libsolidity/util/SoltestTypes.h>
|
||||||
|
|
||||||
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
|
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
@ -27,6 +29,8 @@ namespace solidity
|
|||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using ABITypes = std::vector<ABIType>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class that aids conversions from contract ABI types stored in a
|
* Utility class that aids conversions from contract ABI types stored in a
|
||||||
* Json value to the internal ABIType representation of isoltest.
|
* Json value to the internal ABIType representation of isoltest.
|
||||||
@ -36,10 +40,30 @@ class ContractABIUtils
|
|||||||
public:
|
public:
|
||||||
/// Parses and translates Solidity's ABI types as Json string into
|
/// Parses and translates Solidity's ABI types as Json string into
|
||||||
/// a list of internal type representations of isoltest.
|
/// a list of internal type representations of isoltest.
|
||||||
ParameterList parametersFromJson(
|
/// Creates parameters from Contract ABI and is used to generate values for
|
||||||
|
/// auto-correction during interactive update routine.
|
||||||
|
static boost::optional<ParameterList> parametersFromJsonOutputs(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
Json::Value const& _contractABI,
|
Json::Value const& _contractABI,
|
||||||
std::string const& _functionName
|
std::string const& _functionSignature
|
||||||
) const;
|
);
|
||||||
|
|
||||||
|
/// Overwrites _targetParameters if ABI types or sizes given
|
||||||
|
/// by _sourceParameters do not match.
|
||||||
|
static void overwriteParameters(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
ParameterList& _targetParameters,
|
||||||
|
ParameterList const& _sourceParameters
|
||||||
|
);
|
||||||
|
|
||||||
|
/// If parameter count does not match, take types defined _sourceParameters
|
||||||
|
/// and create a warning if so.
|
||||||
|
static ParameterList preferredParameters(
|
||||||
|
ErrorReporter& _errorReporter,
|
||||||
|
ParameterList const& _targetParameters,
|
||||||
|
ParameterList const& _sourceParameters,
|
||||||
|
bytes const& _bytes
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parses and translates a single type and returns a list of
|
/// Parses and translates a single type and returns a list of
|
||||||
@ -51,7 +75,12 @@ private:
|
|||||||
/// `string` -> [`Unsigned`, `Unsigned`, `String`]
|
/// `string` -> [`Unsigned`, `Unsigned`, `String`]
|
||||||
/// `bytes` -> [`Unsigned`, `Unsigned`, `HexString`]
|
/// `bytes` -> [`Unsigned`, `Unsigned`, `HexString`]
|
||||||
/// ...
|
/// ...
|
||||||
std::vector<ABIType> fromTypeName(std::string const& _type) const;
|
static bool appendTypesFromName(
|
||||||
|
Json::Value const& _functionOutput,
|
||||||
|
ABITypes& _inplaceTypes,
|
||||||
|
ABITypes& _dynamicTypes,
|
||||||
|
bool _isCompoundType = false
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
128
test/libsolidity/util/SoltestErrors.h
Normal file
128
test/libsolidity/util/SoltestErrors.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/AnsiColorized.h>
|
||||||
|
#include <libdevcore/CommonData.h>
|
||||||
|
#include <libdevcore/Exceptions.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
|
||||||
|
#define soltestAssert(CONDITION, DESCRIPTION) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (!(CONDITION)) \
|
||||||
|
BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \
|
||||||
|
} \
|
||||||
|
while (false)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a notice, warning or error that can occur while
|
||||||
|
* formatting and therefore updating an interactive function call test.
|
||||||
|
*/
|
||||||
|
struct FormatError
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Notice,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit FormatError(Type _type, std::string _message):
|
||||||
|
type(_type),
|
||||||
|
message(std::move(_message))
|
||||||
|
{}
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
using FormatErrors = std::vector<FormatError>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class that collects notices, warnings and errors and is able
|
||||||
|
* to format them for ANSI colorized output during the interactive update
|
||||||
|
* process in isoltest.
|
||||||
|
* Its purpose is to help users of isoltest to automatically
|
||||||
|
* update test files and always keep track of what is happening.
|
||||||
|
*/
|
||||||
|
class ErrorReporter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ErrorReporter() {}
|
||||||
|
|
||||||
|
/// Adds a new FormatError of type Notice with the given message.
|
||||||
|
void notice(std::string _notice)
|
||||||
|
{
|
||||||
|
m_errors.push_back(FormatError{FormatError::Notice, std::move(_notice)});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new FormatError of type Warning with the given message.
|
||||||
|
void warning(std::string _warning)
|
||||||
|
{
|
||||||
|
m_errors.push_back(FormatError{FormatError::Warning, std::move(_warning)});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new FormatError of type Error with the given message.
|
||||||
|
void error(std::string _error)
|
||||||
|
{
|
||||||
|
m_errors.push_back(FormatError{FormatError::Error, std::move(_error)});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints all errors depending on their type using ANSI colorized output.
|
||||||
|
/// It will be used to print notices, warnings and errors during the
|
||||||
|
/// interactive update process.
|
||||||
|
std::string format(std::string const& _linePrefix, bool _formatted)
|
||||||
|
{
|
||||||
|
std::stringstream os;
|
||||||
|
for (auto const& error: m_errors)
|
||||||
|
{
|
||||||
|
switch (error.type)
|
||||||
|
{
|
||||||
|
case FormatError::Notice:
|
||||||
|
|
||||||
|
break;
|
||||||
|
case FormatError::Warning:
|
||||||
|
AnsiColorized(
|
||||||
|
os,
|
||||||
|
_formatted,
|
||||||
|
{formatting::YELLOW}
|
||||||
|
) << _linePrefix << "Warning: " << error.message << std::endl;
|
||||||
|
break;
|
||||||
|
case FormatError::Error:
|
||||||
|
AnsiColorized(
|
||||||
|
os,
|
||||||
|
_formatted,
|
||||||
|
{formatting::RED}
|
||||||
|
) << _linePrefix << "Error: " << error.message << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FormatErrors m_errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/AnsiColorized.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
|
|
||||||
@ -111,9 +112,23 @@ struct ABIType
|
|||||||
AlignRight,
|
AlignRight,
|
||||||
AlignNone,
|
AlignNone,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
explicit ABIType(
|
||||||
|
Type _type,
|
||||||
|
Align _align = ABIType::AlignRight,
|
||||||
|
size_t _size = 32
|
||||||
|
): type(_type), align(_align), size(_size) {}
|
||||||
|
|
||||||
|
ABIType(ABIType const& _other):
|
||||||
|
type(_other.type),
|
||||||
|
align(_other.align),
|
||||||
|
size(_other.size),
|
||||||
|
alignDeclared(_other.alignDeclared)
|
||||||
|
{}
|
||||||
|
|
||||||
Type type = ABIType::None;
|
Type type = ABIType::None;
|
||||||
Align align = ABIType::AlignRight;
|
Align align = ABIType::AlignRight;
|
||||||
size_t size = 0;
|
size_t size = 32;
|
||||||
bool alignDeclared = false;
|
bool alignDeclared = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,7 +145,7 @@ struct FormatInfo
|
|||||||
* Parameter abstraction used for the encoding and decoding of
|
* Parameter abstraction used for the encoding and decoding of
|
||||||
* function parameter and expectation / return value lists.
|
* function parameter and expectation / return value lists.
|
||||||
* A parameter list is usually a comma-separated list of literals.
|
* A parameter list is usually a comma-separated list of literals.
|
||||||
* It should not be possible to call create a parameter holding
|
* It should not be possible to create a parameter holding
|
||||||
* an identifier, but if so, the ABI type would be invalid.
|
* an identifier, but if so, the ABI type would be invalid.
|
||||||
*/
|
*/
|
||||||
struct Parameter
|
struct Parameter
|
||||||
@ -154,12 +169,17 @@ struct Parameter
|
|||||||
/// Types that were used to encode `rawBytes`. Expectations
|
/// Types that were used to encode `rawBytes`. Expectations
|
||||||
/// are usually comma separated literals. Their type is auto-
|
/// are usually comma separated literals. Their type is auto-
|
||||||
/// detected and retained in order to format them later on.
|
/// detected and retained in order to format them later on.
|
||||||
ABIType abiType;
|
ABIType abiType = ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32};
|
||||||
/// Format info attached to the parameter. It handles newlines given
|
/// Format info attached to the parameter. It handles newlines given
|
||||||
/// in the declaration of it.
|
/// in the declaration of it.
|
||||||
FormatInfo format;
|
FormatInfo format;
|
||||||
/// Stores the parsed alignment, which can be either left(...) or right(...).
|
/// Stores the parsed alignment, which can be either left(...) or right(...).
|
||||||
Alignment alignment = Alignment::None;
|
Alignment alignment = Alignment::None;
|
||||||
|
/// Compares _bytes to the bytes stored in this object.
|
||||||
|
bool matchesBytes(bytes const& _bytes) const
|
||||||
|
{
|
||||||
|
return rawBytes == _bytes;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
using ParameterList = std::vector<Parameter>;
|
using ParameterList = std::vector<Parameter>;
|
||||||
|
|
||||||
|
@ -234,9 +234,9 @@ Parameter TestFileParser::parseParameter()
|
|||||||
parameter.format.newline = true;
|
parameter.format.newline = true;
|
||||||
m_lineNumber++;
|
m_lineNumber++;
|
||||||
}
|
}
|
||||||
|
parameter.abiType = ABIType{ABIType::None, ABIType::AlignNone, 0};
|
||||||
|
|
||||||
bool isSigned = false;
|
bool isSigned = false;
|
||||||
|
|
||||||
if (accept(Token::Left, true))
|
if (accept(Token::Left, true))
|
||||||
{
|
{
|
||||||
parameter.rawString += formatToken(Token::Left);
|
parameter.rawString += formatToken(Token::Left);
|
||||||
@ -267,10 +267,10 @@ Parameter TestFileParser::parseParameter()
|
|||||||
parameter.abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32};
|
parameter.abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32};
|
||||||
string parsed = parseBoolean();
|
string parsed = parseBoolean();
|
||||||
parameter.rawString += parsed;
|
parameter.rawString += parsed;
|
||||||
parameter.rawBytes = BytesUtils().applyAlign(
|
parameter.rawBytes = BytesUtils::applyAlign(
|
||||||
parameter.alignment,
|
parameter.alignment,
|
||||||
parameter.abiType,
|
parameter.abiType,
|
||||||
BytesUtils().convertBoolean(parsed)
|
BytesUtils::convertBoolean(parsed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (accept(Token::HexNumber))
|
else if (accept(Token::HexNumber))
|
||||||
@ -281,10 +281,10 @@ Parameter TestFileParser::parseParameter()
|
|||||||
parameter.abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32};
|
parameter.abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32};
|
||||||
string parsed = parseHexNumber();
|
string parsed = parseHexNumber();
|
||||||
parameter.rawString += parsed;
|
parameter.rawString += parsed;
|
||||||
parameter.rawBytes = BytesUtils().applyAlign(
|
parameter.rawBytes = BytesUtils::applyAlign(
|
||||||
parameter.alignment,
|
parameter.alignment,
|
||||||
parameter.abiType,
|
parameter.abiType,
|
||||||
BytesUtils().convertHexNumber(parsed)
|
BytesUtils::convertHexNumber(parsed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (accept(Token::Hex, true))
|
else if (accept(Token::Hex, true))
|
||||||
@ -296,7 +296,7 @@ Parameter TestFileParser::parseParameter()
|
|||||||
|
|
||||||
string parsed = parseString();
|
string parsed = parseString();
|
||||||
parameter.rawString += "hex\"" + parsed + "\"";
|
parameter.rawString += "hex\"" + parsed + "\"";
|
||||||
parameter.rawBytes = BytesUtils().convertHexNumber(parsed);
|
parameter.rawBytes = BytesUtils::convertHexNumber(parsed);
|
||||||
parameter.abiType = ABIType{
|
parameter.abiType = ABIType{
|
||||||
ABIType::HexString, ABIType::AlignNone, parameter.rawBytes.size()
|
ABIType::HexString, ABIType::AlignNone, parameter.rawBytes.size()
|
||||||
};
|
};
|
||||||
@ -309,12 +309,12 @@ Parameter TestFileParser::parseParameter()
|
|||||||
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
|
throw Error(Error::Type::ParserError, "String literals cannot be aligned or padded.");
|
||||||
|
|
||||||
string parsed = parseString();
|
string parsed = parseString();
|
||||||
parameter.abiType = {ABIType::String, ABIType::AlignLeft, parsed.size()};
|
parameter.abiType = 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,
|
||||||
parameter.abiType,
|
parameter.abiType,
|
||||||
BytesUtils().convertString(parsed)
|
BytesUtils::convertString(parsed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (accept(Token::Number))
|
else if (accept(Token::Number))
|
||||||
@ -327,10 +327,10 @@ Parameter TestFileParser::parseParameter()
|
|||||||
if (isSigned)
|
if (isSigned)
|
||||||
parsed = "-" + parsed;
|
parsed = "-" + parsed;
|
||||||
|
|
||||||
parameter.rawBytes = BytesUtils().applyAlign(
|
parameter.rawBytes = BytesUtils::applyAlign(
|
||||||
parameter.alignment,
|
parameter.alignment,
|
||||||
parameter.abiType,
|
parameter.abiType,
|
||||||
BytesUtils().convertNumber(parsed)
|
BytesUtils::convertNumber(parsed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (accept(Token::Failure, true))
|
else if (accept(Token::Failure, true))
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <libdevcore/AnsiColorized.h>
|
#include <libdevcore/AnsiColorized.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/optional/optional.hpp>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -98,7 +99,6 @@ string TestFunctionCall::format(
|
|||||||
string result;
|
string result;
|
||||||
if (!_renderResult)
|
if (!_renderResult)
|
||||||
{
|
{
|
||||||
bytes output = m_call.expectations.rawBytes();
|
|
||||||
bool const isFailure = m_call.expectations.failure;
|
bool const isFailure = m_call.expectations.failure;
|
||||||
result = isFailure ?
|
result = isFailure ?
|
||||||
failure :
|
failure :
|
||||||
@ -154,129 +154,49 @@ string TestFunctionCall::formatBytesParameters(
|
|||||||
ErrorReporter& _errorReporter,
|
ErrorReporter& _errorReporter,
|
||||||
bytes const& _bytes,
|
bytes const& _bytes,
|
||||||
string const& _signature,
|
string const& _signature,
|
||||||
dev::solidity::test::ParameterList const& _params,
|
dev::solidity::test::ParameterList const& _parameters,
|
||||||
bool _highlight
|
bool _highlight
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
using ParameterList = dev::solidity::test::ParameterList;
|
using ParameterList = dev::solidity::test::ParameterList;
|
||||||
|
|
||||||
stringstream os;
|
stringstream os;
|
||||||
string functionName{_signature.substr(0, _signature.find("("))};
|
|
||||||
|
|
||||||
/// Create parameters from Contract ABI. Used to generate values for
|
|
||||||
/// auto-correction during interactive update routine.
|
|
||||||
ParameterList abiParams = ContractABIUtils().parametersFromJson(m_contractABI, functionName);
|
|
||||||
|
|
||||||
/// 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 preferredParams;
|
|
||||||
if (m_contractABI && (_params.size() != abiParams.size()))
|
|
||||||
{
|
|
||||||
auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; };
|
|
||||||
size_t encodingSize = std::accumulate(_params.begin(), _params.end(), size_t{0}, sizeFold);
|
|
||||||
|
|
||||||
_errorReporter.warning(
|
|
||||||
"Encoding does not match byte range. The call returned " +
|
|
||||||
to_string(_bytes.size()) + " bytes, but " +
|
|
||||||
to_string(encodingSize) + " bytes were expected."
|
|
||||||
);
|
|
||||||
preferredParams = abiParams;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
preferredParams = _params;
|
|
||||||
|
|
||||||
/// If output is empty, do not format anything.
|
|
||||||
if (_bytes.empty())
|
if (_bytes.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
/// Format output bytes with the given parameters. ABI type takes precedence if:
|
_errorReporter.warning("The call to \"" + _signature + "\" returned \n" + BytesUtils::formatRawBytes(_bytes));
|
||||||
/// - size of ABI type is greater
|
|
||||||
/// - given expected type does not match and needs to be overridden in order
|
boost::optional<ParameterList> abiParams = ContractABIUtils::parametersFromJsonOutputs(
|
||||||
/// to generate a valid output of the parameter
|
_errorReporter,
|
||||||
auto it = _bytes.begin();
|
m_contractABI,
|
||||||
auto abiParam = abiParams.begin();
|
_signature
|
||||||
size_t paramIndex = 1;
|
);
|
||||||
for (auto const& param: preferredParams)
|
|
||||||
|
if (abiParams)
|
||||||
{
|
{
|
||||||
size_t size = param.abiType.size;
|
boost::optional<ParameterList> preferredParams = ContractABIUtils::preferredParameters(
|
||||||
if (m_contractABI)
|
_errorReporter,
|
||||||
size = std::max((*abiParam).abiType.size, param.abiType.size);
|
_parameters,
|
||||||
|
abiParams.get(),
|
||||||
|
_bytes
|
||||||
|
);
|
||||||
|
|
||||||
long offset = static_cast<long>(size);
|
if (preferredParams)
|
||||||
auto offsetIter = it + offset;
|
{
|
||||||
bytes byteRange{it, offsetIter};
|
ContractABIUtils::overwriteParameters(_errorReporter, preferredParams.get(), abiParams.get());
|
||||||
|
os << BytesUtils::formatBytesRange(_bytes, preferredParams.get(), _highlight);
|
||||||
/// Override type with ABI type if given one does not match.
|
}
|
||||||
auto type = param.abiType;
|
|
||||||
if (m_contractABI)
|
|
||||||
if ((*abiParam).abiType.type > param.abiType.type)
|
|
||||||
{
|
|
||||||
type = (*abiParam).abiType;
|
|
||||||
_errorReporter.warning(
|
|
||||||
"Type of parameter " + to_string(paramIndex) +
|
|
||||||
" does not match the one inferred from ABI."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints obtained result if it does not match the expectation
|
|
||||||
/// and prints the expected result otherwise.
|
|
||||||
/// Highlights parameter only if it does not match.
|
|
||||||
if (byteRange != param.rawBytes)
|
|
||||||
AnsiColorized(
|
|
||||||
os,
|
|
||||||
_highlight,
|
|
||||||
{dev::formatting::RED_BACKGROUND}
|
|
||||||
) << formatBytesRange(byteRange, type);
|
|
||||||
else
|
|
||||||
os << param.rawString;
|
|
||||||
|
|
||||||
if (abiParam != abiParams.end())
|
|
||||||
abiParam++;
|
|
||||||
|
|
||||||
it += offset;
|
|
||||||
paramIndex++;
|
|
||||||
if (¶m != &preferredParams.back())
|
|
||||||
os << ", ";
|
|
||||||
}
|
}
|
||||||
return os.str();
|
else
|
||||||
}
|
|
||||||
|
|
||||||
string TestFunctionCall::formatBytesRange(
|
|
||||||
bytes const& _bytes,
|
|
||||||
ABIType const& _abiType
|
|
||||||
) const
|
|
||||||
{
|
|
||||||
stringstream os;
|
|
||||||
|
|
||||||
switch (_abiType.type)
|
|
||||||
{
|
{
|
||||||
case ABIType::UnsignedDec:
|
ParameterList defaultParameters;
|
||||||
// Check if the detected type was wrong and if this could
|
fill_n(
|
||||||
// be signed. If an unsigned was detected in the expectations,
|
back_inserter(defaultParameters),
|
||||||
// but the actual result returned a signed, it would be formatted
|
ceil(_bytes.size() / 32),
|
||||||
// incorrectly.
|
Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}}
|
||||||
os << BytesUtils().formatUnsigned(_bytes);
|
);
|
||||||
break;
|
ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters);
|
||||||
case ABIType::SignedDec:
|
os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight);
|
||||||
os << BytesUtils().formatSigned(_bytes);
|
|
||||||
break;
|
|
||||||
case ABIType::Boolean:
|
|
||||||
os << BytesUtils().formatBoolean(_bytes);
|
|
||||||
break;
|
|
||||||
case ABIType::Hex:
|
|
||||||
os << BytesUtils().formatHex(_bytes);
|
|
||||||
break;
|
|
||||||
case ABIType::HexString:
|
|
||||||
os << BytesUtils().formatHexString(_bytes);
|
|
||||||
break;
|
|
||||||
case ABIType::String:
|
|
||||||
os << BytesUtils().formatString(_bytes, _abiType.size);
|
|
||||||
break;
|
|
||||||
case ABIType::Failure:
|
|
||||||
break;
|
|
||||||
case ABIType::None:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
@ -288,18 +208,14 @@ string TestFunctionCall::formatRawParameters(
|
|||||||
{
|
{
|
||||||
stringstream os;
|
stringstream os;
|
||||||
for (auto const& param: _params)
|
for (auto const& param: _params)
|
||||||
{
|
if (!param.rawString.empty())
|
||||||
if (param.format.newline)
|
|
||||||
os << endl << _linePrefix << "// ";
|
|
||||||
os << param.rawString;
|
|
||||||
if (¶m != &_params.back())
|
|
||||||
{
|
{
|
||||||
if (param.format.newline)
|
if (param.format.newline)
|
||||||
os << ",";
|
os << endl << _linePrefix << "// ";
|
||||||
else
|
os << param.rawString;
|
||||||
|
if (¶m != &_params.back())
|
||||||
os << ", ";
|
os << ", ";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <test/libsolidity/util/TestFileParser.h>
|
#include <test/libsolidity/util/TestFileParser.h>
|
||||||
#include <test/TestCase.h>
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
@ -37,95 +37,6 @@ namespace solidity
|
|||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* Representation of a notice, warning or error that can occur while
|
|
||||||
* formatting and therefore updating an interactive function call test.
|
|
||||||
*/
|
|
||||||
struct FormatError
|
|
||||||
{
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
Notice,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit FormatError(Type _type, std::string _message):
|
|
||||||
type(_type),
|
|
||||||
message(std::move(_message))
|
|
||||||
{}
|
|
||||||
|
|
||||||
Type type;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
using FormatErrors = std::vector<FormatError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class that collects notices, warnings and errors and is able
|
|
||||||
* to format them for ANSI colorized output during the interactive update
|
|
||||||
* process in isoltest.
|
|
||||||
* Its purpose is to help users of isoltest to automatically
|
|
||||||
* update test files and always keep track of what is happening.
|
|
||||||
*/
|
|
||||||
class ErrorReporter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ErrorReporter() {}
|
|
||||||
|
|
||||||
/// Adds a new FormatError of type Notice with the given message.
|
|
||||||
void notice(std::string _notice)
|
|
||||||
{
|
|
||||||
m_errors.push_back(FormatError{FormatError::Notice, std::move(_notice)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new FormatError of type Warning with the given message.
|
|
||||||
void warning(std::string _warning)
|
|
||||||
{
|
|
||||||
m_errors.push_back(FormatError{FormatError::Warning, std::move(_warning)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new FormatError of type Error with the given message.
|
|
||||||
void error(std::string _error)
|
|
||||||
{
|
|
||||||
m_errors.push_back(FormatError{FormatError::Error, std::move(_error)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints all errors depending on their type using ANSI colorized output.
|
|
||||||
/// It will be used to print notices, warnings and errors during the
|
|
||||||
/// interactive update process.
|
|
||||||
std::string format(std::string const& _linePrefix, bool _formatted)
|
|
||||||
{
|
|
||||||
std::stringstream os;
|
|
||||||
for (auto const& error: m_errors)
|
|
||||||
{
|
|
||||||
switch (error.type)
|
|
||||||
{
|
|
||||||
case FormatError::Notice:
|
|
||||||
|
|
||||||
break;
|
|
||||||
case FormatError::Warning:
|
|
||||||
AnsiColorized(
|
|
||||||
os,
|
|
||||||
_formatted,
|
|
||||||
{formatting::YELLOW}
|
|
||||||
) << _linePrefix << "Warning: " << error.message << std::endl;
|
|
||||||
break;
|
|
||||||
case FormatError::Error:
|
|
||||||
AnsiColorized(
|
|
||||||
os,
|
|
||||||
_formatted,
|
|
||||||
{formatting::RED}
|
|
||||||
) << _linePrefix << "Error: " << error.message << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FormatErrors m_errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a function call and the result it returned. It stores the call
|
* Represents a function call and the result it returned. It stores the call
|
||||||
* representation itself, the actual byte result (if any) and a string representation
|
* representation itself, the actual byte result (if any) and a string representation
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <test/libyul/YulOptimizerTest.h>
|
#include <test/libyul/YulOptimizerTest.h>
|
||||||
|
|
||||||
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
#include <test/Options.h>
|
#include <test/Options.h>
|
||||||
|
|
||||||
#include <libyul/optimiser/BlockFlattener.h>
|
#include <libyul/optimiser/BlockFlattener.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user