mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
409 lines
13 KiB
C++
409 lines
13 KiB
C++
/*
|
|
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/CommonData.h>
|
|
#include <libsolidity/ast/Types.h>
|
|
#include <liblangutil/Exceptions.h>
|
|
|
|
#include <iosfwd>
|
|
#include <numeric>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <utility>
|
|
|
|
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<Parameter>;
|
|
|
|
/**
|
|
* 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
|
|
* calls and their expected result after the call was made.
|
|
*
|
|
* - Function calls defined in blocks:
|
|
* // f(uint256, uint256): 1, 1 # Signature and comma-separated list of arguments #
|
|
* // -> 1, 1 # Expected result value #
|
|
* // g(), 2 ether # (Optional) Ether to be send with the call #
|
|
* // -> 2, 3
|
|
* // h(uint256), 1 ether: 42
|
|
* // -> FAILURE # If REVERT or other EVM failure was detected #
|
|
* ...
|
|
*/
|
|
class TestFileParser
|
|
{
|
|
public:
|
|
/// Constructor that takes an input stream \param _stream to operate on
|
|
/// and creates the internal scanner.
|
|
TestFileParser(std::istream& _stream): m_scanner(_stream) {}
|
|
|
|
/// Parses function calls blockwise and returns a list of function calls found.
|
|
/// Throws an exception if a function call cannot be parsed because of its
|
|
/// incorrect structure, an invalid or unsupported encoding
|
|
/// of its arguments or expected results.
|
|
std::vector<FunctionCall> parseFunctionCalls();
|
|
|
|
private:
|
|
using Token = soltest::Token;
|
|
/**
|
|
* Token scanner that is used internally to abstract away character traversal.
|
|
*/
|
|
class Scanner
|
|
{
|
|
public:
|
|
/// Constructor that takes an input stream \param _stream to operate on.
|
|
/// It reads all lines into one single line, keeping the newlines.
|
|
Scanner(std::istream& _stream) { readStream(_stream); }
|
|
|
|
/// Reads input stream into a single line and resets the current iterator.
|
|
void readStream(std::istream& _stream);
|
|
|
|
/// Reads character stream and creates token.
|
|
void scanNextToken();
|
|
|
|
soltest::Token currentToken() { return m_currentToken.first; }
|
|
std::string currentLiteral() { return m_currentToken.second; }
|
|
|
|
std::string scanComment();
|
|
std::string scanIdentifierOrKeyword();
|
|
std::string scanDecimalNumber();
|
|
std::string scanHexNumber();
|
|
std::string scanString();
|
|
|
|
private:
|
|
using TokenDesc = std::pair<Token, std::string>;
|
|
|
|
/// Advances current position in the input stream.
|
|
void advance() { ++m_char; }
|
|
/// Returns the current character.
|
|
char current() const { return *m_char; }
|
|
/// Peeks the next character.
|
|
char peek() const { auto it = m_char; return *(it + 1); }
|
|
/// Returns true if the end of a line is reached, false otherwise.
|
|
bool isEndOfLine() const { return m_char == m_line.end(); }
|
|
|
|
std::string m_line;
|
|
std::string::iterator m_char;
|
|
|
|
std::string m_currentLiteral;
|
|
|
|
TokenDesc m_currentToken;
|
|
};
|
|
|
|
bool accept(soltest::Token _token, bool const _expect = false);
|
|
bool expect(soltest::Token _token, bool const _advance = true);
|
|
|
|
/// Parses a function call signature in the form of f(uint256, ...).
|
|
std::string parseFunctionSignature();
|
|
|
|
/// Parses the optional ether value that can be passed alongside the
|
|
/// function call arguments. Throws an InvalidEtherValueEncoding exception
|
|
/// if given value cannot be converted to `u256`.
|
|
u256 parseFunctionCallValue();
|
|
|
|
/// Parses a comma-separated list of arguments passed with a function call.
|
|
/// Does not check for a potential mismatch between the signature and the number
|
|
/// or types of arguments.
|
|
FunctionCallArgs parseFunctionCallArguments();
|
|
|
|
/// Parses the expected result of a function call execution.
|
|
FunctionCallExpectations parseFunctionCallExpectations();
|
|
|
|
/// Parses the next parameter in a comma separated list.
|
|
/// Takes a newly parsed, and type-annotated `bytes` argument,
|
|
/// appends it to the internal `bytes` buffer of the parameter. It can also
|
|
/// store newlines found in the source, that are needed to
|
|
/// format input and output of the interactive update.
|
|
Parameter parseParameter();
|
|
|
|
/// Parses and converts the current literal to its byte representation and
|
|
/// preserves the chosen ABI type, as well as a raw, unformatted string representation
|
|
/// of this literal.
|
|
/// Based on the type information retrieved, the driver of this parser may format arguments,
|
|
/// expectations and results. Supported types:
|
|
/// - unsigned and signed decimal number literals.
|
|
/// Returns invalid ABI type for empty literal. This is needed in order
|
|
/// to detect empty expectations. Throws a ParserError if data is encoded incorrectly or
|
|
/// if data type is not supported.
|
|
std::tuple<bytes, ABIType, std::string> parseABITypeLiteral();
|
|
|
|
/// Recursively parses an identifier or a tuple definition that contains identifiers
|
|
/// and / or parentheses like `((uint, uint), (uint, (uint, uint)), uint)`.
|
|
std::string parseIdentifierOrTuple();
|
|
|
|
/// Parses a boolean literal.
|
|
std::string parseBoolean();
|
|
|
|
/// Parses a comment that is defined like this:
|
|
/// # A nice comment. #
|
|
std::string parseComment();
|
|
|
|
/// Parses the current decimal number literal.
|
|
std::string parseDecimalNumber();
|
|
|
|
/// Parses the current hex number literal.
|
|
std::string parseHexNumber();
|
|
|
|
/// 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;
|
|
};
|
|
|
|
}
|
|
}
|
|
}
|