Skip to content

Commit 59ddf67

Browse files
committed
[isoltest] Add support for builtin functions.
1 parent be56477 commit 59ddf67

File tree

5 files changed

+74
-17
lines changed

5 files changed

+74
-17
lines changed

test/libsolidity/SemanticTest.cpp

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
*/
1414

1515
#include <test/libsolidity/SemanticTest.h>
16+
1617
#include <libsolutil/Whiskers.h>
1718
#include <libyul/Exceptions.h>
1819
#include <test/Common.h>
20+
#include <test/libsolidity/util/BytesUtils.h>
21+
1922
#include <boost/algorithm/string.hpp>
2023
#include <boost/algorithm/string/predicate.hpp>
2124
#include <boost/algorithm/string/trim.hpp>
@@ -25,7 +28,9 @@
2528
#include <cctype>
2629
#include <fstream>
2730
#include <memory>
31+
#include <optional>
2832
#include <stdexcept>
33+
#include <utility>
2934

3035
using namespace std;
3136
using namespace solidity;
@@ -119,7 +124,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
119124
return result;
120125
}
121126

122-
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
127+
TestCase::TestResult SemanticTest::runTest(
128+
ostream& _stream,
129+
string const& _linePrefix,
130+
bool _formatted,
131+
bool _compileViaYul,
132+
bool _compileToEwasm
133+
)
123134
{
124135
bool success = true;
125136

@@ -142,21 +153,25 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
142153
if (_compileViaYul)
143154
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
144155

145-
for (auto& test: m_tests)
156+
for (TestFunctionCall& test: m_tests)
146157
test.reset();
147158

148159
map<string, solidity::test::Address> libraries;
149160

150161
bool constructed = false;
151162

152-
for (auto& test: m_tests)
163+
for (TestFunctionCall& test: m_tests)
153164
{
154165
if (constructed)
155166
{
156-
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
167+
soltestAssert(
168+
test.call().kind != FunctionCall::Kind::Library,
169+
"Libraries have to be deployed before any other call."
170+
);
157171
soltestAssert(
158172
test.call().kind != FunctionCall::Kind::Constructor,
159-
"Constructor has to be the first function call expect for library deployments.");
173+
"Constructor has to be the first function call expect for library deployments."
174+
);
160175
}
161176
else if (test.call().kind == FunctionCall::Kind::Library)
162177
{
@@ -197,6 +212,17 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197212
bytes output;
198213
if (test.call().kind == FunctionCall::Kind::LowLevel)
199214
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
215+
else if (test.call().kind == FunctionCall::Kind::Builtin)
216+
{
217+
std::optional<bytes> builtinOutput = m_builtins.at(test.call().signature)(test.call());
218+
if (builtinOutput.has_value())
219+
{
220+
m_transactionSuccessful = true;
221+
output = builtinOutput.value();
222+
}
223+
else
224+
m_transactionSuccessful = false;
225+
}
200226
else
201227
{
202228
soltestAssert(
@@ -241,15 +267,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241267
if (!success && (m_runWithYul || !_compileViaYul))
242268
{
243269
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
244-
for (auto const& test: m_tests)
270+
for (TestFunctionCall const& test: m_tests)
245271
{
246272
ErrorReporter errorReporter;
247273
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
248274
_stream << errorReporter.format(_linePrefix, _formatted);
249275
}
250276
_stream << endl;
251277
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
252-
for (auto const& test: m_tests)
278+
for (TestFunctionCall const& test: m_tests)
253279
{
254280
ErrorReporter errorReporter;
255281
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
@@ -320,7 +346,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320346

321347
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
322348
{
323-
for (auto const& test: m_tests)
349+
for (TestFunctionCall const& test: m_tests)
324350
_stream << test.format("", true, false) << endl;
325351
}
326352

@@ -340,12 +366,16 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre
340366

341367
void SemanticTest::parseExpectations(istream& _stream)
342368
{
343-
TestFileParser parser{_stream};
344-
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
345-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
369+
TestFileParser parser{_stream, m_builtins};
370+
m_tests += parser.parseFunctionCalls(m_lineOffset);
346371
}
347372

348-
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
373+
bool SemanticTest::deploy(
374+
string const& _contractName,
375+
u256 const& _value,
376+
bytes const& _arguments,
377+
map<string, solidity::test::Address> const& _libraries
378+
)
349379
{
350380
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
351381
return !output.empty() && m_transactionSuccessful;

test/libsolidity/SemanticTest.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
5858
/// Compiles and deploys currently held source.
5959
/// Returns true if deployment was successful, false otherwise.
6060
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
61+
6162
private:
6263
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
6364
SourceMap m_sources;
@@ -70,6 +71,7 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
7071
bool m_runWithABIEncoderV1Only = false;
7172
bool m_allowNonExistingFunctions = false;
7273
bool m_compileViaYulCanBeSet = false;
74+
std::map<std::string, Builtin> m_builtins{};
7375
};
7476

7577
}

test/libsolidity/util/SoltestTypes.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <libsolutil/AnsiColorized.h>
1818
#include <libsolutil/CommonData.h>
1919

20+
#include <test/ExecutionFramework.h>
21+
2022
namespace solidity::frontend::test
2123
{
2224

@@ -174,6 +176,8 @@ struct Parameter
174176
};
175177
using ParameterList = std::vector<Parameter>;
176178

179+
struct FunctionCall;
180+
177181
/**
178182
* Represents the expected result of a function call after it has been executed. This may be a single
179183
* return value or a comma-separated list of return values. It also contains the detected input
@@ -193,6 +197,7 @@ struct FunctionCallExpectations
193197
/// A Comment that can be attached to the expectations,
194198
/// that is retained and can be displayed.
195199
std::string comment;
200+
196201
/// ABI encoded `bytes` of parsed expected return values. It is checked
197202
/// against the actual result of a function call when used in test framework.
198203
bytes rawBytes() const
@@ -286,12 +291,16 @@ struct FunctionCall
286291
/// Marks a library deployment call.
287292
Library,
288293
/// Check that the storage of the current contract is empty or non-empty.
289-
Storage
294+
Storage,
295+
/// Call to a builtin.
296+
Builtin
290297
};
291298
Kind kind = Kind::Regular;
292299
/// Marks this function call as "short-handed", meaning
293300
/// no `->` declared.
294301
bool omitsArrow = true;
295302
};
296303

304+
using Builtin = std::function<std::optional<bytes>(FunctionCall const&)>;
305+
297306
}

test/libsolidity/util/TestFileParser.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
5555
vector<FunctionCall> calls;
5656
if (!accept(Token::EOS))
5757
{
58-
assert(m_scanner.currentToken() == Token::Unknown);
58+
soltestAssert(m_scanner.currentToken() == Token::Unknown, "");
5959
m_scanner.scanNextToken();
6060

6161
while (!accept(Token::EOS))
@@ -106,6 +106,8 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
106106
tie(call.signature, lowLevelCall) = parseFunctionSignature();
107107
if (lowLevelCall)
108108
call.kind = FunctionCall::Kind::LowLevel;
109+
else if (isBuiltinFunction(call.signature))
110+
call.kind = FunctionCall::Kind::Builtin;
109111

110112
if (accept(Token::Comma, true))
111113
call.value = parseFunctionCallValue();
@@ -195,6 +197,9 @@ pair<string, bool> TestFileParser::parseFunctionSignature()
195197
expect(Token::Identifier);
196198
}
197199

200+
if (isBuiltinFunction(signature))
201+
return {signature, false};
202+
198203
signature += formatToken(Token::LParen);
199204
expect(Token::LParen);
200205

@@ -488,7 +493,7 @@ void TestFileParser::Scanner::readStream(istream& _stream)
488493
void TestFileParser::Scanner::scanNextToken()
489494
{
490495
// Make code coverage happy.
491-
assert(formatToken(Token::NUM_TOKENS) == "");
496+
soltestAssert(formatToken(Token::NUM_TOKENS).empty(), "");
492497

493498
auto detectKeyword = [](std::string const& _literal = "") -> std::pair<Token, std::string> {
494499
if (_literal == "true") return {Token::Boolean, "true"};
@@ -712,3 +717,8 @@ char TestFileParser::Scanner::scanHexPart()
712717

713718
return static_cast<char>(value);
714719
}
720+
721+
bool TestFileParser::isBuiltinFunction(std::string const& signature)
722+
{
723+
return m_builtins.count(signature) > 0;
724+
}

test/libsolidity/util/TestFileParser.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
#pragma once
1616

17-
#include <libsolutil/CommonData.h>
1817
#include <liblangutil/Exceptions.h>
18+
#include <libsolutil/CommonData.h>
1919
#include <test/libsolidity/util/SoltestTypes.h>
2020

2121
#include <iosfwd>
@@ -52,7 +52,7 @@ class TestFileParser
5252
public:
5353
/// Constructor that takes an input stream \param _stream to operate on
5454
/// and creates the internal scanner.
55-
TestFileParser(std::istream& _stream): m_scanner(_stream) {}
55+
explicit TestFileParser(std::istream& _stream, std::map<std::string, Builtin> const& _builtins): m_scanner(_stream), m_builtins(_builtins) {}
5656

5757
/// Parses function calls blockwise and returns a list of function calls found.
5858
/// Throws an exception if a function call cannot be parsed because of its
@@ -177,12 +177,18 @@ class TestFileParser
177177
/// Parses the current string literal.
178178
std::string parseString();
179179

180+
/// Checks whether a builtin function with the given signature exist.
181+
/// @returns true, if builtin found, false otherwise
182+
bool isBuiltinFunction(std::string const& signature);
183+
180184
/// A scanner instance
181185
Scanner m_scanner;
182186

183187
/// The current line number. Incremented when Token::Newline (//) is found and
184188
/// used to enhance parser error messages.
185189
size_t m_lineNumber = 0;
190+
191+
std::map<std::string, Builtin> const& m_builtins;
186192
};
187193

188194
}

0 commit comments

Comments
 (0)