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