mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[isoltest] Add support for builtin functions.
This commit is contained in:
parent
be5647735e
commit
94895822d2
@ -13,9 +13,12 @@
|
||||
*/
|
||||
|
||||
#include <test/libsolidity/SemanticTest.h>
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <test/Common.h>
|
||||
#include <test/libsolidity/util/BytesUtils.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
@ -25,7 +28,9 @@
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -119,7 +124,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
|
||||
return result;
|
||||
}
|
||||
|
||||
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
|
||||
TestCase::TestResult SemanticTest::runTest(
|
||||
ostream& _stream,
|
||||
string const& _linePrefix,
|
||||
bool _formatted,
|
||||
bool _compileViaYul,
|
||||
bool _compileToEwasm
|
||||
)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
@ -142,21 +153,25 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
if (_compileViaYul)
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
for (TestFunctionCall& test: m_tests)
|
||||
test.reset();
|
||||
|
||||
map<string, solidity::test::Address> libraries;
|
||||
|
||||
bool constructed = false;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
for (TestFunctionCall& test: m_tests)
|
||||
{
|
||||
if (constructed)
|
||||
{
|
||||
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
|
||||
soltestAssert(
|
||||
test.call().kind != FunctionCall::Kind::Library,
|
||||
"Libraries have to be deployed before any other call."
|
||||
);
|
||||
soltestAssert(
|
||||
test.call().kind != FunctionCall::Kind::Constructor,
|
||||
"Constructor has to be the first function call expect for library deployments.");
|
||||
"Constructor has to be the first function call expect for library deployments."
|
||||
);
|
||||
}
|
||||
else if (test.call().kind == FunctionCall::Kind::Library)
|
||||
{
|
||||
@ -197,6 +212,17 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
bytes output;
|
||||
if (test.call().kind == FunctionCall::Kind::LowLevel)
|
||||
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
|
||||
else if (test.call().kind == FunctionCall::Kind::Builtin)
|
||||
{
|
||||
std::optional<bytes> builtinOutput = m_builtins.at(test.call().signature)(test.call());
|
||||
if (builtinOutput.has_value())
|
||||
{
|
||||
m_transactionSuccessful = true;
|
||||
output = builtinOutput.value();
|
||||
}
|
||||
else
|
||||
m_transactionSuccessful = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
soltestAssert(
|
||||
@ -241,7 +267,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
if (!success && (m_runWithYul || !_compileViaYul))
|
||||
{
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
|
||||
for (auto const& test: m_tests)
|
||||
for (TestFunctionCall const& test: m_tests)
|
||||
{
|
||||
ErrorReporter errorReporter;
|
||||
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
|
||||
@ -249,7 +275,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
}
|
||||
_stream << endl;
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
||||
for (auto const& test: m_tests)
|
||||
for (TestFunctionCall const& test: m_tests)
|
||||
{
|
||||
ErrorReporter errorReporter;
|
||||
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
|
||||
@ -320,7 +346,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
|
||||
|
||||
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
|
||||
{
|
||||
for (auto const& test: m_tests)
|
||||
for (TestFunctionCall const& test: m_tests)
|
||||
_stream << test.format("", true, false) << endl;
|
||||
}
|
||||
|
||||
@ -340,12 +366,16 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
|
||||
|
||||
void SemanticTest::parseExpectations(istream& _stream)
|
||||
{
|
||||
TestFileParser parser{_stream};
|
||||
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
|
||||
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
|
||||
TestFileParser parser{_stream, m_builtins};
|
||||
m_tests += parser.parseFunctionCalls(m_lineOffset);
|
||||
}
|
||||
|
||||
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
|
||||
bool SemanticTest::deploy(
|
||||
string const& _contractName,
|
||||
u256 const& _value,
|
||||
bytes const& _arguments,
|
||||
map<string, solidity::test::Address> const& _libraries
|
||||
)
|
||||
{
|
||||
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
|
||||
return !output.empty() && m_transactionSuccessful;
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
/// Compiles and deploys currently held source.
|
||||
/// Returns true if deployment was successful, false otherwise.
|
||||
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
|
||||
|
||||
private:
|
||||
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
|
||||
SourceMap m_sources;
|
||||
@ -70,6 +71,7 @@ private:
|
||||
bool m_runWithABIEncoderV1Only = false;
|
||||
bool m_allowNonExistingFunctions = false;
|
||||
bool m_compileViaYulCanBeSet = false;
|
||||
std::map<std::string, Builtin> m_builtins{};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <libsolutil/AnsiColorized.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <test/ExecutionFramework.h>
|
||||
|
||||
namespace solidity::frontend::test
|
||||
{
|
||||
|
||||
@ -174,6 +176,8 @@ struct Parameter
|
||||
};
|
||||
using ParameterList = std::vector<Parameter>;
|
||||
|
||||
struct FunctionCall;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -193,6 +197,7 @@ struct FunctionCallExpectations
|
||||
/// 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
|
||||
@ -286,7 +291,9 @@ struct FunctionCall
|
||||
/// Marks a library deployment call.
|
||||
Library,
|
||||
/// Check that the storage of the current contract is empty or non-empty.
|
||||
Storage
|
||||
Storage,
|
||||
/// Call to a builtin.
|
||||
Builtin
|
||||
};
|
||||
Kind kind = Kind::Regular;
|
||||
/// Marks this function call as "short-handed", meaning
|
||||
@ -294,4 +301,6 @@ struct FunctionCall
|
||||
bool omitsArrow = true;
|
||||
};
|
||||
|
||||
using Builtin = std::function<std::optional<bytes>(FunctionCall const&)>;
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
|
||||
vector<FunctionCall> calls;
|
||||
if (!accept(Token::EOS))
|
||||
{
|
||||
assert(m_scanner.currentToken() == Token::Unknown);
|
||||
soltestAssert(m_scanner.currentToken() == Token::Unknown, "");
|
||||
m_scanner.scanNextToken();
|
||||
|
||||
while (!accept(Token::EOS))
|
||||
@ -106,6 +106,8 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
|
||||
tie(call.signature, lowLevelCall) = parseFunctionSignature();
|
||||
if (lowLevelCall)
|
||||
call.kind = FunctionCall::Kind::LowLevel;
|
||||
else if (isBuiltinFunction(call.signature))
|
||||
call.kind = FunctionCall::Kind::Builtin;
|
||||
|
||||
if (accept(Token::Comma, true))
|
||||
call.value = parseFunctionCallValue();
|
||||
@ -195,6 +197,9 @@ pair<string, bool> TestFileParser::parseFunctionSignature()
|
||||
expect(Token::Identifier);
|
||||
}
|
||||
|
||||
if (isBuiltinFunction(signature))
|
||||
return {signature, false};
|
||||
|
||||
signature += formatToken(Token::LParen);
|
||||
expect(Token::LParen);
|
||||
|
||||
@ -488,7 +493,7 @@ void TestFileParser::Scanner::readStream(istream& _stream)
|
||||
void TestFileParser::Scanner::scanNextToken()
|
||||
{
|
||||
// Make code coverage happy.
|
||||
assert(formatToken(Token::NUM_TOKENS) == "");
|
||||
soltestAssert(formatToken(Token::NUM_TOKENS).empty(), "");
|
||||
|
||||
auto detectKeyword = [](std::string const& _literal = "") -> std::pair<Token, std::string> {
|
||||
if (_literal == "true") return {Token::Boolean, "true"};
|
||||
@ -712,3 +717,8 @@ char TestFileParser::Scanner::scanHexPart()
|
||||
|
||||
return static_cast<char>(value);
|
||||
}
|
||||
|
||||
bool TestFileParser::isBuiltinFunction(std::string const& signature)
|
||||
{
|
||||
return m_builtins.count(signature) > 0;
|
||||
}
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <test/libsolidity/util/SoltestTypes.h>
|
||||
|
||||
#include <iosfwd>
|
||||
@ -52,7 +52,7 @@ 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) {}
|
||||
explicit TestFileParser(std::istream& _stream, std::map<std::string, Builtin> const& _builtins): m_scanner(_stream), m_builtins(_builtins) {}
|
||||
|
||||
/// 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
|
||||
@ -177,12 +177,18 @@ private:
|
||||
/// Parses the current string literal.
|
||||
std::string parseString();
|
||||
|
||||
/// Checks whether a builtin function with the given signature exist.
|
||||
/// @returns true, if builtin found, false otherwise
|
||||
bool isBuiltinFunction(std::string const& signature);
|
||||
|
||||
/// A scanner instance
|
||||
Scanner m_scanner;
|
||||
|
||||
/// The current line number. Incremented when Token::Newline (//) is found and
|
||||
/// used to enhance parser error messages.
|
||||
size_t m_lineNumber = 0;
|
||||
|
||||
std::map<std::string, Builtin> const& m_builtins;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ namespace
|
||||
vector<FunctionCall> parse(string const& _source)
|
||||
{
|
||||
istringstream stream{_source, ios_base::out};
|
||||
TestFileParser parser{stream};
|
||||
TestFileParser parser{stream, {}};
|
||||
return parser.parseFunctionCalls(0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user