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 <test/libsolidity/SemanticTest.h>
|
||||||
|
|
||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <test/Common.h>
|
#include <test/Common.h>
|
||||||
|
#include <test/libsolidity/util/BytesUtils.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
@ -25,7 +28,9 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -119,7 +124,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
|
|||||||
return result;
|
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;
|
bool success = true;
|
||||||
|
|
||||||
@ -142,21 +153,25 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
|||||||
if (_compileViaYul)
|
if (_compileViaYul)
|
||||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
|
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
|
||||||
|
|
||||||
for (auto& test: m_tests)
|
for (TestFunctionCall& test: m_tests)
|
||||||
test.reset();
|
test.reset();
|
||||||
|
|
||||||
map<string, solidity::test::Address> libraries;
|
map<string, solidity::test::Address> libraries;
|
||||||
|
|
||||||
bool constructed = false;
|
bool constructed = false;
|
||||||
|
|
||||||
for (auto& test: m_tests)
|
for (TestFunctionCall& test: m_tests)
|
||||||
{
|
{
|
||||||
if (constructed)
|
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(
|
soltestAssert(
|
||||||
test.call().kind != FunctionCall::Kind::Constructor,
|
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)
|
else if (test.call().kind == FunctionCall::Kind::Library)
|
||||||
{
|
{
|
||||||
@ -197,6 +212,17 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
|||||||
bytes output;
|
bytes output;
|
||||||
if (test.call().kind == FunctionCall::Kind::LowLevel)
|
if (test.call().kind == FunctionCall::Kind::LowLevel)
|
||||||
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
|
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
|
else
|
||||||
{
|
{
|
||||||
soltestAssert(
|
soltestAssert(
|
||||||
@ -241,7 +267,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
|||||||
if (!success && (m_runWithYul || !_compileViaYul))
|
if (!success && (m_runWithYul || !_compileViaYul))
|
||||||
{
|
{
|
||||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
|
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
|
||||||
for (auto const& test: m_tests)
|
for (TestFunctionCall const& test: m_tests)
|
||||||
{
|
{
|
||||||
ErrorReporter errorReporter;
|
ErrorReporter errorReporter;
|
||||||
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
|
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
|
||||||
@ -249,7 +275,7 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
|||||||
}
|
}
|
||||||
_stream << endl;
|
_stream << endl;
|
||||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
||||||
for (auto const& test: m_tests)
|
for (TestFunctionCall const& test: m_tests)
|
||||||
{
|
{
|
||||||
ErrorReporter errorReporter;
|
ErrorReporter errorReporter;
|
||||||
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
|
_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
|
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;
|
_stream << test.format("", true, false) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,12 +366,16 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
|
|||||||
|
|
||||||
void SemanticTest::parseExpectations(istream& _stream)
|
void SemanticTest::parseExpectations(istream& _stream)
|
||||||
{
|
{
|
||||||
TestFileParser parser{_stream};
|
TestFileParser parser{_stream, m_builtins};
|
||||||
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
|
m_tests += parser.parseFunctionCalls(m_lineOffset);
|
||||||
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
|
||||||
return !output.empty() && m_transactionSuccessful;
|
return !output.empty() && m_transactionSuccessful;
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
/// Compiles and deploys currently held source.
|
/// Compiles and deploys currently held source.
|
||||||
/// Returns true if deployment was successful, false otherwise.
|
/// 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 = {});
|
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
|
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
|
||||||
SourceMap m_sources;
|
SourceMap m_sources;
|
||||||
@ -70,6 +71,7 @@ private:
|
|||||||
bool m_runWithABIEncoderV1Only = false;
|
bool m_runWithABIEncoderV1Only = false;
|
||||||
bool m_allowNonExistingFunctions = false;
|
bool m_allowNonExistingFunctions = false;
|
||||||
bool m_compileViaYulCanBeSet = false;
|
bool m_compileViaYulCanBeSet = false;
|
||||||
|
std::map<std::string, Builtin> m_builtins{};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#include <libsolutil/AnsiColorized.h>
|
#include <libsolutil/AnsiColorized.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
#include <test/ExecutionFramework.h>
|
||||||
|
|
||||||
namespace solidity::frontend::test
|
namespace solidity::frontend::test
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -174,6 +176,8 @@ struct Parameter
|
|||||||
};
|
};
|
||||||
using ParameterList = std::vector<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
|
* 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
|
* 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,
|
/// A Comment that can be attached to the expectations,
|
||||||
/// that is retained and can be displayed.
|
/// that is retained and can be displayed.
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
|
||||||
/// ABI encoded `bytes` of parsed expected return values. It is checked
|
/// ABI encoded `bytes` of parsed expected return values. It is checked
|
||||||
/// against the actual result of a function call when used in test framework.
|
/// against the actual result of a function call when used in test framework.
|
||||||
bytes rawBytes() const
|
bytes rawBytes() const
|
||||||
@ -286,7 +291,9 @@ struct FunctionCall
|
|||||||
/// Marks a library deployment call.
|
/// Marks a library deployment call.
|
||||||
Library,
|
Library,
|
||||||
/// Check that the storage of the current contract is empty or non-empty.
|
/// Check that the storage of the current contract is empty or non-empty.
|
||||||
Storage
|
Storage,
|
||||||
|
/// Call to a builtin.
|
||||||
|
Builtin
|
||||||
};
|
};
|
||||||
Kind kind = Kind::Regular;
|
Kind kind = Kind::Regular;
|
||||||
/// Marks this function call as "short-handed", meaning
|
/// Marks this function call as "short-handed", meaning
|
||||||
@ -294,4 +301,6 @@ struct FunctionCall
|
|||||||
bool omitsArrow = true;
|
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;
|
vector<FunctionCall> calls;
|
||||||
if (!accept(Token::EOS))
|
if (!accept(Token::EOS))
|
||||||
{
|
{
|
||||||
assert(m_scanner.currentToken() == Token::Unknown);
|
soltestAssert(m_scanner.currentToken() == Token::Unknown, "");
|
||||||
m_scanner.scanNextToken();
|
m_scanner.scanNextToken();
|
||||||
|
|
||||||
while (!accept(Token::EOS))
|
while (!accept(Token::EOS))
|
||||||
@ -106,6 +106,8 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
|
|||||||
tie(call.signature, lowLevelCall) = parseFunctionSignature();
|
tie(call.signature, lowLevelCall) = parseFunctionSignature();
|
||||||
if (lowLevelCall)
|
if (lowLevelCall)
|
||||||
call.kind = FunctionCall::Kind::LowLevel;
|
call.kind = FunctionCall::Kind::LowLevel;
|
||||||
|
else if (isBuiltinFunction(call.signature))
|
||||||
|
call.kind = FunctionCall::Kind::Builtin;
|
||||||
|
|
||||||
if (accept(Token::Comma, true))
|
if (accept(Token::Comma, true))
|
||||||
call.value = parseFunctionCallValue();
|
call.value = parseFunctionCallValue();
|
||||||
@ -195,6 +197,9 @@ pair<string, bool> TestFileParser::parseFunctionSignature()
|
|||||||
expect(Token::Identifier);
|
expect(Token::Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isBuiltinFunction(signature))
|
||||||
|
return {signature, false};
|
||||||
|
|
||||||
signature += formatToken(Token::LParen);
|
signature += formatToken(Token::LParen);
|
||||||
expect(Token::LParen);
|
expect(Token::LParen);
|
||||||
|
|
||||||
@ -488,7 +493,7 @@ void TestFileParser::Scanner::readStream(istream& _stream)
|
|||||||
void TestFileParser::Scanner::scanNextToken()
|
void TestFileParser::Scanner::scanNextToken()
|
||||||
{
|
{
|
||||||
// Make code coverage happy.
|
// 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> {
|
auto detectKeyword = [](std::string const& _literal = "") -> std::pair<Token, std::string> {
|
||||||
if (_literal == "true") return {Token::Boolean, "true"};
|
if (_literal == "true") return {Token::Boolean, "true"};
|
||||||
@ -712,3 +717,8 @@ char TestFileParser::Scanner::scanHexPart()
|
|||||||
|
|
||||||
return static_cast<char>(value);
|
return static_cast<char>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TestFileParser::isBuiltinFunction(std::string const& signature)
|
||||||
|
{
|
||||||
|
return m_builtins.count(signature) > 0;
|
||||||
|
}
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
#include <test/libsolidity/util/SoltestTypes.h>
|
#include <test/libsolidity/util/SoltestTypes.h>
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
@ -52,7 +52,7 @@ class TestFileParser
|
|||||||
public:
|
public:
|
||||||
/// Constructor that takes an input stream \param _stream to operate on
|
/// Constructor that takes an input stream \param _stream to operate on
|
||||||
/// and creates the internal scanner.
|
/// 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.
|
/// 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
|
/// Throws an exception if a function call cannot be parsed because of its
|
||||||
@ -177,12 +177,18 @@ private:
|
|||||||
/// Parses the current string literal.
|
/// Parses the current string literal.
|
||||||
std::string parseString();
|
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
|
/// A scanner instance
|
||||||
Scanner m_scanner;
|
Scanner m_scanner;
|
||||||
|
|
||||||
/// The current line number. Incremented when Token::Newline (//) is found and
|
/// The current line number. Incremented when Token::Newline (//) is found and
|
||||||
/// used to enhance parser error messages.
|
/// used to enhance parser error messages.
|
||||||
size_t m_lineNumber = 0;
|
size_t m_lineNumber = 0;
|
||||||
|
|
||||||
|
std::map<std::string, Builtin> const& m_builtins;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace
|
|||||||
vector<FunctionCall> parse(string const& _source)
|
vector<FunctionCall> parse(string const& _source)
|
||||||
{
|
{
|
||||||
istringstream stream{_source, ios_base::out};
|
istringstream stream{_source, ios_base::out};
|
||||||
TestFileParser parser{stream};
|
TestFileParser parser{stream, {}};
|
||||||
return parser.parseFunctionCalls(0);
|
return parser.parseFunctionCalls(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user