diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp
new file mode 100644
index 000000000..1659324de
--- /dev/null
+++ b/test/libsolidity/util/BytesUtils.cpp
@@ -0,0 +1,165 @@
+/*
+ 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 .
+*/
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+using namespace dev;
+using namespace langutil;
+using namespace solidity;
+using namespace dev::solidity::test;
+using namespace std;
+using namespace soltest;
+
+bytes BytesUtils::convertBoolean(string const& _literal)
+{
+ if (_literal == "true")
+ return bytes{true};
+ else if (_literal == "false")
+ return bytes{false};
+ else
+ throw Error(Error::Type::ParserError, "Boolean literal invalid.");
+}
+
+bytes BytesUtils::convertNumber(string const& _literal)
+{
+ try
+ {
+ return toCompactBigEndian(u256{_literal});
+ }
+ catch (std::exception const&)
+ {
+ throw Error(Error::Type::ParserError, "Number encoding invalid.");
+ }
+}
+
+bytes BytesUtils::convertHexNumber(string const& _literal)
+{
+ try
+ {
+ if (_literal.size() % 2)
+ throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
+ else
+ return fromHex(_literal);
+ }
+ catch (std::exception const&)
+ {
+ throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
+ }
+}
+
+bytes BytesUtils::convertString(string const& _literal)
+{
+ try
+ {
+ return asBytes(_literal);
+ }
+ catch (std::exception const&)
+ {
+ throw Error(Error::Type::ParserError, "String encoding invalid.");
+ }
+}
+
+string BytesUtils::formatUnsigned(bytes const& _bytes) const
+{
+ stringstream os;
+
+ if (*_bytes.begin() & 0x80)
+ os << u2s(fromBigEndian(_bytes));
+ else
+ os << fromBigEndian(_bytes);
+
+ return os.str();
+}
+
+string BytesUtils::formatSigned(bytes const& _bytes) const
+{
+ stringstream os;
+
+ if (*_bytes.begin() & 0x80)
+ os << u2s(fromBigEndian(_bytes));
+ else
+ os << fromBigEndian(_bytes);
+
+ return os.str();
+}
+
+string BytesUtils::formatBoolean(bytes const& _bytes) const
+{
+ stringstream os;
+ u256 result = fromBigEndian(_bytes);
+
+ if (result == 0)
+ os << "false";
+ else if (result == 1)
+ os << "true";
+ else
+ os << result;
+
+ return os.str();
+}
+
+string BytesUtils::formatHex(bytes const& _bytes) const
+{
+ stringstream os;
+
+ string hex{toHex(_bytes, HexPrefix::Add)};
+ boost::algorithm::replace_all(hex, "00", "");
+ os << hex;
+
+ return os.str();
+}
+
+string BytesUtils::formatHexString(bytes const& _bytes) const
+{
+ stringstream os;
+
+ os << "hex\"" << toHex(_bytes) << "\"";
+
+ return os.str();
+}
+
+string BytesUtils::formatString(bytes const& _bytes) const
+{
+ stringstream os;
+
+ os << "\"";
+ bool expectZeros = false;
+ for (auto const& v: _bytes)
+ {
+ if (expectZeros && v != 0)
+ return {};
+ if (v == 0) expectZeros = true;
+ else
+ {
+ if (!isprint(v) || v == '"')
+ return {};
+ os << v;
+ }
+ }
+ os << "\"";
+
+ return os.str();
+}
diff --git a/test/libsolidity/util/BytesUtils.h b/test/libsolidity/util/BytesUtils.h
new file mode 100644
index 000000000..4bcf1a5b4
--- /dev/null
+++ b/test/libsolidity/util/BytesUtils.h
@@ -0,0 +1,84 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+#include
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+/**
+ * Utility class that aids conversions from parsed strings to an
+ * isoltest-internal, ABI-based bytes representation and vice-versa.
+ */
+class BytesUtils
+{
+public:
+ /// Tries to convert \param _literal to an unpadded `bytes`
+ /// representation of the boolean number literal. Throws if conversion fails.
+ bytes convertBoolean(std::string const& _literal);
+
+ /// Tries to convert \param _literal to an unpadded `bytes`
+ /// representation of the decimal number literal. Throws if conversion fails.
+ bytes convertNumber(std::string const& _literal);
+
+ /// Tries to convert \param _literal to an unpadded `bytes`
+ /// representation of the hex literal. Throws if conversion fails.
+ bytes convertHexNumber(std::string const& _literal);
+
+ /// Tries to convert \param _literal to an unpadded `bytes`
+ /// representation of the string literal. Throws if conversion fails.
+ bytes convertString(std::string const& _literal);
+
+ /// Converts \param _bytes to a soltest-compliant and human-readable
+ /// string representation of a byte array which is assumed to hold
+ /// an unsigned value.
+ std::string formatUnsigned(bytes const& _bytes) const;
+
+ /// Converts \param _bytes to a soltest-compliant and human-readable
+ /// string representation of a byte array which is assumed to hold
+ /// a signed value.
+ std::string formatSigned(bytes const& _bytes) const;
+
+ /// Converts \param _bytes to a soltest-compliant and human-readable
+ /// string representation of a byte array which is assumed to hold
+ /// a boolean value.
+ std::string formatBoolean(bytes const& _bytes) const;
+
+ /// Converts \param _bytes to a soltest-compliant and human-readable
+ /// string representation of a byte array which is assumed to hold
+ /// a hex value.
+ std::string formatHex(bytes const& _bytes) const;
+
+ /// Converts \param _bytes to a soltest-compliant and human-readable
+ /// string representation of a byte array which is assumed to hold
+ /// a hexString value.
+ std::string formatHexString(bytes const& _bytes) const;
+
+ /// 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;
+};
+
+}
+}
+}
diff --git a/test/libsolidity/util/ContractABIUtils.cpp b/test/libsolidity/util/ContractABIUtils.cpp
new file mode 100644
index 000000000..ec9cbcba0
--- /dev/null
+++ b/test/libsolidity/util/ContractABIUtils.cpp
@@ -0,0 +1,87 @@
+/*
+ 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 .
+*/
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+using namespace dev;
+using namespace langutil;
+using namespace solidity;
+using namespace dev::solidity::test;
+using namespace std;
+using namespace soltest;
+
+dev::solidity::test::ParameterList ContractABIUtils::parametersFromJson(
+ Json::Value const& _contractABI,
+ string const& _functionName
+) const
+{
+ ParameterList abiParams;
+ for (auto const& function: _contractABI)
+ if (function["name"] == _functionName)
+ for (auto const& output: function["outputs"])
+ {
+ auto types = fromTypeName(output["type"].asString());
+ for (auto const& type: types)
+ abiParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
+ }
+
+ return abiParams;
+}
+
+std::vector ContractABIUtils::fromTypeName(string const& _type) const
+{
+ static regex s_boolType{"(bool)"};
+ static regex s_uintType{"(uint\\d*)"};
+ static regex s_intType{"(int\\d*)"};
+ static regex s_bytesType{"(bytes\\d+)"};
+ static regex s_dynBytesType{"(\\bbytes\\b)"};
+ static regex s_stringType{"(string)"};
+
+ vector abiTypes;
+ if (regex_match(_type, s_boolType))
+ abiTypes.push_back(ABIType{ABIType::Boolean, ABIType::AlignRight, 32});
+ else if (regex_match(_type, s_uintType))
+ abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
+ 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))
+ {
+ abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
+ abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
+ abiTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft, 32});
+ }
+ else
+ abiTypes.push_back(ABIType{ABIType::None, ABIType::AlignRight, 0});
+ return abiTypes;
+}
diff --git a/test/libsolidity/util/ContractABIUtils.h b/test/libsolidity/util/ContractABIUtils.h
new file mode 100644
index 000000000..cafd63bac
--- /dev/null
+++ b/test/libsolidity/util/ContractABIUtils.h
@@ -0,0 +1,59 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+/**
+ * Utility class that aids conversions from contract ABI types stored in a
+ * Json value to the internal ABIType representation of isoltest.
+ */
+class ContractABIUtils
+{
+public:
+ /// Parses and translates Solidity's ABI types as Json string into
+ /// a list of internal type representations of isoltest.
+ ParameterList parametersFromJson(
+ Json::Value const& _contractABI,
+ std::string const& _functionName
+ ) const;
+
+private:
+ /// Parses and translates a single type and returns a list of
+ /// internal type representations of isoltest.
+ /// Types defined by the ABI will translate to ABITypes
+ /// as follows:
+ /// `bool` -> [`Boolean`]
+ /// `uint` -> [`Unsigned`]
+ /// `string` -> [`Unsigned`, `Unsigned`, `String`]
+ /// `bytes` -> [`Unsigned`, `Unsigned`, `HexString`]
+ /// ...
+ std::vector fromTypeName(std::string const& _type) const;
+};
+
+}
+}
+}
diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h
new file mode 100644
index 000000000..9eba08869
--- /dev/null
+++ b/test/libsolidity/util/SoltestTypes.h
@@ -0,0 +1,247 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+#include
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+/**
+ * All soltest tokens.
+ */
+#define SOLT_TOKEN_LIST(T, K) \
+ T(Unknown, "unknown", 0) \
+ T(Invalid, "invalid", 0) \
+ T(EOS, "EOS", 0) \
+ T(Whitespace, "_", 0) \
+ /* punctuations */ \
+ T(LParen, "(", 0) \
+ T(RParen, ")", 0) \
+ T(LBrack, "[", 0) \
+ T(RBrack, "]", 0) \
+ T(LBrace, "{", 0) \
+ T(RBrace, "}", 0) \
+ T(Sub, "-", 0) \
+ T(Colon, ":", 0) \
+ T(Comma, ",", 0) \
+ T(Period, ".", 0) \
+ T(Arrow, "->", 0) \
+ T(Newline, "//", 0) \
+ /* Literals & identifier */ \
+ T(Comment, "#", 0) \
+ T(Number, "number", 0) \
+ T(HexNumber, "hex_number", 0) \
+ T(String, "string", 0) \
+ T(Identifier, "identifier", 0) \
+ /* type keywords */ \
+ K(Ether, "ether", 0) \
+ K(Hex, "hex", 0) \
+ K(Boolean, "boolean", 0) \
+ /* special keywords */ \
+ K(Left, "left", 0) \
+ K(Right, "right", 0) \
+ K(Failure, "FAILURE", 0) \
+
+namespace soltest
+{
+ enum class Token : unsigned int {
+ #define T(name, string, precedence) name,
+ SOLT_TOKEN_LIST(T, T)
+ NUM_TOKENS
+ #undef T
+ };
+
+ /// Prints a friendly string representation of \param _token.
+ inline std::string formatToken(Token _token)
+ {
+ switch (_token)
+ {
+ #define T(name, string, precedence) case Token::name: return string;
+ SOLT_TOKEN_LIST(T, T)
+ #undef T
+ default: // Token::NUM_TOKENS:
+ return "";
+ }
+ }
+}
+
+/**
+ * The purpose of the ABI type is the storage of type information
+ * retrieved while parsing a test. This information is used
+ * for the conversion of human-readable function arguments and
+ * return values to `bytes` and vice-versa.
+ * Defaults to None, a 0-byte representation. 0-bytes
+ * can also be interpreted as Failure, which means
+ * either a REVERT or another EVM failure.
+ */
+struct ABIType
+{
+ enum Type
+ {
+ None,
+ Failure,
+ Boolean,
+ UnsignedDec,
+ SignedDec,
+ Hex,
+ HexString,
+ String
+ };
+ enum Align
+ {
+ AlignLeft,
+ AlignRight,
+ AlignNone,
+ };
+ Type type = ABIType::None;
+ Align align = ABIType::AlignRight;
+ size_t size = 0;
+ bool alignDeclared = false;
+};
+
+/**
+ * Helper that can hold format information retrieved
+ * while scanning through a parameter list in soltest.
+ */
+struct FormatInfo
+{
+ bool newline = false;
+};
+
+/**
+ * Parameter abstraction used for the encoding and decoding of
+ * function parameter and expectation / return value lists.
+ * A parameter list is usually a comma-separated list of literals.
+ * It should not be possible to call create a parameter holding
+ * an identifier, but if so, the ABI type would be invalid.
+ */
+struct Parameter
+{
+ /// ABI encoded / decoded `bytes` of values.
+ /// These `bytes` are used to pass values to function calls
+ /// and also to store expected return vales. These are
+ /// compared to the actual result of a function call
+ /// and used for validating it.
+ bytes rawBytes;
+ /// Stores the raw string representation of this parameter.
+ /// Used to print the unformatted arguments of a function call.
+ std::string rawString;
+ /// Types that were used to encode `rawBytes`. Expectations
+ /// are usually comma separated literals. Their type is auto-
+ /// detected and retained in order to format them later on.
+ ABIType abiType;
+ /// Format info attached to the parameter. It handles newlines given
+ /// in the declaration of it.
+ FormatInfo format;
+};
+using ParameterList = std::vector;
+
+/**
+ * Represents the expected result of a function call after it has been executed. This may be a single
+ * return value or a comma-separated list of return values. It also contains the detected input
+ * formats used to convert the values to `bytes` needed for the comparison with the actual result
+ * of a call. In addition to that, it also stores the expected transaction status.
+ * An optional comment can be assigned.
+ */
+struct FunctionCallExpectations
+{
+ /// Representation of the comma-separated (or empty) list of expected result values
+ /// attached to the function call object. It is checked against the actual result of
+ /// a function call when used in test framework.
+ ParameterList result;
+ /// Expected status of the transaction. It can be either
+ /// a REVERT or a different EVM failure (e.g. out-of-gas).
+ bool failure = true;
+ /// A Comment that can be attached to the expectations,
+ /// that is retained and can be displayed.
+ std::string comment;
+ /// ABI encoded `bytes` of parsed expected return values. It is checked
+ /// against the actual result of a function call when used in test framework.
+ bytes rawBytes() const
+ {
+ bytes raw;
+ for (auto const& param: result)
+ raw += param.rawBytes;
+ return raw;
+ }
+};
+
+/**
+ * Represents the arguments passed to a function call. This can be a single
+ * argument or a comma-separated list of arguments. It also contains the detected input
+ * formats used to convert the arguments to `bytes` needed for the call.
+ * An optional comment can be assigned.
+ */
+struct FunctionCallArgs
+{
+ /// Types that were used to encode `rawBytes`. Parameters
+ /// are usually comma separated literals. Their type is auto-
+ /// detected and retained in order to format them later on.
+ ParameterList parameters;
+ /// A Comment that can be attached to the expectations,
+ /// that is retained and can be displayed.
+ std::string comment;
+ /// ABI encoded `bytes` of parsed parameters. These `bytes`
+ /// passed to the function call.
+ bytes rawBytes() const
+ {
+ bytes raw;
+ for (auto const& param: parameters)
+ raw += param.rawBytes;
+ return raw;
+ }
+};
+
+/**
+ * Represents a function call read from an input stream. It contains the signature, the
+ * arguments, an optional ether value and an expected execution result.
+ */
+struct FunctionCall
+{
+ /// Signature of the function call, e.g. `f(uint256, uint256)`.
+ std::string signature;
+ /// Optional `ether` value that can be send with the call.
+ u256 value;
+ /// Object that holds all function parameters in their `bytes`
+ /// representations given by the contract ABI.
+ FunctionCallArgs arguments;
+ /// Object that holds all function call expectation in
+ /// their `bytes` representations given by the contract ABI.
+ /// They are checked against the actual results and their
+ /// `bytes` representation, as well as the transaction status.
+ FunctionCallExpectations expectations;
+ /// single / multi-line mode will be detected as follows:
+ /// every newline (//) in source results in a function call
+ /// that has its display mode set to multi-mode. Function and
+ /// result parameter lists are an exception: a single parameter
+ /// stores a format information that contains a newline definition.
+ enum DisplayMode {
+ SingleLine,
+ MultiLine
+ };
+ DisplayMode displayMode = DisplayMode::SingleLine;
+ /// Marks this function call as the constructor.
+ bool isConstructor = false;
+};
+
+}
+}
+}
diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp
index 4f61f6911..05f883be5 100644
--- a/test/libsolidity/util/TestFileParser.cpp
+++ b/test/libsolidity/util/TestFileParser.cpp
@@ -16,12 +16,17 @@
*/
#include
+
+#include
#include
+
#include
+
#include
#include
#include
#include
+
#include
#include
#include
@@ -242,10 +247,12 @@ Parameter TestFileParser::parseParameter()
Parameter parameter;
if (accept(Token::Newline, true))
parameter.format.newline = true;
+
auto literal = parseABITypeLiteral();
parameter.rawBytes = get<0>(literal);
parameter.abiType = get<1>(literal);
parameter.rawString = get<2>(literal);
+
return parameter;
}
@@ -286,7 +293,7 @@ tuple TestFileParser::parseABITypeLiteral()
abiType = ABIType{ABIType::Boolean, ABIType::AlignRight, 32};
string parsed = parseBoolean();
rawString += parsed;
- result = applyAlign(alignment, abiType, convertBoolean(parsed));
+ result = applyAlign(alignment, abiType, BytesUtils().convertBoolean(parsed));
}
else if (accept(Token::HexNumber))
{
@@ -295,7 +302,7 @@ tuple TestFileParser::parseABITypeLiteral()
abiType = ABIType{ABIType::Hex, ABIType::AlignRight, 32};
string parsed = parseHexNumber();
rawString += parsed;
- result = applyAlign(alignment, abiType, convertHexNumber(parsed));
+ result = applyAlign(alignment, abiType, BytesUtils().convertHexNumber(parsed));
}
else if (accept(Token::Hex, true))
{
@@ -305,7 +312,7 @@ tuple TestFileParser::parseABITypeLiteral()
throw Error(Error::Type::ParserError, "Hex string literals cannot be aligned or padded.");
string parsed = parseString();
rawString += "hex\"" + parsed + "\"";
- result = convertHexNumber(parsed);
+ result = BytesUtils().convertHexNumber(parsed);
abiType = ABIType{ABIType::HexString, ABIType::AlignNone, result.size()};
}
else if (accept(Token::String))
@@ -317,7 +324,7 @@ tuple TestFileParser::parseABITypeLiteral()
abiType = ABIType{ABIType::String, ABIType::AlignLeft, 32};
string parsed = parseString();
rawString += "\"" + parsed + "\"";
- result = applyAlign(DeclaredAlignment::Left, abiType, convertString(parsed));
+ result = applyAlign(DeclaredAlignment::Left, abiType, BytesUtils().convertString(parsed));
}
else if (accept(Token::Number))
{
@@ -327,7 +334,7 @@ tuple TestFileParser::parseABITypeLiteral()
rawString += parsed;
if (isSigned)
parsed = "-" + parsed;
- result = applyAlign(alignment, abiType, convertNumber(parsed));
+ result = applyAlign(alignment, abiType, BytesUtils().convertNumber(parsed));
}
else if (accept(Token::Failure, true))
{
@@ -426,55 +433,6 @@ string TestFileParser::parseString()
return literal;
}
-bytes TestFileParser::convertBoolean(string const& _literal)
-{
- if (_literal == "true")
- return bytes{true};
- else if (_literal == "false")
- return bytes{false};
- else
- throw Error(Error::Type::ParserError, "Boolean literal invalid.");
-}
-
-bytes TestFileParser::convertNumber(string const& _literal)
-{
- try
- {
- return toCompactBigEndian(u256{_literal});
- }
- catch (std::exception const&)
- {
- throw Error(Error::Type::ParserError, "Number encoding invalid.");
- }
-}
-
-bytes TestFileParser::convertHexNumber(string const& _literal)
-{
- try
- {
- if (_literal.size() % 2)
- throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
- else
- return fromHex(_literal);
- }
- catch (std::exception const&)
- {
- throw Error(Error::Type::ParserError, "Hex number encoding invalid.");
- }
-}
-
-bytes TestFileParser::convertString(string const& _literal)
-{
- try
- {
- return asBytes(_literal);
- }
- catch (std::exception const&)
- {
- throw Error(Error::Type::ParserError, "String encoding invalid.");
- }
-}
-
void TestFileParser::Scanner::readStream(istream& _stream)
{
std::string line;
diff --git a/test/libsolidity/util/TestFileParser.h b/test/libsolidity/util/TestFileParser.h
index ea948a9b7..2eb2b793f 100644
--- a/test/libsolidity/util/TestFileParser.h
+++ b/test/libsolidity/util/TestFileParser.h
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
@@ -33,226 +34,6 @@ namespace solidity
namespace test
{
-/**
- * All soltest tokens.
- */
-#define SOLT_TOKEN_LIST(T, K) \
- T(Unknown, "unknown", 0) \
- T(Invalid, "invalid", 0) \
- T(EOS, "EOS", 0) \
- T(Whitespace, "_", 0) \
- /* punctuations */ \
- T(LParen, "(", 0) \
- T(RParen, ")", 0) \
- T(LBrack, "[", 0) \
- T(RBrack, "]", 0) \
- T(LBrace, "{", 0) \
- T(RBrace, "}", 0) \
- T(Sub, "-", 0) \
- T(Colon, ":", 0) \
- T(Comma, ",", 0) \
- T(Period, ".", 0) \
- T(Arrow, "->", 0) \
- T(Newline, "//", 0) \
- /* Literals & identifier */ \
- T(Comment, "#", 0) \
- T(Number, "number", 0) \
- T(HexNumber, "hex_number", 0) \
- T(String, "string", 0) \
- T(Identifier, "identifier", 0) \
- /* type keywords */ \
- K(Ether, "ether", 0) \
- K(Hex, "hex", 0) \
- K(Boolean, "boolean", 0) \
- /* special keywords */ \
- K(Left, "left", 0) \
- K(Right, "right", 0) \
- K(Failure, "FAILURE", 0) \
-
-namespace soltest
-{
- enum class Token : unsigned int {
- #define T(name, string, precedence) name,
- SOLT_TOKEN_LIST(T, T)
- NUM_TOKENS
- #undef T
- };
-
- /// Prints a friendly string representation of \param _token.
- inline std::string formatToken(Token _token)
- {
- switch (_token)
- {
- #define T(name, string, precedence) case Token::name: return string;
- SOLT_TOKEN_LIST(T, T)
- #undef T
- default: // Token::NUM_TOKENS:
- return "";
- }
- }
-}
-
-
-
-/**
- * The purpose of the ABI type is the storage of type information
- * retrieved while parsing a test. This information is used
- * for the conversion of human-readable function arguments and
- * return values to `bytes` and vice-versa.
- * Defaults to None, a 0-byte representation. 0-bytes
- * can also be interpreted as Failure, which means
- * either a REVERT or another EVM failure.
- */
-struct ABIType
-{
- enum Type
- {
- None,
- Failure,
- Boolean,
- UnsignedDec,
- SignedDec,
- Hex,
- HexString,
- String
- };
- enum Align
- {
- AlignLeft,
- AlignRight,
- AlignNone,
- };
- Type type = ABIType::None;
- Align align = ABIType::AlignRight;
- size_t size = 0;
- bool alignDeclared = false;
-};
-
-/**
- * Helper that can hold format information retrieved
- * while scanning through a parameter list in soltest.
- */
-struct FormatInfo
-{
- bool newline = false;
-};
-
-/**
- * Parameter abstraction used for the encoding and decoding of
- * function parameter and expectation / return value lists.
- * A parameter list is usually a comma-separated list of literals.
- * It should not be possible to call create a parameter holding
- * an identifier, but if so, the ABI type would be invalid.
- */
-struct Parameter
-{
- /// ABI encoded / decoded `bytes` of values.
- /// These `bytes` are used to pass values to function calls
- /// and also to store expected return vales. These are
- /// compared to the actual result of a function call
- /// and used for validating it.
- bytes rawBytes;
- /// Stores the raw string representation of this parameter.
- /// Used to print the unformatted arguments of a function call.
- std::string rawString;
- /// Types that were used to encode `rawBytes`. Expectations
- /// are usually comma separated literals. Their type is auto-
- /// detected and retained in order to format them later on.
- ABIType abiType;
- /// Format info attached to the parameter. It handles newlines given
- /// in the declaration of it.
- FormatInfo format;
-};
-using ParameterList = std::vector;
-
-/**
- * Represents the expected result of a function call after it has been executed. This may be a single
- * return value or a comma-separated list of return values. It also contains the detected input
- * formats used to convert the values to `bytes` needed for the comparison with the actual result
- * of a call. In addition to that, it also stores the expected transaction status.
- * An optional comment can be assigned.
- */
-struct FunctionCallExpectations
-{
- /// Representation of the comma-separated (or empty) list of expected result values
- /// attached to the function call object. It is checked against the actual result of
- /// a function call when used in test framework.
- ParameterList result;
- /// Expected status of the transaction. It can be either
- /// a REVERT or a different EVM failure (e.g. out-of-gas).
- bool failure = true;
- /// A Comment that can be attached to the expectations,
- /// that is retained and can be displayed.
- std::string comment;
- /// ABI encoded `bytes` of parsed expected return values. It is checked
- /// against the actual result of a function call when used in test framework.
- bytes rawBytes() const
- {
- bytes raw;
- for (auto const& param: result)
- raw += param.rawBytes;
- return raw;
- }
-};
-
-/**
- * Represents the arguments passed to a function call. This can be a single
- * argument or a comma-separated list of arguments. It also contains the detected input
- * formats used to convert the arguments to `bytes` needed for the call.
- * An optional comment can be assigned.
- */
-struct FunctionCallArgs
-{
- /// Types that were used to encode `rawBytes`. Parameters
- /// are usually comma separated literals. Their type is auto-
- /// detected and retained in order to format them later on.
- ParameterList parameters;
- /// A Comment that can be attached to the expectations,
- /// that is retained and can be displayed.
- std::string comment;
- /// ABI encoded `bytes` of parsed parameters. These `bytes`
- /// passed to the function call.
- bytes rawBytes() const
- {
- bytes raw;
- for (auto const& param: parameters)
- raw += param.rawBytes;
- return raw;
- }
-};
-
-/**
- * Represents a function call read from an input stream. It contains the signature, the
- * arguments, an optional ether value and an expected execution result.
- */
-struct FunctionCall
-{
- /// Signature of the function call, e.g. `f(uint256, uint256)`.
- std::string signature;
- /// Optional `ether` value that can be send with the call.
- u256 value;
- /// Object that holds all function parameters in their `bytes`
- /// representations given by the contract ABI.
- FunctionCallArgs arguments;
- /// Object that holds all function call expectation in
- /// their `bytes` representations given by the contract ABI.
- /// They are checked against the actual results and their
- /// `bytes` representation, as well as the transaction status.
- FunctionCallExpectations expectations;
- /// single / multi-line mode will be detected as follows:
- /// every newline (//) in source results in a function call
- /// that has its display mode set to multi-mode. Function and
- /// result parameter lists are an exception: a single parameter
- /// stores a format information that contains a newline definition.
- enum DisplayMode {
- SingleLine,
- MultiLine
- };
- DisplayMode displayMode = DisplayMode::SingleLine;
- /// Marks this function call as the constructor.
- bool isConstructor = false;
-};
-
/**
* Class that is able to parse an additional and well-formed comment section in a Solidity
* source file used by the file-based unit test environment. For now, it parses function
@@ -398,22 +179,6 @@ private:
/// Parses the current string literal.
std::string parseString();
- /// Tries to convert \param _literal to an unpadded `bytes`
- /// representation of the boolean number literal. Throws if conversion fails.
- bytes convertBoolean(std::string const& _literal);
-
- /// Tries to convert \param _literal to an unpadded `bytes`
- /// representation of the decimal number literal. Throws if conversion fails.
- bytes convertNumber(std::string const& _literal);
-
- /// Tries to convert \param _literal to an unpadded `bytes`
- /// representation of the hex literal. Throws if conversion fails.
- bytes convertHexNumber(std::string const& _literal);
-
- /// Tries to convert \param _literal to an unpadded `bytes`
- /// representation of the string literal. Throws if conversion fails.
- bytes convertString(std::string const& _literal);
-
/// A scanner instance
Scanner m_scanner;
};
diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp
index 15c4d50bb..4b9e39c19 100644
--- a/test/libsolidity/util/TestFunctionCall.cpp
+++ b/test/libsolidity/util/TestFunctionCall.cpp
@@ -14,11 +14,13 @@
#include
+#include
+#include
+
#include
#include
-#include
#include
#include
@@ -27,47 +29,6 @@ using namespace solidity;
using namespace dev::solidity::test;
using namespace std;
-namespace
-{
-
-static regex s_boolType{"(bool)"};
-static regex s_uintType{"(uint\\d*)"};
-static regex s_intType{"(int\\d*)"};
-static regex s_bytesType{"(bytes\\d+)"};
-static regex s_dynBytesType{"(\\bbytes\\b)"};
-static regex s_stringType{"(string)"};
-
-/// Translates Solidity's ABI types into the internal type representation of
-/// soltest.
-auto contractABITypes(string const& _type) -> vector
-{
- vector abiTypes;
- if (regex_match(_type, s_boolType))
- abiTypes.push_back(ABIType{ABIType::Boolean, ABIType::AlignRight, 32});
- else if (regex_match(_type, s_uintType))
- abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
- 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))
- {
- abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
- abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
- abiTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft, 32});
- }
- else
- abiTypes.push_back(ABIType{ABIType::None, ABIType::AlignRight, 0});
- return abiTypes;
-};
-}
-
string TestFunctionCall::format(
ErrorReporter& _errorReporter,
string const& _linePrefix,
@@ -192,21 +153,9 @@ string TestFunctionCall::formatBytesParameters(
stringstream os;
string functionName{_signature.substr(0, _signature.find("("))};
- 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);
-
- /// Infer type from Contract ABI. Used to generate values for
+ /// Create parameters from Contract ABI. Used to generate values for
/// auto-correction during interactive update routine.
- ParameterList abiParams;
- for (auto const& function: m_contractABI)
- if (function["name"] == functionName)
- for (auto const& output: function["outputs"])
- {
- auto types = contractABITypes(output["type"].asString());
- for (auto const& type: types)
- abiParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
- }
-
+ 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
@@ -214,6 +163,9 @@ string TestFunctionCall::formatBytesParameters(
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 " +
@@ -294,57 +246,23 @@ string TestFunctionCall::formatBytesRange(
// 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 << u2s(fromBigEndian(_bytes));
- else
- os << fromBigEndian(_bytes);
+ os << BytesUtils().formatUnsigned(_bytes);
break;
case ABIType::SignedDec:
- if (*_bytes.begin() & 0x80)
- os << u2s(fromBigEndian(_bytes));
- else
- os << fromBigEndian(_bytes);
+ os << BytesUtils().formatSigned(_bytes);
break;
case ABIType::Boolean:
- {
- u256 result = fromBigEndian(_bytes);
- if (result == 0)
- os << "false";
- else if (result == 1)
- os << "true";
- else
- os << result;
+ os << BytesUtils().formatBoolean(_bytes);
break;
- }
case ABIType::Hex:
- {
- string hex{toHex(_bytes, HexPrefix::Add)};
- boost::algorithm::replace_all(hex, "00", "");
- os << hex;
+ os << BytesUtils().formatHex(_bytes);
break;
- }
case ABIType::HexString:
- os << "hex\"" << toHex(_bytes) << "\"";
+ os << BytesUtils().formatHexString(_bytes);
break;
case ABIType::String:
- {
- os << "\"";
- bool expectZeros = false;
- for (auto const& v: _bytes)
- {
- if (expectZeros && v != 0)
- return {};
- if (v == 0) expectZeros = true;
- else
- {
- if (!isprint(v) || v == '"')
- return {};
- os << v;
- }
- }
- os << "\"";
+ os << BytesUtils().formatString(_bytes);
break;
- }
case ABIType::Failure:
break;
case ABIType::None:
diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h
index 1d8bc9e98..f56abe93d 100644
--- a/test/libsolidity/util/TestFunctionCall.h
+++ b/test/libsolidity/util/TestFunctionCall.h
@@ -199,6 +199,14 @@ private:
std::string const& _linePrefix = ""
) const;
+ /// 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 choosePreferredParameters(
+ ParameterList const& _parsedParamters,
+ ParameterList const& _abiParameters
+ ) const;
+
/// Compares raw expectations (which are converted to a byte representation before),
/// and also the expected transaction status of the function call to the actual test results.
bool matchesExpectation() const;
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index b9e72d27b..cafb5f3f6 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -16,6 +16,8 @@ add_executable(isoltest
../Options.cpp
../Common.cpp
../TestCase.cpp
+ ../libsolidity/util/BytesUtils.cpp
+ ../libsolidity/util/ContractABIUtils.cpp
../libsolidity/util/TestFileParser.cpp
../libsolidity/util/TestFunctionCall.cpp
../libsolidity/GasTest.cpp