1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19 * @date 2017
20 * Unit tests for interface/StandardCompiler.h.
21 */
22
23 #include <string>
24 #include <boost/test/unit_test.hpp>
25 #include <libsolidity/interface/OptimiserSettings.h>
26 #include <libsolidity/interface/StandardCompiler.h>
27 #include <libsolidity/interface/Version.h>
28 #include <libsolutil/JSON.h>
29 #include <libsolutil/CommonData.h>
30 #include <test/Metadata.h>
31
32 #include <algorithm>
33 #include <set>
34
35 using namespace std;
36 using namespace solidity::evmasm;
37
38 namespace solidity::frontend::test
39 {
40
41 namespace
42 {
43
str2Severity(string const & _cat)44 langutil::Error::Severity str2Severity(string const& _cat)
45 {
46 map<string, langutil::Error::Severity> cats{
47 {"info", langutil::Error::Severity::Info},
48 {"Info", langutil::Error::Severity::Info},
49 {"warning", langutil::Error::Severity::Warning},
50 {"Warning", langutil::Error::Severity::Warning},
51 {"error", langutil::Error::Severity::Error},
52 {"Error", langutil::Error::Severity::Error}
53 };
54 return cats.at(_cat);
55 }
56
57 /// Helper to match a specific error type and message
containsError(Json::Value const & _compilerResult,string const & _type,string const & _message)58 bool containsError(Json::Value const& _compilerResult, string const& _type, string const& _message)
59 {
60 if (!_compilerResult.isMember("errors"))
61 return false;
62
63 for (auto const& error: _compilerResult["errors"])
64 {
65 BOOST_REQUIRE(error.isObject());
66 BOOST_REQUIRE(error["type"].isString());
67 BOOST_REQUIRE(error["message"].isString());
68 if ((error["type"].asString() == _type) && (error["message"].asString() == _message))
69 return true;
70 }
71
72 return false;
73 }
74
containsAtMostWarnings(Json::Value const & _compilerResult)75 bool containsAtMostWarnings(Json::Value const& _compilerResult)
76 {
77 if (!_compilerResult.isMember("errors"))
78 return true;
79
80 for (auto const& error: _compilerResult["errors"])
81 {
82 BOOST_REQUIRE(error.isObject());
83 BOOST_REQUIRE(error["severity"].isString());
84 if (langutil::Error::isError(str2Severity(error["severity"].asString())))
85 return false;
86 }
87
88 return true;
89 }
90
getContractResult(Json::Value const & _compilerResult,string const & _file,string const & _name)91 Json::Value getContractResult(Json::Value const& _compilerResult, string const& _file, string const& _name)
92 {
93 if (
94 !_compilerResult["contracts"].isObject() ||
95 !_compilerResult["contracts"][_file].isObject() ||
96 !_compilerResult["contracts"][_file][_name].isObject()
97 )
98 return Json::Value();
99 return _compilerResult["contracts"][_file][_name];
100 }
101
checkLinkReferencesSchema(Json::Value const & _contractResult)102 void checkLinkReferencesSchema(Json::Value const& _contractResult)
103 {
104 BOOST_TEST_REQUIRE(_contractResult.isObject());
105 BOOST_TEST_REQUIRE(_contractResult["evm"]["bytecode"].isObject());
106
107 Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"];
108 BOOST_TEST_REQUIRE(linkReferenceResult.isObject());
109
110 for (string const& fileName: linkReferenceResult.getMemberNames())
111 {
112 BOOST_TEST_REQUIRE(linkReferenceResult[fileName].isObject());
113 for (string const& libraryName: linkReferenceResult[fileName].getMemberNames())
114 {
115 BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName].isArray());
116 BOOST_TEST_REQUIRE(!linkReferenceResult[fileName][libraryName].empty());
117 for (int i = 0; i < static_cast<int>(linkReferenceResult.size()); ++i)
118 {
119 BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].isObject());
120 BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].size() == 2);
121 BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["length"].isUInt());
122 BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["start"].isUInt());
123 }
124 }
125 }
126 }
127
expectLinkReferences(Json::Value const & _contractResult,map<string,set<string>> const & _expectedLinkReferences)128 void expectLinkReferences(Json::Value const& _contractResult, map<string, set<string>> const& _expectedLinkReferences)
129 {
130 checkLinkReferencesSchema(_contractResult);
131
132 Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"];
133 BOOST_TEST(linkReferenceResult.size() == _expectedLinkReferences.size());
134
135 for (auto const& [fileName, libraries]: _expectedLinkReferences)
136 {
137 BOOST_TEST(linkReferenceResult.isMember(fileName));
138 BOOST_TEST(linkReferenceResult[fileName].size() == libraries.size());
139 for (string const& libraryName: libraries)
140 BOOST_TEST(linkReferenceResult[fileName].isMember(libraryName));
141 }
142 }
143
compile(string _input)144 Json::Value compile(string _input)
145 {
146 StandardCompiler compiler;
147 string output = compiler.compile(std::move(_input));
148 Json::Value ret;
149 BOOST_REQUIRE(util::jsonParseStrict(output, ret));
150 return ret;
151 }
152
153 } // end anonymous namespace
154
155 BOOST_AUTO_TEST_SUITE(StandardCompiler)
156
BOOST_AUTO_TEST_CASE(assume_object_input)157 BOOST_AUTO_TEST_CASE(assume_object_input)
158 {
159 Json::Value result;
160
161 /// Use the native JSON interface of StandardCompiler to trigger these
162 frontend::StandardCompiler compiler;
163 result = compiler.compile(Json::Value());
164 BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object."));
165 result = compiler.compile(Json::Value("INVALID"));
166 BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object."));
167
168 /// Use the string interface of StandardCompiler to trigger these
169 result = compile("");
170 BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n"));
171 result = compile("invalid");
172 BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 2\n Extra non-whitespace after JSON value.\n"));
173 result = compile("\"invalid\"");
174 BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n"));
175 BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n"));
176 result = compile("{}");
177 BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n"));
178 BOOST_CHECK(!containsAtMostWarnings(result));
179 }
180
BOOST_AUTO_TEST_CASE(invalid_language)181 BOOST_AUTO_TEST_CASE(invalid_language)
182 {
183 char const* input = R"(
184 {
185 "language": "INVALID",
186 "sources": { "name": { "content": "abc" } }
187 }
188 )";
189 Json::Value result = compile(input);
190 BOOST_CHECK(containsError(result, "JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language."));
191 }
192
BOOST_AUTO_TEST_CASE(valid_language)193 BOOST_AUTO_TEST_CASE(valid_language)
194 {
195 char const* input = R"(
196 {
197 "language": "Solidity"
198 }
199 )";
200 Json::Value result = compile(input);
201 BOOST_CHECK(!containsError(result, "JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language."));
202 }
203
BOOST_AUTO_TEST_CASE(no_sources)204 BOOST_AUTO_TEST_CASE(no_sources)
205 {
206 char const* input = R"(
207 {
208 "language": "Solidity"
209 }
210 )";
211 Json::Value result = compile(input);
212 BOOST_CHECK(containsError(result, "JSONError", "No input sources specified."));
213 }
214
BOOST_AUTO_TEST_CASE(no_sources_empty_object)215 BOOST_AUTO_TEST_CASE(no_sources_empty_object)
216 {
217 char const* input = R"(
218 {
219 "language": "Solidity",
220 "sources": {}
221 }
222 )";
223 Json::Value result = compile(input);
224 BOOST_CHECK(containsError(result, "JSONError", "No input sources specified."));
225 }
226
BOOST_AUTO_TEST_CASE(no_sources_empty_array)227 BOOST_AUTO_TEST_CASE(no_sources_empty_array)
228 {
229 char const* input = R"(
230 {
231 "language": "Solidity",
232 "sources": []
233 }
234 )";
235 Json::Value result = compile(input);
236 BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object."));
237 }
238
BOOST_AUTO_TEST_CASE(sources_is_array)239 BOOST_AUTO_TEST_CASE(sources_is_array)
240 {
241 char const* input = R"(
242 {
243 "language": "Solidity",
244 "sources": ["aa", "bb"]
245 }
246 )";
247 Json::Value result = compile(input);
248 BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object."));
249 }
250
BOOST_AUTO_TEST_CASE(unexpected_trailing_test)251 BOOST_AUTO_TEST_CASE(unexpected_trailing_test)
252 {
253 char const* input = R"(
254 {
255 "language": "Solidity",
256 "sources": {
257 "A": {
258 "content": "contract A { function f() {} }"
259 }
260 }
261 }
262 }
263 }
264 )";
265 Json::Value result = compile(input);
266 BOOST_CHECK(containsError(result, "JSONError", "* Line 10, Column 2\n Extra non-whitespace after JSON value.\n"));
267 }
268
BOOST_AUTO_TEST_CASE(smoke_test)269 BOOST_AUTO_TEST_CASE(smoke_test)
270 {
271 char const* input = R"(
272 {
273 "language": "Solidity",
274 "sources": {
275 "empty": {
276 "content": ""
277 }
278 }
279 }
280 )";
281 Json::Value result = compile(input);
282 BOOST_CHECK(containsAtMostWarnings(result));
283 }
284
BOOST_AUTO_TEST_CASE(error_recovery_field)285 BOOST_AUTO_TEST_CASE(error_recovery_field)
286 {
287 auto input = R"(
288 {
289 "language": "Solidity",
290 "settings": {
291 "parserErrorRecovery": "1"
292 },
293 "sources": {
294 "empty": {
295 "content": ""
296 }
297 }
298 }
299 )";
300
301 Json::Value result = compile(input);
302 BOOST_CHECK(containsError(result, "JSONError", "\"settings.parserErrorRecovery\" must be a Boolean."));
303
304 input = R"(
305 {
306 "language": "Solidity",
307 "settings": {
308 "parserErrorRecovery": true
309 },
310 "sources": {
311 "empty": {
312 "content": ""
313 }
314 }
315 }
316 )";
317
318 result = compile(input);
319 BOOST_CHECK(containsAtMostWarnings(result));
320 }
321
BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean)322 BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean)
323 {
324 char const* input = R"(
325 {
326 "language": "Solidity",
327 "settings": {
328 "optimizer": {
329 "enabled": "wrong"
330 }
331 },
332 "sources": {
333 "empty": {
334 "content": ""
335 }
336 }
337 }
338 )";
339 Json::Value result = compile(input);
340 BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a Boolean."));
341 }
342
BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number)343 BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number)
344 {
345 char const* input = R"(
346 {
347 "language": "Solidity",
348 "settings": {
349 "optimizer": {
350 "enabled": true,
351 "runs": "not a number"
352 }
353 },
354 "sources": {
355 "empty": {
356 "content": ""
357 }
358 }
359 }
360 )";
361 Json::Value result = compile(input);
362 BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number."));
363 }
364
BOOST_AUTO_TEST_CASE(optimizer_runs_not_an_unsigned_number)365 BOOST_AUTO_TEST_CASE(optimizer_runs_not_an_unsigned_number)
366 {
367 char const* input = R"(
368 {
369 "language": "Solidity",
370 "settings": {
371 "optimizer": {
372 "enabled": true,
373 "runs": -1
374 }
375 },
376 "sources": {
377 "empty": {
378 "content": ""
379 }
380 }
381 }
382 )";
383 Json::Value result = compile(input);
384 BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number."));
385 }
386
BOOST_AUTO_TEST_CASE(basic_compilation)387 BOOST_AUTO_TEST_CASE(basic_compilation)
388 {
389 char const* input = R"(
390 {
391 "language": "Solidity",
392 "sources": {
393 "fileA": {
394 "content": "contract A { }"
395 }
396 },
397 "settings": {
398 "outputSelection": {
399 "fileA": {
400 "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
401 "": [ "ast" ]
402 }
403 }
404 }
405 }
406 )";
407 Json::Value result = compile(input);
408 BOOST_CHECK(containsAtMostWarnings(result));
409 Json::Value contract = getContractResult(result, "fileA", "A");
410 BOOST_CHECK(contract.isObject());
411 BOOST_CHECK(contract["abi"].isArray());
412 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
413 BOOST_CHECK(contract["devdoc"].isObject());
414 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["devdoc"]), R"({"kind":"dev","methods":{},"version":1})");
415 BOOST_CHECK(contract["userdoc"].isObject());
416 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["userdoc"]), R"({"kind":"user","methods":{},"version":1})");
417 BOOST_CHECK(contract["evm"].isObject());
418 /// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode
419 BOOST_CHECK(contract["evm"]["bytecode"].isObject());
420 BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
421 BOOST_CHECK_EQUAL(
422 solidity::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
423 string("6080604052348015600f57600080fd5b5060") +
424 (VersionIsRelease ? "3f" : util::toHex(bytes{uint8_t(61 + VersionStringStrict.size())})) +
425 "80601d6000396000f3fe6080604052600080fdfe"
426 );
427 BOOST_CHECK(contract["evm"]["assembly"].isString());
428 BOOST_CHECK(contract["evm"]["assembly"].asString().find(
429 " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n "
430 "callvalue\n dup1\n "
431 "iszero\n tag_1\n jumpi\n "
432 "0x00\n "
433 "dup1\n revert\n"
434 "tag_1:\n pop\n dataSize(sub_0)\n dup1\n "
435 "dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n "
436 "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n "
437 "0x00\n "
438 "dup1\n revert\n\n auxdata: 0xa26469706673582212"
439 ) == 0);
440 BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
441 BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"].size(), 1);
442 BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"].isObject());
443 BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"]["creation"].size(), 3);
444 BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["codeDepositCost"].isString());
445 BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["executionCost"].isString());
446 BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["totalCost"].isString());
447 BOOST_CHECK_EQUAL(
448 u256(contract["evm"]["gasEstimates"]["creation"]["codeDepositCost"].asString()) +
449 u256(contract["evm"]["gasEstimates"]["creation"]["executionCost"].asString()),
450 u256(contract["evm"]["gasEstimates"]["creation"]["totalCost"].asString())
451 );
452 // Lets take the top level `.code` section (the "deployer code"), that should expose most of the features of
453 // the assembly JSON. What we want to check here is Operation, Push, PushTag, PushSub, PushSubSize and Tag.
454 BOOST_CHECK(contract["evm"]["legacyAssembly"].isObject());
455 BOOST_CHECK(contract["evm"]["legacyAssembly"][".code"].isArray());
456 BOOST_CHECK_EQUAL(
457 util::jsonCompactPrint(contract["evm"]["legacyAssembly"][".code"]),
458 "[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"80\"},"
459 "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"40\"},"
460 "{\"begin\":0,\"end\":14,\"name\":\"MSTORE\",\"source\":0},"
461 "{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\",\"source\":0},"
462 "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0},"
463 "{\"begin\":0,\"end\":14,\"name\":\"ISZERO\",\"source\":0},"
464 "{\"begin\":0,\"end\":14,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
465 "{\"begin\":0,\"end\":14,\"name\":\"JUMPI\",\"source\":0},"
466 "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
467 "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0},"
468 "{\"begin\":0,\"end\":14,\"name\":\"REVERT\",\"source\":0},"
469 "{\"begin\":0,\"end\":14,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
470 "{\"begin\":0,\"end\":14,\"name\":\"JUMPDEST\",\"source\":0},"
471 "{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0},"
472 "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
473 "{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0},"
474 "{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
475 "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
476 "{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\",\"source\":0},"
477 "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
478 "{\"begin\":0,\"end\":14,\"name\":\"RETURN\",\"source\":0}]"
479 );
480 BOOST_CHECK(contract["metadata"].isString());
481 BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString()));
482 BOOST_CHECK(result["sources"].isObject());
483 BOOST_CHECK(result["sources"]["fileA"].isObject());
484 BOOST_CHECK(result["sources"]["fileA"]["ast"].isObject());
485 BOOST_CHECK_EQUAL(
486 util::jsonCompactPrint(result["sources"]["fileA"]["ast"]),
487 "{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"id\":2,\"nodeType\":\"SourceUnit\",\"nodes\":[{\"abstract\":false,"
488 "\"baseContracts\":[],\"canonicalName\":\"A\",\"contractDependencies\":[],\"contractKind\":\"contract\",\"fullyImplemented\":true,\"id\":1,"
489 "\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nameLocation\":\"9:1:0\",\"nodeType\":\"ContractDefinition\",\"nodes\":[],\"scope\":2,"
490 "\"src\":\"0:14:0\",\"usedErrors\":[]}],\"src\":\"0:14:0\"}"
491 );
492 }
493
BOOST_AUTO_TEST_CASE(compilation_error)494 BOOST_AUTO_TEST_CASE(compilation_error)
495 {
496 char const* input = R"(
497 {
498 "language": "Solidity",
499 "settings": {
500 "outputSelection": {
501 "fileA": {
502 "A": [
503 "abi"
504 ]
505 }
506 }
507 },
508 "sources": {
509 "fileA": {
510 "content": "contract A { function }"
511 }
512 }
513 }
514 )";
515 Json::Value result = compile(input);
516 BOOST_CHECK(result.isMember("errors"));
517 BOOST_CHECK(result["errors"].size() >= 1);
518 for (auto const& error: result["errors"])
519 {
520 BOOST_REQUIRE(error.isObject());
521 BOOST_REQUIRE(error["message"].isString());
522 if (error["message"].asString().find("pre-release compiler") == string::npos)
523 {
524 BOOST_CHECK_EQUAL(
525 util::jsonCompactPrint(error),
526 "{\"component\":\"general\",\"errorCode\":\"2314\",\"formattedMessage\":\"ParserError: Expected identifier but got '}'\\n"
527 " --> fileA:1:23:\\n |\\n1 | contract A { function }\\n | ^\\n\\n\",\"message\":\"Expected identifier but got '}'\","
528 "\"severity\":\"error\",\"sourceLocation\":{\"end\":23,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}"
529 );
530 }
531 }
532 }
533
BOOST_AUTO_TEST_CASE(output_selection_explicit)534 BOOST_AUTO_TEST_CASE(output_selection_explicit)
535 {
536 char const* input = R"(
537 {
538 "language": "Solidity",
539 "settings": {
540 "outputSelection": {
541 "fileA": {
542 "A": [
543 "abi"
544 ]
545 }
546 }
547 },
548 "sources": {
549 "fileA": {
550 "content": "contract A { }"
551 }
552 }
553 }
554 )";
555 Json::Value result = compile(input);
556 BOOST_CHECK(containsAtMostWarnings(result));
557 Json::Value contract = getContractResult(result, "fileA", "A");
558 BOOST_CHECK(contract.isObject());
559 BOOST_CHECK(contract["abi"].isArray());
560 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
561 }
562
BOOST_AUTO_TEST_CASE(output_selection_all_contracts)563 BOOST_AUTO_TEST_CASE(output_selection_all_contracts)
564 {
565 char const* input = R"(
566 {
567 "language": "Solidity",
568 "settings": {
569 "outputSelection": {
570 "fileA": {
571 "*": [
572 "abi"
573 ]
574 }
575 }
576 },
577 "sources": {
578 "fileA": {
579 "content": "contract A { }"
580 }
581 }
582 }
583 )";
584 Json::Value result = compile(input);
585 BOOST_CHECK(containsAtMostWarnings(result));
586 Json::Value contract = getContractResult(result, "fileA", "A");
587 BOOST_CHECK(contract.isObject());
588 BOOST_CHECK(contract["abi"].isArray());
589 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
590 }
591
BOOST_AUTO_TEST_CASE(output_selection_all_files_single_contract)592 BOOST_AUTO_TEST_CASE(output_selection_all_files_single_contract)
593 {
594 char const* input = R"(
595 {
596 "language": "Solidity",
597 "settings": {
598 "outputSelection": {
599 "*": {
600 "A": [
601 "abi"
602 ]
603 }
604 }
605 },
606 "sources": {
607 "fileA": {
608 "content": "contract A { }"
609 }
610 }
611 }
612 )";
613 Json::Value result = compile(input);
614 BOOST_CHECK(containsAtMostWarnings(result));
615 Json::Value contract = getContractResult(result, "fileA", "A");
616 BOOST_CHECK(contract.isObject());
617 BOOST_CHECK(contract["abi"].isArray());
618 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
619 }
620
BOOST_AUTO_TEST_CASE(output_selection_all_files_all_contracts)621 BOOST_AUTO_TEST_CASE(output_selection_all_files_all_contracts)
622 {
623 char const* input = R"(
624 {
625 "language": "Solidity",
626 "settings": {
627 "outputSelection": {
628 "*": {
629 "*": [
630 "abi"
631 ]
632 }
633 }
634 },
635 "sources": {
636 "fileA": {
637 "content": "contract A { }"
638 }
639 }
640 }
641 )";
642 Json::Value result = compile(input);
643 BOOST_CHECK(containsAtMostWarnings(result));
644 Json::Value contract = getContractResult(result, "fileA", "A");
645 BOOST_CHECK(contract.isObject());
646 BOOST_CHECK(contract["abi"].isArray());
647 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
648 }
649
BOOST_AUTO_TEST_CASE(output_selection_dependent_contract)650 BOOST_AUTO_TEST_CASE(output_selection_dependent_contract)
651 {
652 char const* input = R"(
653 {
654 "language": "Solidity",
655 "settings": {
656 "outputSelection": {
657 "*": {
658 "A": [
659 "abi"
660 ]
661 }
662 }
663 },
664 "sources": {
665 "fileA": {
666 "content": "contract B { } contract A { function f() public { new B(); } }"
667 }
668 }
669 }
670 )";
671 Json::Value result = compile(input);
672 BOOST_CHECK(containsAtMostWarnings(result));
673 Json::Value contract = getContractResult(result, "fileA", "A");
674 BOOST_CHECK(contract.isObject());
675 BOOST_CHECK(contract["abi"].isArray());
676 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]");
677 }
678
BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import)679 BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import)
680 {
681 char const* input = R"(
682 {
683 "language": "Solidity",
684 "settings": {
685 "outputSelection": {
686 "*": {
687 "A": [
688 "abi"
689 ]
690 }
691 }
692 },
693 "sources": {
694 "fileA": {
695 "content": "import \"fileB\"; contract A { function f() public { new B(); } }"
696 },
697 "fileB": {
698 "content": "contract B { }"
699 }
700 }
701 }
702 )";
703 Json::Value result = compile(input);
704 BOOST_CHECK(containsAtMostWarnings(result));
705 Json::Value contract = getContractResult(result, "fileA", "A");
706 BOOST_CHECK(contract.isObject());
707 BOOST_CHECK(contract["abi"].isArray());
708 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[{\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]");
709 }
710
BOOST_AUTO_TEST_CASE(filename_with_colon)711 BOOST_AUTO_TEST_CASE(filename_with_colon)
712 {
713 char const* input = R"(
714 {
715 "language": "Solidity",
716 "settings": {
717 "outputSelection": {
718 "http://github.com/ethereum/solidity/std/StandardToken.sol": {
719 "A": [
720 "abi"
721 ]
722 }
723 }
724 },
725 "sources": {
726 "http://github.com/ethereum/solidity/std/StandardToken.sol": {
727 "content": "contract A { }"
728 }
729 }
730 }
731 )";
732 Json::Value result = compile(input);
733 BOOST_CHECK(containsAtMostWarnings(result));
734 Json::Value contract = getContractResult(result, "http://github.com/ethereum/solidity/std/StandardToken.sol", "A");
735 BOOST_CHECK(contract.isObject());
736 BOOST_CHECK(contract["abi"].isArray());
737 BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]");
738 }
739
BOOST_AUTO_TEST_CASE(library_filename_with_colon)740 BOOST_AUTO_TEST_CASE(library_filename_with_colon)
741 {
742 char const* input = R"(
743 {
744 "language": "Solidity",
745 "settings": {
746 "outputSelection": {
747 "fileA": {
748 "A": [
749 "evm.bytecode"
750 ]
751 }
752 }
753 },
754 "sources": {
755 "fileA": {
756 "content": "import \"git:library.sol\"; contract A { function f() public returns (uint) { return L.g(); } }"
757 },
758 "git:library.sol": {
759 "content": "library L { function g() public returns (uint) { return 1; } }"
760 }
761 }
762 }
763 )";
764 Json::Value result = compile(input);
765 BOOST_CHECK(containsAtMostWarnings(result));
766 Json::Value contract = getContractResult(result, "fileA", "A");
767 BOOST_CHECK(contract.isObject());
768 expectLinkReferences(contract, {{"git:library.sol", {"L"}}});
769 }
770
BOOST_AUTO_TEST_CASE(libraries_invalid_top_level)771 BOOST_AUTO_TEST_CASE(libraries_invalid_top_level)
772 {
773 char const* input = R"(
774 {
775 "language": "Solidity",
776 "settings": {
777 "libraries": "42"
778 },
779 "sources": {
780 "empty": {
781 "content": ""
782 }
783 }
784 }
785 )";
786 Json::Value result = compile(input);
787 BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object."));
788 }
789
BOOST_AUTO_TEST_CASE(libraries_invalid_entry)790 BOOST_AUTO_TEST_CASE(libraries_invalid_entry)
791 {
792 char const* input = R"(
793 {
794 "language": "Solidity",
795 "settings": {
796 "libraries": {
797 "L": "42"
798 }
799 },
800 "sources": {
801 "empty": {
802 "content": ""
803 }
804 }
805 }
806 )";
807 Json::Value result = compile(input);
808 BOOST_CHECK(containsError(result, "JSONError", "Library entry is not a JSON object."));
809 }
810
BOOST_AUTO_TEST_CASE(libraries_invalid_hex)811 BOOST_AUTO_TEST_CASE(libraries_invalid_hex)
812 {
813 char const* input = R"(
814 {
815 "language": "Solidity",
816 "settings": {
817 "libraries": {
818 "library.sol": {
819 "L": "0x4200000000000000000000000000000000000xx1"
820 }
821 }
822 },
823 "sources": {
824 "empty": {
825 "content": ""
826 }
827 }
828 }
829 )";
830 Json::Value result = compile(input);
831 BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied."));
832 }
833
BOOST_AUTO_TEST_CASE(libraries_invalid_length)834 BOOST_AUTO_TEST_CASE(libraries_invalid_length)
835 {
836 char const* input = R"(
837 {
838 "language": "Solidity",
839 "settings": {
840 "libraries": {
841 "library.sol": {
842 "L1": "0x42",
843 "L2": "0x4200000000000000000000000000000000000001ff"
844 }
845 }
846 },
847 "sources": {
848 "empty": {
849 "content": ""
850 }
851 }
852 }
853 )";
854 Json::Value result = compile(input);
855 BOOST_CHECK(containsError(result, "JSONError", "Library address is of invalid length."));
856 }
857
BOOST_AUTO_TEST_CASE(libraries_missing_hex_prefix)858 BOOST_AUTO_TEST_CASE(libraries_missing_hex_prefix)
859 {
860 char const* input = R"(
861 {
862 "language": "Solidity",
863 "settings": {
864 "libraries": {
865 "library.sol": {
866 "L": "4200000000000000000000000000000000000001"
867 }
868 }
869 },
870 "sources": {
871 "empty": {
872 "content": ""
873 }
874 }
875 }
876 )";
877 Json::Value result = compile(input);
878 BOOST_CHECK(containsError(result, "JSONError", "Library address is not prefixed with \"0x\"."));
879 }
880
BOOST_AUTO_TEST_CASE(library_linking)881 BOOST_AUTO_TEST_CASE(library_linking)
882 {
883 char const* input = R"(
884 {
885 "language": "Solidity",
886 "settings": {
887 "libraries": {
888 "library.sol": {
889 "L": "0x4200000000000000000000000000000000000001"
890 }
891 },
892 "outputSelection": {
893 "fileA": {
894 "A": [
895 "evm.bytecode"
896 ]
897 }
898 }
899 },
900 "sources": {
901 "fileA": {
902 "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() public returns (uint) { L2.g(); return L.g(); } }"
903 },
904 "library.sol": {
905 "content": "library L { function g() public returns (uint) { return 1; } }"
906 },
907 "library2.sol": {
908 "content": "library L2 { function g() public { } }"
909 }
910 }
911 }
912 )";
913 Json::Value result = compile(input);
914 BOOST_TEST(containsAtMostWarnings(result));
915 Json::Value contractResult = getContractResult(result, "fileA", "A");
916 expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}});
917 }
918
BOOST_AUTO_TEST_CASE(linking_yul)919 BOOST_AUTO_TEST_CASE(linking_yul)
920 {
921 char const* input = R"(
922 {
923 "language": "Yul",
924 "settings": {
925 "libraries": {
926 "fileB": {
927 "L": "0x4200000000000000000000000000000000000001"
928 }
929 },
930 "outputSelection": {
931 "fileA": {
932 "*": [
933 "evm.bytecode.linkReferences"
934 ]
935 }
936 }
937 },
938 "sources": {
939 "fileA": {
940 "content": "object \"a\" { code { let addr := linkersymbol(\"fileB:L\") sstore(0, addr) } }"
941 }
942 }
943 }
944 )";
945 Json::Value result = compile(input);
946 BOOST_TEST(containsAtMostWarnings(result));
947 Json::Value contractResult = getContractResult(result, "fileA", "a");
948 expectLinkReferences(contractResult, {});
949 }
950
BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference)951 BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference)
952 {
953 char const* input = R"(
954 {
955 "language": "Yul",
956 "settings": {
957 "libraries": {
958 "": {
959 "": "0x4200000000000000000000000000000000000001"
960 }
961 },
962 "outputSelection": {
963 "fileA": {
964 "*": [
965 "evm.bytecode.linkReferences"
966 ]
967 }
968 }
969 },
970 "sources": {
971 "fileA": {
972 "content": "object \"a\" { code { let addr := linkersymbol(\"\") sstore(0, addr) } }"
973 }
974 }
975 }
976 )";
977 Json::Value result = compile(input);
978 BOOST_TEST(containsAtMostWarnings(result));
979 Json::Value contractResult = getContractResult(result, "fileA", "a");
980 expectLinkReferences(contractResult, {{"", {""}}});
981 }
982
BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference)983 BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference)
984 {
985 char const* input = R"(
986 {
987 "language": "Yul",
988 "settings": {
989 "libraries": {
990 "": {
991 "L": "0x4200000000000000000000000000000000000001"
992 }
993 },
994 "outputSelection": {
995 "fileA": {
996 "*": [
997 "evm.bytecode.linkReferences"
998 ]
999 }
1000 }
1001 },
1002 "sources": {
1003 "fileA": {
1004 "content": "object \"a\" { code { let addr := linkersymbol(\"L\") sstore(0, addr) } }"
1005 }
1006 }
1007 }
1008 )";
1009 Json::Value result = compile(input);
1010 BOOST_TEST(containsAtMostWarnings(result));
1011 Json::Value contractResult = getContractResult(result, "fileA", "a");
1012 expectLinkReferences(contractResult, {{"", {"L"}}});
1013 }
1014
BOOST_AUTO_TEST_CASE(linking_yul_same_library_name_different_files)1015 BOOST_AUTO_TEST_CASE(linking_yul_same_library_name_different_files)
1016 {
1017 char const* input = R"(
1018 {
1019 "language": "Yul",
1020 "settings": {
1021 "libraries": {
1022 "fileB": {
1023 "L": "0x4200000000000000000000000000000000000001"
1024 }
1025 },
1026 "outputSelection": {
1027 "fileA": {
1028 "*": [
1029 "evm.bytecode.linkReferences"
1030 ]
1031 }
1032 }
1033 },
1034 "sources": {
1035 "fileA": {
1036 "content": "object \"a\" { code { let addr := linkersymbol(\"fileC:L\") sstore(0, addr) } }"
1037 }
1038 }
1039 }
1040 )";
1041 Json::Value result = compile(input);
1042 BOOST_TEST(containsAtMostWarnings(result));
1043 Json::Value contractResult = getContractResult(result, "fileA", "a");
1044 expectLinkReferences(contractResult, {{"fileC", {"L"}}});
1045 }
1046
BOOST_AUTO_TEST_CASE(evm_version)1047 BOOST_AUTO_TEST_CASE(evm_version)
1048 {
1049 auto inputForVersion = [](string const& _version)
1050 {
1051 return R"(
1052 {
1053 "language": "Solidity",
1054 "sources": { "fileA": { "content": "contract A { }" } },
1055 "settings": {
1056 )" + _version + R"(
1057 "outputSelection": {
1058 "fileA": {
1059 "A": [ "metadata" ]
1060 }
1061 }
1062 }
1063 }
1064 )";
1065 };
1066 Json::Value result;
1067 result = compile(inputForVersion("\"evmVersion\": \"homestead\","));
1068 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"homestead\"") != string::npos);
1069 result = compile(inputForVersion("\"evmVersion\": \"tangerineWhistle\","));
1070 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"tangerineWhistle\"") != string::npos);
1071 result = compile(inputForVersion("\"evmVersion\": \"spuriousDragon\","));
1072 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos);
1073 result = compile(inputForVersion("\"evmVersion\": \"byzantium\","));
1074 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos);
1075 result = compile(inputForVersion("\"evmVersion\": \"constantinople\","));
1076 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"constantinople\"") != string::npos);
1077 result = compile(inputForVersion("\"evmVersion\": \"petersburg\","));
1078 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"petersburg\"") != string::npos);
1079 result = compile(inputForVersion("\"evmVersion\": \"istanbul\","));
1080 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"istanbul\"") != string::npos);
1081 result = compile(inputForVersion("\"evmVersion\": \"berlin\","));
1082 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"berlin\"") != string::npos);
1083 result = compile(inputForVersion("\"evmVersion\": \"london\","));
1084 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"london\"") != string::npos);
1085 // test default
1086 result = compile(inputForVersion(""));
1087 BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"london\"") != string::npos);
1088 // test invalid
1089 result = compile(inputForVersion("\"evmVersion\": \"invalid\","));
1090 BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested.");
1091 }
1092
BOOST_AUTO_TEST_CASE(optimizer_settings_default_disabled)1093 BOOST_AUTO_TEST_CASE(optimizer_settings_default_disabled)
1094 {
1095 char const* input = R"(
1096 {
1097 "language": "Solidity",
1098 "settings": {
1099 "outputSelection": {
1100 "fileA": { "A": [ "metadata" ] }
1101 }
1102 },
1103 "sources": {
1104 "fileA": {
1105 "content": "contract A { }"
1106 }
1107 }
1108 }
1109 )";
1110 Json::Value result = compile(input);
1111 BOOST_CHECK(containsAtMostWarnings(result));
1112 Json::Value contract = getContractResult(result, "fileA", "A");
1113 BOOST_CHECK(contract.isObject());
1114 BOOST_CHECK(contract["metadata"].isString());
1115 Json::Value metadata;
1116 BOOST_CHECK(util::jsonParseStrict(contract["metadata"].asString(), metadata));
1117
1118 Json::Value const& optimizer = metadata["settings"]["optimizer"];
1119 BOOST_CHECK(optimizer.isMember("enabled"));
1120 BOOST_CHECK(optimizer["enabled"].asBool() == false);
1121 BOOST_CHECK(!optimizer.isMember("details"));
1122 BOOST_CHECK(optimizer["runs"].asUInt() == 200);
1123 }
1124
BOOST_AUTO_TEST_CASE(optimizer_settings_default_enabled)1125 BOOST_AUTO_TEST_CASE(optimizer_settings_default_enabled)
1126 {
1127 char const* input = R"(
1128 {
1129 "language": "Solidity",
1130 "settings": {
1131 "outputSelection": {
1132 "fileA": { "A": [ "metadata" ] }
1133 },
1134 "optimizer": { "enabled": true }
1135 },
1136 "sources": {
1137 "fileA": {
1138 "content": "contract A { }"
1139 }
1140 }
1141 }
1142 )";
1143 Json::Value result = compile(input);
1144 BOOST_CHECK(containsAtMostWarnings(result));
1145 Json::Value contract = getContractResult(result, "fileA", "A");
1146 BOOST_CHECK(contract.isObject());
1147 BOOST_CHECK(contract["metadata"].isString());
1148 Json::Value metadata;
1149 BOOST_CHECK(util::jsonParseStrict(contract["metadata"].asString(), metadata));
1150
1151 Json::Value const& optimizer = metadata["settings"]["optimizer"];
1152 BOOST_CHECK(optimizer.isMember("enabled"));
1153 BOOST_CHECK(optimizer["enabled"].asBool() == true);
1154 BOOST_CHECK(!optimizer.isMember("details"));
1155 BOOST_CHECK(optimizer["runs"].asUInt() == 200);
1156 }
1157
BOOST_AUTO_TEST_CASE(optimizer_settings_details_exactly_as_default_disabled)1158 BOOST_AUTO_TEST_CASE(optimizer_settings_details_exactly_as_default_disabled)
1159 {
1160 char const* input = R"(
1161 {
1162 "language": "Solidity",
1163 "settings": {
1164 "outputSelection": {
1165 "fileA": { "A": [ "metadata" ] }
1166 },
1167 "optimizer": { "details": {
1168 "constantOptimizer" : false,
1169 "cse" : false,
1170 "deduplicate" : false,
1171 "jumpdestRemover" : true,
1172 "orderLiterals" : false,
1173 "peephole" : true
1174 } }
1175 },
1176 "sources": {
1177 "fileA": {
1178 "content": "contract A { }"
1179 }
1180 }
1181 }
1182 )";
1183 Json::Value result = compile(input);
1184 BOOST_CHECK(containsAtMostWarnings(result));
1185 Json::Value contract = getContractResult(result, "fileA", "A");
1186 BOOST_CHECK(contract.isObject());
1187 BOOST_CHECK(contract["metadata"].isString());
1188 Json::Value metadata;
1189 BOOST_CHECK(util::jsonParseStrict(contract["metadata"].asString(), metadata));
1190
1191 Json::Value const& optimizer = metadata["settings"]["optimizer"];
1192 BOOST_CHECK(optimizer.isMember("enabled"));
1193 // enabled is switched to false instead!
1194 BOOST_CHECK(optimizer["enabled"].asBool() == false);
1195 BOOST_CHECK(!optimizer.isMember("details"));
1196 BOOST_CHECK(optimizer["runs"].asUInt() == 200);
1197 }
1198
BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)1199 BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
1200 {
1201 char const* input = R"(
1202 {
1203 "language": "Solidity",
1204 "settings": {
1205 "outputSelection": {
1206 "fileA": { "A": [ "metadata" ] }
1207 },
1208 "optimizer": { "runs": 600, "details": {
1209 "constantOptimizer" : true,
1210 "cse" : false,
1211 "deduplicate" : true,
1212 "jumpdestRemover" : true,
1213 "orderLiterals" : false,
1214 "peephole" : true,
1215 "yul": true,
1216 "inliner": true
1217 } }
1218 },
1219 "sources": {
1220 "fileA": {
1221 "content": "contract A { }"
1222 }
1223 }
1224 }
1225 )";
1226 Json::Value result = compile(input);
1227 BOOST_CHECK(containsAtMostWarnings(result));
1228 Json::Value contract = getContractResult(result, "fileA", "A");
1229 BOOST_CHECK(contract.isObject());
1230 BOOST_CHECK(contract["metadata"].isString());
1231 Json::Value metadata;
1232 BOOST_CHECK(util::jsonParseStrict(contract["metadata"].asString(), metadata));
1233
1234 Json::Value const& optimizer = metadata["settings"]["optimizer"];
1235 BOOST_CHECK(!optimizer.isMember("enabled"));
1236 BOOST_CHECK(optimizer.isMember("details"));
1237 BOOST_CHECK(optimizer["details"]["constantOptimizer"].asBool() == true);
1238 BOOST_CHECK(optimizer["details"]["cse"].asBool() == false);
1239 BOOST_CHECK(optimizer["details"]["deduplicate"].asBool() == true);
1240 BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true);
1241 BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false);
1242 BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true);
1243 BOOST_CHECK(optimizer["details"]["yul"].asBool() == true);
1244 BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
1245 BOOST_CHECK(
1246 util::convertContainer<set<string>>(optimizer["details"]["yulDetails"].getMemberNames()) ==
1247 (set<string>{"stackAllocation", "optimizerSteps"})
1248 );
1249 BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
1250 BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps);
1251 BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 9);
1252 BOOST_CHECK(optimizer["runs"].asUInt() == 600);
1253 }
1254
BOOST_AUTO_TEST_CASE(metadata_without_compilation)1255 BOOST_AUTO_TEST_CASE(metadata_without_compilation)
1256 {
1257 // NOTE: the contract code here should fail to compile due to "out of stack"
1258 // If the metadata is successfully returned, that means no compilation was attempted.
1259 char const* input = R"(
1260 {
1261 "language": "Solidity",
1262 "settings": {
1263 "outputSelection": {
1264 "fileA": { "A": [ "metadata" ] }
1265 }
1266 },
1267 "sources": {
1268 "fileA": {
1269 "content": "contract A {
1270 function x(uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h, uint i, uint j, uint k, uint l, uint m, uint n, uint o, uint p) pure public {}
1271 function y() pure public {
1272 uint a; uint b; uint c; uint d; uint e; uint f; uint g; uint h; uint i; uint j; uint k; uint l; uint m; uint n; uint o; uint p;
1273 x(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
1274 }
1275 }"
1276 }
1277 }
1278 }
1279 )";
1280 Json::Value result = compile(input);
1281 BOOST_CHECK(containsAtMostWarnings(result));
1282 Json::Value contract = getContractResult(result, "fileA", "A");
1283 BOOST_CHECK(contract.isObject());
1284 BOOST_CHECK(contract["metadata"].isString());
1285 BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString()));
1286 }
1287
1288
BOOST_AUTO_TEST_CASE(license_in_metadata)1289 BOOST_AUTO_TEST_CASE(license_in_metadata)
1290 {
1291 string const input = R"(
1292 {
1293 "language": "Solidity",
1294 "sources": {
1295 "fileA": { "content": "import \"fileB\"; contract A { } // SPDX-License-Identifier: GPL-3.0 \n" },
1296 "fileB": { "content": "import \"fileC\"; /* SPDX-License-Identifier: MIT */ contract B { }" },
1297 "fileC": { "content": "import \"fileD\"; /* SPDX-License-Identifier: MIT AND GPL-3.0 */ contract C { }" },
1298 "fileD": { "content": "// SPDX-License-Identifier: (GPL-3.0+ OR MIT) AND MIT \n import \"fileE\"; contract D { }" },
1299 "fileE": { "content": "import \"fileF\"; /// SPDX-License-Identifier: MIT \n contract E { }" },
1300 "fileF": { "content": "/*\n * SPDX-License-Identifier: MIT\n */ contract F { }" }
1301 },
1302 "settings": {
1303 "outputSelection": {
1304 "fileA": {
1305 "*": [ "metadata" ]
1306 }
1307 }
1308 }
1309 }
1310 )";
1311 Json::Value result = compile(input);
1312 BOOST_CHECK(containsAtMostWarnings(result));
1313 Json::Value contract = getContractResult(result, "fileA", "A");
1314 BOOST_CHECK(contract.isObject());
1315 BOOST_CHECK(contract["metadata"].isString());
1316 Json::Value metadata;
1317 BOOST_REQUIRE(util::jsonParseStrict(contract["metadata"].asString(), metadata));
1318 BOOST_CHECK_EQUAL(metadata["sources"]["fileA"]["license"], "GPL-3.0");
1319 BOOST_CHECK_EQUAL(metadata["sources"]["fileB"]["license"], "MIT");
1320 BOOST_CHECK_EQUAL(metadata["sources"]["fileC"]["license"], "MIT AND GPL-3.0");
1321 BOOST_CHECK_EQUAL(metadata["sources"]["fileD"]["license"], "(GPL-3.0+ OR MIT) AND MIT");
1322 // This is actually part of the docstring, but still picked up
1323 // because the source location of the contract does not cover the docstring.
1324 BOOST_CHECK_EQUAL(metadata["sources"]["fileE"]["license"], "MIT");
1325 BOOST_CHECK_EQUAL(metadata["sources"]["fileF"]["license"], "MIT");
1326 }
1327
BOOST_AUTO_TEST_CASE(common_pattern)1328 BOOST_AUTO_TEST_CASE(common_pattern)
1329 {
1330 char const* input = R"(
1331 {
1332 "language": "Solidity",
1333 "settings": {
1334 "outputSelection": {
1335 "*": {
1336 "*": [ "evm.bytecode.object", "metadata" ]
1337 }
1338 }
1339 },
1340 "sources": {
1341 "fileA": {
1342 "content": "contract A { function f() pure public {} }"
1343 }
1344 }
1345 }
1346 )";
1347 Json::Value result = compile(input);
1348 BOOST_CHECK(containsAtMostWarnings(result));
1349 Json::Value contract = getContractResult(result, "fileA", "A");
1350 BOOST_CHECK(contract.isObject());
1351 BOOST_CHECK(contract["metadata"].isString());
1352 BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString()));
1353 BOOST_CHECK(contract["evm"]["bytecode"].isObject());
1354 BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
1355 }
1356
BOOST_AUTO_TEST_CASE(use_stack_optimization)1357 BOOST_AUTO_TEST_CASE(use_stack_optimization)
1358 {
1359 // NOTE: the contract code here should fail to compile due to "out of stack"
1360 // If we enable stack optimization, though, it will compile.
1361 char const* input = R"(
1362 {
1363 "language": "Solidity",
1364 "settings": {
1365 "optimizer": { "enabled": true, "details": { "yul": true } },
1366 "outputSelection": {
1367 "fileA": { "A": [ "evm.bytecode.object" ] }
1368 }
1369 },
1370 "sources": {
1371 "fileA": {
1372 "content": "contract A {
1373 function y() public {
1374 assembly {
1375 function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3
1376 {
1377 let a := 1
1378 let b := 1
1379 let z3 := 1
1380 sstore(a, b)
1381 sstore(add(a, 1), b)
1382 sstore(add(a, 2), b)
1383 sstore(add(a, 3), b)
1384 sstore(add(a, 4), b)
1385 sstore(add(a, 5), b)
1386 sstore(add(a, 6), b)
1387 sstore(add(a, 7), b)
1388 sstore(add(a, 8), b)
1389 sstore(add(a, 9), b)
1390 sstore(add(a, 10), b)
1391 sstore(add(a, 11), b)
1392 sstore(add(a, 12), b)
1393 }
1394 let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun()
1395 let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun()
1396 sstore(a1, a2)
1397 }
1398 }
1399 }"
1400 }
1401 }
1402 }
1403 )";
1404
1405 Json::Value parsedInput;
1406 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1407
1408 solidity::frontend::StandardCompiler compiler;
1409 Json::Value result = compiler.compile(parsedInput);
1410
1411 BOOST_CHECK(containsAtMostWarnings(result));
1412 Json::Value contract = getContractResult(result, "fileA", "A");
1413 BOOST_REQUIRE(contract.isObject());
1414 BOOST_REQUIRE(contract["evm"]["bytecode"]["object"].isString());
1415 BOOST_CHECK(contract["evm"]["bytecode"]["object"].asString().length() > 20);
1416
1417 // Now disable stack optimizations and UnusedFunctionParameterPruner (p)
1418 // results in "stack too deep"
1419 string optimiserSteps = OptimiserSettings::DefaultYulOptimiserSteps;
1420 optimiserSteps.erase(
__anon835bba630302(char ch) 1421 remove_if(optimiserSteps.begin(), optimiserSteps.end(), [](char ch) { return ch == 'p'; }),
1422 optimiserSteps.end()
1423 );
1424 parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["stackAllocation"] = false;
1425 parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["optimizerSteps"] = optimiserSteps;
1426
1427 result = compiler.compile(parsedInput);
1428 BOOST_REQUIRE(result["errors"].isArray());
1429 BOOST_CHECK(result["errors"][0]["severity"] == "error");
1430 BOOST_REQUIRE(result["errors"][0]["message"].isString());
1431 BOOST_CHECK(result["errors"][0]["message"].asString().find("Stack too deep when compiling inline assembly") != std::string::npos);
1432 BOOST_CHECK(result["errors"][0]["type"] == "CompilerError");
1433 }
1434
1435 BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard)
1436 {
1437 char const* input = R"(
1438 {
1439 "language": "Solidity",
1440 "sources":
1441 {
1442 "A":
1443 {
1444 "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
1445 }
1446 },
1447 "settings":
1448 {
1449 "outputSelection":
1450 {
1451 "*": { "C": ["evm.bytecode"] }
1452 }
1453 }
1454 }
1455 )";
1456
1457 Json::Value parsedInput;
1458 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1459
1460 solidity::frontend::StandardCompiler compiler;
1461 Json::Value result = compiler.compile(parsedInput);
1462
1463 BOOST_REQUIRE(result["contracts"].isObject());
1464 BOOST_REQUIRE(result["contracts"].size() == 1);
1465 BOOST_REQUIRE(result["contracts"]["A"].isObject());
1466 BOOST_REQUIRE(result["contracts"]["A"].size() == 1);
1467 BOOST_REQUIRE(result["contracts"]["A"]["C"].isObject());
1468 BOOST_REQUIRE(result["contracts"]["A"]["C"]["evm"].isObject());
1469 BOOST_REQUIRE(result["contracts"]["A"]["C"]["evm"]["bytecode"].isObject());
1470 BOOST_REQUIRE(result["sources"].isObject());
1471 BOOST_REQUIRE(result["sources"].size() == 1);
1472 BOOST_REQUIRE(result["sources"]["A"].isObject());
1473
1474 }
1475
BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_colon_source)1476 BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_colon_source)
1477 {
1478 char const* input = R"(
1479 {
1480 "language": "Solidity",
1481 "sources":
1482 {
1483 ":A":
1484 {
1485 "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
1486 }
1487 },
1488 "settings":
1489 {
1490 "outputSelection":
1491 {
1492 "*": { "C": ["evm.bytecode"] }
1493 }
1494 }
1495 }
1496 )";
1497
1498 Json::Value parsedInput;
1499 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1500
1501 solidity::frontend::StandardCompiler compiler;
1502 Json::Value result = compiler.compile(parsedInput);
1503
1504 BOOST_REQUIRE(result["contracts"].isObject());
1505 BOOST_REQUIRE(result["contracts"].size() == 1);
1506 BOOST_REQUIRE(result["contracts"][":A"].isObject());
1507 BOOST_REQUIRE(result["contracts"][":A"].size() == 1);
1508 BOOST_REQUIRE(result["contracts"][":A"]["C"].isObject());
1509 BOOST_REQUIRE(result["contracts"][":A"]["C"]["evm"].isObject());
1510 BOOST_REQUIRE(result["contracts"][":A"]["C"]["evm"]["bytecode"].isObject());
1511 BOOST_REQUIRE(result["sources"].isObject());
1512 BOOST_REQUIRE(result["sources"].size() == 1);
1513 BOOST_REQUIRE(result["sources"][":A"].isObject());
1514 }
1515
BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_empty_source)1516 BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_empty_source)
1517 {
1518 char const* input = R"(
1519 {
1520 "language": "Solidity",
1521 "sources":
1522 {
1523 "":
1524 {
1525 "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
1526 }
1527 },
1528 "settings":
1529 {
1530 "outputSelection":
1531 {
1532 "*": { "C": ["evm.bytecode"] }
1533 }
1534 }
1535 }
1536 )";
1537
1538 Json::Value parsedInput;
1539 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1540
1541 solidity::frontend::StandardCompiler compiler;
1542 Json::Value result = compiler.compile(parsedInput);
1543
1544 BOOST_REQUIRE(result["contracts"].isObject());
1545 BOOST_REQUIRE(result["contracts"].size() == 1);
1546 BOOST_REQUIRE(result["contracts"][""].isObject());
1547 BOOST_REQUIRE(result["contracts"][""].size() == 1);
1548 BOOST_REQUIRE(result["contracts"][""]["C"].isObject());
1549 BOOST_REQUIRE(result["contracts"][""]["C"]["evm"].isObject());
1550 BOOST_REQUIRE(result["contracts"][""]["C"]["evm"]["bytecode"].isObject());
1551 BOOST_REQUIRE(result["sources"].isObject());
1552 BOOST_REQUIRE(result["sources"].size() == 1);
1553 BOOST_REQUIRE(result["sources"][""].isObject());
1554 }
1555
BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_multiple_sources)1556 BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_multiple_sources)
1557 {
1558 char const* input = R"(
1559 {
1560 "language": "Solidity",
1561 "sources":
1562 {
1563 "A":
1564 {
1565 "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
1566 },
1567 "B":
1568 {
1569 "content": "pragma solidity >=0.0; contract D { function f() public pure {} }"
1570 }
1571 },
1572 "settings":
1573 {
1574 "outputSelection":
1575 {
1576 "*": { "D": ["evm.bytecode"] }
1577 }
1578 }
1579 }
1580 )";
1581
1582 Json::Value parsedInput;
1583 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1584
1585 solidity::frontend::StandardCompiler compiler;
1586 Json::Value result = compiler.compile(parsedInput);
1587
1588 BOOST_REQUIRE(result["contracts"].isObject());
1589 BOOST_REQUIRE(result["contracts"].size() == 1);
1590 BOOST_REQUIRE(result["contracts"]["B"].isObject());
1591 BOOST_REQUIRE(result["contracts"]["B"].size() == 1);
1592 BOOST_REQUIRE(result["contracts"]["B"]["D"].isObject());
1593 BOOST_REQUIRE(result["contracts"]["B"]["D"]["evm"].isObject());
1594 BOOST_REQUIRE(result["contracts"]["B"]["D"]["evm"]["bytecode"].isObject());
1595 BOOST_REQUIRE(result["sources"].isObject());
1596 BOOST_REQUIRE(result["sources"].size() == 2);
1597 BOOST_REQUIRE(result["sources"]["A"].isObject());
1598 BOOST_REQUIRE(result["sources"]["B"].isObject());
1599 }
1600
BOOST_AUTO_TEST_CASE(stopAfter_invalid_value)1601 BOOST_AUTO_TEST_CASE(stopAfter_invalid_value)
1602 {
1603 char const* input = R"(
1604 {
1605 "language": "Solidity",
1606 "sources":
1607 { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } },
1608 "settings":
1609 {
1610 "stopAfter": "rrr",
1611 "outputSelection":
1612 {
1613 "*": { "C": ["evm.bytecode"] }
1614 }
1615 }
1616 }
1617 )";
1618 Json::Value result = compile(input);
1619 BOOST_CHECK(containsError(result, "JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\"."));
1620 }
1621
BOOST_AUTO_TEST_CASE(stopAfter_invalid_type)1622 BOOST_AUTO_TEST_CASE(stopAfter_invalid_type)
1623 {
1624 char const* input = R"(
1625 {
1626 "language": "Solidity",
1627 "sources":
1628 { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } },
1629 "settings":
1630 {
1631 "stopAfter": 3,
1632 "outputSelection":
1633 {
1634 "*": { "C": ["evm.bytecode"] }
1635 }
1636 }
1637 }
1638 )";
1639 Json::Value result = compile(input);
1640 BOOST_CHECK(containsError(result, "JSONError", "\"settings.stopAfter\" must be a string."));
1641 }
1642
BOOST_AUTO_TEST_CASE(stopAfter_bin_conflict)1643 BOOST_AUTO_TEST_CASE(stopAfter_bin_conflict)
1644 {
1645 char const* input = R"(
1646 {
1647 "language": "Solidity",
1648 "sources":
1649 { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } },
1650 "settings":
1651 {
1652 "stopAfter": "parsing",
1653 "outputSelection":
1654 {
1655 "*": { "C": ["evm.bytecode"] }
1656 }
1657 }
1658 }
1659 )";
1660 Json::Value result = compile(input);
1661 BOOST_CHECK(containsError(result, "JSONError", "Requested output selection conflicts with \"settings.stopAfter\"."));
1662 }
1663
BOOST_AUTO_TEST_CASE(stopAfter_ast_output)1664 BOOST_AUTO_TEST_CASE(stopAfter_ast_output)
1665 {
1666 char const* input = R"(
1667 {
1668 "language": "Solidity",
1669 "sources": {
1670 "a.sol": {
1671 "content": "// SPDX-License-Identifier: GPL-3.0\nimport \"tes32.sol\";\n contract C is X { constructor() {} }"
1672 }
1673 },
1674 "settings": {
1675 "stopAfter": "parsing",
1676 "outputSelection": { "*": { "": [ "ast" ] } }
1677 }
1678 }
1679 )";
1680 Json::Value result = compile(input);
1681 BOOST_CHECK(result["sources"].isObject());
1682 BOOST_CHECK(result["sources"]["a.sol"].isObject());
1683 BOOST_CHECK(result["sources"]["a.sol"]["ast"].isObject());
1684 }
1685
BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract)1686 BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract)
1687 {
1688 char const* input = R"(
1689 {
1690 "language": "Solidity",
1691 "sources": {
1692 "BlockRewardAuRaBase.sol": {
1693 "content": " contract Sacrifice { constructor() payable {} } abstract contract BlockRewardAuRaBase { function _transferNativeReward() internal { new Sacrifice(); } function _distributeTokenRewards() internal virtual; } "
1694 },
1695 "BlockRewardAuRaCoins.sol": {
1696 "content": " import \"./BlockRewardAuRaBase.sol\"; contract BlockRewardAuRaCoins is BlockRewardAuRaBase { function transferReward() public { _transferNativeReward(); } function _distributeTokenRewards() internal override {} } "
1697 }
1698 },
1699 "settings": {
1700 "outputSelection": {
1701 "BlockRewardAuRaCoins.sol": {
1702 "BlockRewardAuRaCoins": ["ir", "evm.bytecode.sourceMap"]
1703 }
1704 }
1705 }
1706 }
1707 )";
1708
1709 Json::Value parsedInput;
1710 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1711
1712 solidity::frontend::StandardCompiler compiler;
1713 Json::Value result = compiler.compile(parsedInput);
1714
1715 BOOST_REQUIRE(result["contracts"].isObject());
1716 BOOST_REQUIRE(result["contracts"].size() == 1);
1717 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].isObject());
1718 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].size() == 1);
1719 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"].isObject());
1720 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"].isObject());
1721 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["ir"].isString());
1722 BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"]["bytecode"].isObject());
1723 BOOST_REQUIRE(result["sources"].isObject());
1724 BOOST_REQUIRE(result["sources"].size() == 2);
1725 }
1726
BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract_yul)1727 BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract_yul)
1728 {
1729 char const* input = R"(
1730 {
1731 "language": "Solidity",
1732 "sources": {
1733 "A.sol": {
1734 "content": "contract A {} contract B {} contract C { constructor() { new B(); } } contract D {}"
1735 }
1736 },
1737 "settings": {
1738 "outputSelection": {
1739 "A.sol": {
1740 "C": ["ir"]
1741 }
1742 }
1743 }
1744 }
1745 )";
1746
1747 Json::Value parsedInput;
1748 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1749
1750 solidity::frontend::StandardCompiler compiler;
1751 Json::Value result = compiler.compile(parsedInput);
1752
1753 BOOST_REQUIRE(result["contracts"].isObject());
1754 BOOST_REQUIRE(result["contracts"].size() == 1);
1755 BOOST_REQUIRE(result["contracts"]["A.sol"].isObject());
1756 BOOST_REQUIRE(result["contracts"]["A.sol"].size() == 1);
1757 BOOST_REQUIRE(result["contracts"]["A.sol"]["C"].isObject());
1758 BOOST_REQUIRE(result["contracts"]["A.sol"]["C"]["ir"].isString());
1759
1760 const string& irCode = result["contracts"]["A.sol"]["C"]["ir"].asString();
1761
1762 // Make sure C and B contracts are deployed
1763 BOOST_REQUIRE(irCode.find("object \"C") != string::npos);
1764 BOOST_REQUIRE(irCode.find("object \"B") != string::npos);
1765
1766 // Make sure A and D are NOT deployed as they were not requested and are not
1767 // in any dependency
1768 BOOST_REQUIRE(irCode.find("object \"A") == string::npos);
1769 BOOST_REQUIRE(irCode.find("object \"D") == string::npos);
1770
1771
1772 BOOST_REQUIRE(result["sources"].isObject());
1773 BOOST_REQUIRE(result["sources"].size() == 1);
1774 }
1775
BOOST_AUTO_TEST_CASE(source_location_of_bare_block)1776 BOOST_AUTO_TEST_CASE(source_location_of_bare_block)
1777 {
1778 char const* input = R"(
1779 {
1780 "language": "Solidity",
1781 "sources": {
1782 "A.sol": {
1783 "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }"
1784 }
1785 },
1786 "settings": {
1787 "outputSelection": {
1788 "A.sol": {
1789 "A": ["evm.bytecode.sourceMap"]
1790 }
1791 }
1792 }
1793 }
1794 )";
1795
1796 Json::Value parsedInput;
1797 BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput));
1798
1799 solidity::frontend::StandardCompiler compiler;
1800 Json::Value result = compiler.compile(parsedInput);
1801
1802 string sourceMap = result["contracts"]["A.sol"]["A"]["evm"]["bytecode"]["sourceMap"].asString();
1803
1804 // Check that the bare block's source location is referenced.
1805 string sourceRef =
1806 ";" +
1807 to_string(string{"contract A { constructor() { uint x = 2; "}.size()) +
1808 ":" +
1809 to_string(string{"{ uint y = 3; }"}.size());
1810 BOOST_REQUIRE(sourceMap.find(sourceRef) != string::npos);
1811 }
1812
1813 BOOST_AUTO_TEST_SUITE_END()
1814
1815 } // end namespaces
1816