Skip to content

Commit 767514e

Browse files
committed
[isoltest] Add support for builtin functions.
1 parent ad2ecf1 commit 767514e

File tree

6 files changed

+101
-17
lines changed

6 files changed

+101
-17
lines changed

test/libsolidity/SemanticTest.cpp

Lines changed: 58 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;
@@ -47,6 +52,22 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
4752
m_lineOffset(m_reader.lineNumber()),
4853
m_enforceViaYul(enforceViaYul)
4954
{
55+
auto simpleSmokeBuiltin = [](FunctionCall const& _call) -> std::optional<bytes> {
56+
// This function is only used in test/libsolidity/semanticTests/builtins/smoke.sol.
57+
// It could be removed when we have actual builtins.
58+
std::optional<bytes> result;
59+
if (_call.arguments.parameters.size() < 3)
60+
{
61+
result = bytes();
62+
for (auto const& parameter: _call.arguments.parameters)
63+
result.value() += util::toBigEndian(u256{util::fromHex(parameter.rawString)});
64+
}
65+
return result;
66+
};
67+
m_builtins["smoke_test0"] = simpleSmokeBuiltin;
68+
m_builtins["smoke_test1"] = simpleSmokeBuiltin;
69+
m_builtins["smoke_test2"] = simpleSmokeBuiltin;
70+
5071
string choice = m_reader.stringSetting("compileViaYul", "default");
5172
if (choice == "also")
5273
{
@@ -119,7 +140,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
119140
return result;
120141
}
121142

122-
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
143+
TestCase::TestResult SemanticTest::runTest(
144+
ostream& _stream,
145+
string const& _linePrefix,
146+
bool _formatted,
147+
bool _compileViaYul,
148+
bool _compileToEwasm
149+
)
123150
{
124151
bool success = true;
125152

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

145-
for (auto& test: m_tests)
172+
for (TestFunctionCall& test: m_tests)
146173
test.reset();
147174

148175
map<string, solidity::test::Address> libraries;
149176

150177
bool constructed = false;
151178

152-
for (auto& test: m_tests)
179+
for (TestFunctionCall& test: m_tests)
153180
{
154181
if (constructed)
155182
{
156-
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
183+
soltestAssert(
184+
test.call().kind != FunctionCall::Kind::Library,
185+
"Libraries have to be deployed before any other call."
186+
);
157187
soltestAssert(
158188
test.call().kind != FunctionCall::Kind::Constructor,
159-
"Constructor has to be the first function call expect for library deployments.");
189+
"Constructor has to be the first function call expect for library deployments."
190+
);
160191
}
161192
else if (test.call().kind == FunctionCall::Kind::Library)
162193
{
@@ -197,6 +228,17 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
197228
bytes output;
198229
if (test.call().kind == FunctionCall::Kind::LowLevel)
199230
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
231+
else if (test.call().kind == FunctionCall::Kind::Builtin)
232+
{
233+
std::optional<bytes> builtinOutput = m_builtins.at(test.call().signature)(test.call());
234+
if (builtinOutput.has_value())
235+
{
236+
m_transactionSuccessful = true;
237+
output = builtinOutput.value();
238+
}
239+
else
240+
m_transactionSuccessful = false;
241+
}
200242
else
201243
{
202244
soltestAssert(
@@ -241,15 +283,15 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
241283
if (!success && (m_runWithYul || !_compileViaYul))
242284
{
243285
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
244-
for (auto const& test: m_tests)
286+
for (TestFunctionCall const& test: m_tests)
245287
{
246288
ErrorReporter errorReporter;
247289
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
248290
_stream << errorReporter.format(_linePrefix, _formatted);
249291
}
250292
_stream << endl;
251293
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
252-
for (auto const& test: m_tests)
294+
for (TestFunctionCall const& test: m_tests)
253295
{
254296
ErrorReporter errorReporter;
255297
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
@@ -320,7 +362,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
320362

321363
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
322364
{
323-
for (auto const& test: m_tests)
365+
for (TestFunctionCall const& test: m_tests)
324366
_stream << test.format("", true, false) << endl;
325367
}
326368

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

341383
void SemanticTest::parseExpectations(istream& _stream)
342384
{
343-
TestFileParser parser{_stream};
344-
auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
345-
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
385+
TestFileParser parser{_stream, &m_builtins};
386+
m_tests += parser.parseFunctionCalls(m_lineOffset);
346387
}
347388

348-
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, solidity::test::Address> const& _libraries)
389+
bool SemanticTest::deploy(
390+
string const& _contractName,
391+
u256 const& _value,
392+
bytes const& _arguments,
393+
map<string, solidity::test::Address> const& _libraries
394+
)
349395
{
350396
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
351397
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
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
contract ClientReceipt {
2+
}
3+
// ====
4+
// compileViaYul: also
5+
// ----
6+
// smoke_test0 ->
7+
// smoke_test1: 1 -> 1
8+
// smoke_test2: 2, 3 -> 2, 3
9+
// smoke_test2: 2, 3, 4 -> FAILURE
10+
// smoke_test0 ->

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: 13 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))
@@ -107,6 +107,9 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
107107
if (lowLevelCall)
108108
call.kind = FunctionCall::Kind::LowLevel;
109109

110+
if (checkBuiltinFunction(call.signature))
111+
call.kind = FunctionCall::Kind::Builtin;
112+
110113
if (accept(Token::Comma, true))
111114
call.value = parseFunctionCallValue();
112115

@@ -195,6 +198,9 @@ pair<string, bool> TestFileParser::parseFunctionSignature()
195198
expect(Token::Identifier);
196199
}
197200

201+
if (checkBuiltinFunction(signature))
202+
return {signature, false};
203+
198204
signature += formatToken(Token::LParen);
199205
expect(Token::LParen);
200206

@@ -488,7 +494,7 @@ void TestFileParser::Scanner::readStream(istream& _stream)
488494
void TestFileParser::Scanner::scanNextToken()
489495
{
490496
// Make code coverage happy.
491-
assert(formatToken(Token::NUM_TOKENS) == "");
497+
soltestAssert(formatToken(Token::NUM_TOKENS).empty(), "");
492498

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

713719
return static_cast<char>(value);
714720
}
721+
722+
bool TestFileParser::checkBuiltinFunction(std::string const& signature)
723+
{
724+
return m_builtins && m_builtins->count(signature) > 0;
725+
}

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>* _builtins = nullptr): 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 checkBuiltinFunction(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>* m_builtins{nullptr};
186192
};
187193

188194
}

0 commit comments

Comments
 (0)