1 /*
2 	This file is part of solidity.
3 	solidity is free software: you can redistribute it and/or modify
4 	it under the terms of the GNU General Public License as published by
5 	the Free Software Foundation, either version 3 of the License, or
6 	(at your option) any later version.
7 	solidity is distributed in the hope that it will be useful,
8 	but WITHOUT ANY WARRANTY; without even the implied warranty of
9 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 	GNU General Public License for more details.
11 	You should have received a copy of the GNU General Public License
12 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
13 */
14 
15 #include <test/libsolidity/SemanticTest.h>
16 
17 #include <libsolutil/Whiskers.h>
18 #include <libyul/Exceptions.h>
19 #include <test/Common.h>
20 #include <test/libsolidity/util/BytesUtils.h>
21 
22 #include <boost/algorithm/string.hpp>
23 #include <boost/algorithm/string/predicate.hpp>
24 #include <boost/algorithm/string/trim.hpp>
25 #include <boost/throw_exception.hpp>
26 
27 #include <algorithm>
28 #include <cctype>
29 #include <fstream>
30 #include <functional>
31 #include <memory>
32 #include <optional>
33 #include <stdexcept>
34 #include <string>
35 #include <utility>
36 
37 using namespace std;
38 using namespace solidity;
39 using namespace solidity::yul;
40 using namespace solidity::langutil;
41 using namespace solidity::util;
42 using namespace solidity::util::formatting;
43 using namespace solidity::frontend::test;
44 using namespace boost::algorithm;
45 using namespace boost::unit_test;
46 namespace fs = boost::filesystem;
47 
SemanticTest(string const & _filename,langutil::EVMVersion _evmVersion,vector<boost::filesystem::path> const & _vmPaths,bool _enforceViaYul,bool _enforceCompileToEwasm,bool _enforceGasCost,u256 _enforceGasCostMinValue)48 SemanticTest::SemanticTest(
49 	string const& _filename,
50 	langutil::EVMVersion _evmVersion,
51 	vector<boost::filesystem::path> const& _vmPaths,
52 	bool _enforceViaYul,
53 	bool _enforceCompileToEwasm,
54 	bool _enforceGasCost,
55 	u256 _enforceGasCostMinValue
56 ):
57 	SolidityExecutionFramework(_evmVersion, _vmPaths),
58 	EVMVersionRestrictedTestCase(_filename),
59 	m_sources(m_reader.sources()),
60 	m_lineOffset(m_reader.lineNumber()),
61 	m_builtins(makeBuiltins()),
62 	m_sideEffectHooks(makeSideEffectHooks()),
63 	m_enforceViaYul(_enforceViaYul),
64 	m_enforceCompileToEwasm(_enforceCompileToEwasm),
65 	m_enforceGasCost(_enforceGasCost),
66 	m_enforceGasCostMinValue(move(_enforceGasCostMinValue))
67 {
68 	static set<string> const compileViaYulAllowedValues{"also", "true", "false", "default"};
69 	static set<string> const yulRunTriggers{"also", "true"};
70 	static set<string> const legacyRunTriggers{"also", "false", "default"};
71 
72 	string compileViaYul = m_reader.stringSetting("compileViaYul", "default");
73 	if (!contains(compileViaYulAllowedValues, compileViaYul))
74 		BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + compileViaYul + "."));
75 	m_testCaseWantsYulRun = contains(yulRunTriggers, compileViaYul);
76 	m_testCaseWantsLegacyRun = contains(legacyRunTriggers, compileViaYul);
77 
78 	// Do not enforce via yul and ewasm, if via yul was explicitly denied.
79 	if (compileViaYul == "false")
80 	{
81 		m_enforceViaYul = false;
82 		m_enforceCompileToEwasm = false;
83 	}
84 
85 	string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false");
86 	if (compileToEwasm == "also")
87 		m_testCaseWantsEwasmRun = true;
88 	else if (compileToEwasm == "false")
89 		m_testCaseWantsEwasmRun = false;
90 	else
91 		BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + "."));
92 
93 	if (m_testCaseWantsEwasmRun && !m_testCaseWantsYulRun)
94 		BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled."));
95 
96 	// run ewasm tests only if an ewasm evmc vm was defined
97 	if (m_testCaseWantsEwasmRun && !m_supportsEwasm)
98 		m_testCaseWantsEwasmRun = false;
99 
100 	m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
101 	if (m_runWithABIEncoderV1Only && !solidity::test::CommonOptions::get().useABIEncoderV1)
102 		m_shouldRun = false;
103 
104 	// Sanity check
105 	if (m_runWithABIEncoderV1Only && (compileViaYul == "true" || compileViaYul == "also"))
106 		BOOST_THROW_EXCEPTION(runtime_error(
107 			"ABIEncoderV1Only can not be used with compileViaYul=" + compileViaYul +
108 			", set it to false or omit the flag. The compileViaYul setting ignores the abicoder pragma"
109 			" and runs everything with ABICoder V2."
110 		));
111 
112 	auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default"));
113 	soltestAssert(revertStrings, "Invalid revertStrings setting.");
114 	m_revertStrings = revertStrings.value();
115 
116 	m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false);
117 
118 	parseExpectations(m_reader.stream());
119 	soltestAssert(!m_tests.empty(), "No tests specified in " + _filename);
120 
121 	if (m_enforceGasCost)
122 	{
123 		m_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata);
124 		m_compiler.setMetadataHash(CompilerStack::MetadataHash::None);
125 	}
126 }
127 
makeBuiltins()128 map<string, Builtin> SemanticTest::makeBuiltins()
129 {
130 	return {
131 		{
132 			"isoltest_builtin_test",
133 			[](FunctionCall const&) -> optional<bytes>
134 			{
135 				return toBigEndian(u256(0x1234));
136 			}
137 		},
138 		{
139 			"isoltest_side_effects_test",
140 			[](FunctionCall const& _call) -> optional<bytes>
141 			{
142 				if (_call.arguments.parameters.empty())
143 					return toBigEndian(0);
144 				else
145 					return _call.arguments.rawBytes();
146 			}
147 		},
148 		{
149 			"balance",
150 			[this](FunctionCall const& _call) -> optional<bytes>
151 			{
152 				soltestAssert(_call.arguments.parameters.size() <= 1, "Account address expected.");
153 				h160 address;
154 				if (_call.arguments.parameters.size() == 1)
155 					address = h160(_call.arguments.parameters.at(0).rawString);
156 				else
157 					address = m_contractAddress;
158 				return toBigEndian(balanceAt(address));
159 			}
160 		},
161 		{
162 			"storageEmpty",
163 			[this](FunctionCall const& _call) -> optional<bytes>
164 			{
165 				soltestAssert(_call.arguments.parameters.empty(), "No arguments expected.");
166 				return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0));
167 		 	}
168 		},
169 		{
170 			"account",
171 			[this](FunctionCall const& _call) -> optional<bytes>
172 			{
173 				soltestAssert(_call.arguments.parameters.size() == 1, "Account number expected.");
174 				size_t accountNumber = static_cast<size_t>(stoi(_call.arguments.parameters.at(0).rawString));
175 				// Need to pad it to 32-bytes to workaround limitations in BytesUtils::formatHex.
176 				return toBigEndian(h256(ExecutionFramework::setAccount(accountNumber).asBytes(), h256::AlignRight));
177 			}
178 		},
179 	};
180 }
181 
makeSideEffectHooks() const182 vector<SideEffectHook> SemanticTest::makeSideEffectHooks() const
183 {
184 	using namespace std::placeholders;
185 	return {
186 		[](FunctionCall const& _call) -> vector<string>
187 		{
188 			if (_call.signature == "isoltest_side_effects_test")
189 			{
190 				vector<string> result;
191 				for (auto const& argument: _call.arguments.parameters)
192 					result.emplace_back(toHex(argument.rawBytes));
193 				return result;
194 			}
195 			return {};
196 		},
197 		bind(&SemanticTest::eventSideEffectHook, this, _1)
198 	};
199 }
200 
formatEventParameter(optional<AnnotatedEventSignature> _signature,bool _indexed,size_t _index,bytes const & _data)201 string SemanticTest::formatEventParameter(optional<AnnotatedEventSignature> _signature, bool _indexed, size_t _index, bytes const& _data)
202 {
203 	auto isPrintableASCII = [](bytes const& s)
204 	{
205 		bool zeroes = true;
206 		for (auto c: s)
207 		{
208 			if (static_cast<unsigned>(c) != 0x00)
209 			{
210 				zeroes = false;
211 				if (static_cast<unsigned>(c) <= 0x1f || static_cast<unsigned>(c) >= 0x7f)
212 					return false;
213 			} else
214 				break;
215 		}
216 		return !zeroes;
217 	};
218 
219 	ABIType abiType(ABIType::Type::Hex);
220 	if (isPrintableASCII(_data))
221 		abiType = ABIType(ABIType::Type::String);
222 	if (_signature.has_value())
223 	{
224 		vector<string> const& types = _indexed ? _signature->indexedTypes : _signature->nonIndexedTypes;
225 		if (_index < types.size())
226 		{
227 			if (types.at(_index) == "bool")
228 				abiType = ABIType(ABIType::Type::Boolean);
229 		}
230 	}
231 	return BytesUtils::formatBytes(_data, abiType);
232 }
233 
eventSideEffectHook(FunctionCall const &) const234 vector<string> SemanticTest::eventSideEffectHook(FunctionCall const&) const
235 {
236 	vector<string> sideEffects;
237 	vector<LogRecord> recordedLogs = ExecutionFramework::recordedLogs();
238 	for (LogRecord const& log: recordedLogs)
239 	{
240 		optional<AnnotatedEventSignature> eventSignature;
241 		if (!log.topics.empty())
242 			eventSignature = matchEvent(log.topics[0]);
243 		stringstream sideEffect;
244 		sideEffect << "emit ";
245 		if (eventSignature.has_value())
246 			sideEffect << eventSignature.value().signature;
247 		else
248 			sideEffect << "<anonymous>";
249 
250 		if (m_contractAddress != log.creator)
251 			sideEffect << " from 0x" << log.creator;
252 
253 		vector<string> eventStrings;
254 		size_t index{0};
255 		for (h256 const& topic: log.topics)
256 		{
257 			if (!eventSignature.has_value() || index != 0)
258 				eventStrings.push_back("#" + formatEventParameter(eventSignature, true, index, topic.asBytes()));
259 			++index;
260 		}
261 
262 		soltestAssert(log.data.size() % 32 == 0, "");
263 		for (size_t index = 0; index < log.data.size() / 32; ++index)
264 		{
265 			auto begin = log.data.begin() + static_cast<long>(index * 32);
266 			bytes const& data = bytes{begin, begin + 32};
267 			eventStrings.emplace_back(formatEventParameter(eventSignature, false, index, data));
268 		}
269 
270 		if (!eventStrings.empty())
271 			sideEffect << ": ";
272 		sideEffect << joinHumanReadable(eventStrings);
273 		sideEffects.emplace_back(sideEffect.str());
274 	}
275 	return sideEffects;
276 }
277 
matchEvent(util::h256 const & hash) const278 optional<AnnotatedEventSignature> SemanticTest::matchEvent(util::h256 const& hash) const
279 {
280 	optional<AnnotatedEventSignature> result;
281 	for (string& contractName: m_compiler.contractNames())
282 	{
283 		ContractDefinition const& contract = m_compiler.contractDefinition(contractName);
284 		for (EventDefinition const* event: contract.events())
285 		{
286 			FunctionTypePointer eventFunctionType = event->functionType(true);
287 			if (!event->isAnonymous() && keccak256(eventFunctionType->externalSignature()) == hash)
288 			{
289 				AnnotatedEventSignature eventInfo;
290 				eventInfo.signature = eventFunctionType->externalSignature();
291 				for (auto const& param: event->parameters())
292 					if (param->isIndexed())
293 						eventInfo.indexedTypes.emplace_back(param->type()->toString(true));
294 					else
295 						eventInfo.nonIndexedTypes.emplace_back(param->type()->toString(true));
296 				result = eventInfo;
297 			}
298 		}
299 	}
300 	return result;
301 }
302 
run(ostream & _stream,string const & _linePrefix,bool _formatted)303 TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
304 {
305 	TestResult result = TestResult::Success;
306 
307 	if (m_testCaseWantsLegacyRun)
308 		result = runTest(_stream, _linePrefix, _formatted, false, false);
309 
310 	if ((m_testCaseWantsYulRun || m_enforceViaYul) && result == TestResult::Success)
311 		result = runTest(_stream, _linePrefix, _formatted, true, false);
312 
313 	if ((m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success)
314 	{
315 		// TODO: Once we have full Ewasm support, we could remove try/catch here.
316 		try
317 		{
318 			result = runTest(_stream, _linePrefix, _formatted, true, true);
319 		}
320 		catch (...)
321 		{
322 			if (!m_enforceCompileToEwasm)
323 				throw;
324 		}
325 	}
326 	return result;
327 }
328 
runTest(ostream & _stream,string const & _linePrefix,bool _formatted,bool _isYulRun,bool _isEwasmRun)329 TestCase::TestResult SemanticTest::runTest(
330 	ostream& _stream,
331 	string const& _linePrefix,
332 	bool _formatted,
333 	bool _isYulRun,
334 	bool _isEwasmRun)
335 {
336 	bool success = true;
337 	m_gasCostFailure = false;
338 
339 	if (_isEwasmRun)
340 	{
341 		soltestAssert(_isYulRun, "");
342 		selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM);
343 	}
344 	else
345 		selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
346 
347 	reset();
348 
349 	m_compileViaYul = _isYulRun;
350 	if (_isEwasmRun)
351 	{
352 		soltestAssert(m_compileViaYul, "");
353 		m_compileToEwasm = _isEwasmRun;
354 	}
355 
356 	m_canEnableYulRun = false;
357 	m_canEnableEwasmRun = false;
358 
359 	if (_isYulRun)
360 		AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul" << (_isEwasmRun ? " (ewasm):" : ":") << endl;
361 
362 	for (TestFunctionCall& test: m_tests)
363 		test.reset();
364 
365 	map<string, solidity::test::Address> libraries;
366 
367 	bool constructed = false;
368 
369 	for (TestFunctionCall& test: m_tests)
370 	{
371 		if (constructed)
372 		{
373 			soltestAssert(
374 				test.call().kind != FunctionCall::Kind::Library,
375 				"Libraries have to be deployed before any other call."
376 			);
377 			soltestAssert(
378 				test.call().kind != FunctionCall::Kind::Constructor,
379 				"Constructor has to be the first function call expect for library deployments."
380 			);
381 		}
382 		else if (test.call().kind == FunctionCall::Kind::Library)
383 		{
384 			soltestAssert(
385 				deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
386 				"Failed to deploy library " + test.call().signature);
387 			// For convenience, in semantic tests we assume that an unqualified name like `L` is equivalent to one
388 			// with an empty source unit name (`:L`). This is fine because the compiler never uses unqualified
389 			// names in the Yul code it produces and does not allow `linkersymbol()` at all in inline assembly.
390 			if (test.call().signature.find(':') == string::npos)
391 				libraries[":" + test.call().signature] = m_contractAddress;
392 			else
393 				libraries[test.call().signature] = m_contractAddress;
394 			continue;
395 		}
396 		else
397 		{
398 			if (test.call().kind == FunctionCall::Kind::Constructor)
399 				deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
400 			else
401 				soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
402 			constructed = true;
403 		}
404 
405 		if (test.call().kind == FunctionCall::Kind::Constructor)
406 		{
407 			if (m_transactionSuccessful == test.call().expectations.failure)
408 				success = false;
409 			if (success && !checkGasCostExpectation(test, _isYulRun))
410 			{
411 				success = false;
412 				m_gasCostFailure = true;
413 			}
414 
415 			test.setFailure(!m_transactionSuccessful);
416 			test.setRawBytes(bytes());
417 		}
418 		else
419 		{
420 			bytes output;
421 			if (test.call().kind == FunctionCall::Kind::LowLevel)
422 				output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
423 			else if (test.call().kind == FunctionCall::Kind::Builtin)
424 			{
425 				optional<bytes> builtinOutput = m_builtins.at(test.call().signature)(test.call());
426 				if (builtinOutput.has_value())
427 				{
428 					m_transactionSuccessful = true;
429 					output = builtinOutput.value();
430 				}
431 				else
432 					m_transactionSuccessful = false;
433 			}
434 			else
435 			{
436 				soltestAssert(
437 					m_allowNonExistingFunctions ||
438 					m_compiler.methodIdentifiers(m_compiler.lastContractName(m_sources.mainSourceFile)).isMember(test.call().signature),
439 					"The function " + test.call().signature + " is not known to the compiler"
440 				);
441 
442 				output = callContractFunctionWithValueNoEncoding(
443 					test.call().signature,
444 					test.call().value.value,
445 					test.call().arguments.rawBytes()
446 				);
447 			}
448 
449 			bool outputMismatch = (output != test.call().expectations.rawBytes());
450 			if (!outputMismatch && !checkGasCostExpectation(test, _isYulRun))
451 			{
452 				success = false;
453 				m_gasCostFailure = true;
454 			}
455 
456 			// Pre byzantium, it was not possible to return failure data, so we disregard
457 			// output mismatch for those EVM versions.
458 			if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata())
459 				outputMismatch = false;
460 			if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch)
461 				success = false;
462 
463 			test.setFailure(!m_transactionSuccessful);
464 			test.setRawBytes(move(output));
465 			test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile)));
466 		}
467 
468 		vector<string> effects;
469 		for (SideEffectHook const& hook: m_sideEffectHooks)
470 			effects += hook(test.call());
471 		test.setSideEffects(move(effects));
472 
473 		success &= test.call().expectedSideEffects == test.call().actualSideEffects;
474 	}
475 
476 	if (!m_testCaseWantsYulRun && _isYulRun)
477 	{
478 		m_canEnableYulRun = success;
479 		string message = success ?
480 			"Test can pass via Yul, but marked with \"compileViaYul: false.\"" :
481 			"Test compiles via Yul, but it gives different test results.";
482 		AnsiColorized(_stream, _formatted, {BOLD, success ? YELLOW : MAGENTA}) <<
483 			_linePrefix << endl <<
484 			_linePrefix << message << endl;
485 		return TestResult::Failure;
486 	}
487 
488 	// Right now we have sometimes different test results in Yul vs. Ewasm.
489 	// The main reason is that Ewasm just returns a failure in some cases.
490 	// TODO: If Ewasm support got fully implemented, we could implement this in the same way as above.
491 	if (success && !m_testCaseWantsEwasmRun && _isEwasmRun)
492 	{
493 		// TODO: There is something missing in Ewasm to support other types of revert strings:
494 		//  for now, we just ignore test-cases that do not use RevertStrings::Default.
495 		if (m_revertStrings != RevertStrings::Default)
496 			return TestResult::Success;
497 
498 		m_canEnableEwasmRun = true;
499 		AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) <<
500 			_linePrefix << endl <<
501 			_linePrefix << "Test can pass via Yul (Ewasm), but marked with \"compileToEwasm: false.\"" << endl;
502 		return TestResult::Failure;
503 	}
504 
505 	if (!success)
506 	{
507 		// Ignore failing tests that can't yet get compiled to Ewasm:
508 		// if the test run was not successful and enforce compiling to ewasm was set,
509 		// but the test case did not want to get run with Ewasm, we just ignore this failure.
510 		if (m_enforceCompileToEwasm && !m_testCaseWantsEwasmRun)
511 			return TestResult::Success;
512 
513 		AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
514 		for (TestFunctionCall const& test: m_tests)
515 		{
516 			ErrorReporter errorReporter;
517 			_stream << test.format(
518 				errorReporter,
519 				_linePrefix,
520 				TestFunctionCall::RenderMode::ExpectedValuesExpectedGas,
521 				_formatted,
522 				/* _interactivePrint */ true
523 			) << endl;
524 			_stream << errorReporter.format(_linePrefix, _formatted);
525 		}
526 		_stream << endl;
527 		AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
528 		for (TestFunctionCall const& test: m_tests)
529 		{
530 			ErrorReporter errorReporter;
531 			_stream << test.format(
532 				errorReporter,
533 				_linePrefix,
534 				m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
535 				_formatted,
536 				/* _interactivePrint */ true
537 			) << endl;
538 			_stream << errorReporter.format(_linePrefix, _formatted);
539 		}
540 		AnsiColorized(_stream, _formatted, {BOLD, RED})
541 			<< _linePrefix << endl
542 			<< _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl;
543 		if (_isYulRun && m_testCaseWantsLegacyRun)
544 		{
545 			_stream << _linePrefix << endl << _linePrefix;
546 			AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul.";
547 			_stream << endl;
548 		}
549 		else if (!_isYulRun && m_testCaseWantsYulRun)
550 			AnsiColorized(_stream, _formatted, {BOLD, YELLOW})
551 				<< _linePrefix << endl
552 				<< _linePrefix << "Note that the test also has to pass via Yul." << endl;
553 		return TestResult::Failure;
554 	}
555 
556 	return TestResult::Success;
557 }
558 
checkGasCostExpectation(TestFunctionCall & io_test,bool _compileViaYul) const559 bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const
560 {
561 	string setting =
562 		(_compileViaYul ? "ir"s : "legacy"s) +
563 		(m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : "");
564 
565 	// We don't check gas if enforce gas cost is not active
566 	// or test is run with abi encoder v1 only
567 	// or gas used less than threshold for enforcing feature
568 	// or the test has used up all available gas (test will fail anyway)
569 	// or setting is "ir" and it's not included in expectations
570 	// or if the called function is an isoltest builtin e.g. `smokeTest` or `storageEmpty`
571 	if (
572 		!m_enforceGasCost ||
573 		m_gasUsed < m_enforceGasCostMinValue ||
574 		m_gasUsed >= InitialGas ||
575 		(setting == "ir" && io_test.call().expectations.gasUsed.count(setting) == 0) ||
576 		io_test.call().kind == FunctionCall::Kind::Builtin
577 	)
578 		return true;
579 
580 	solAssert(!m_runWithABIEncoderV1Only, "");
581 
582 	io_test.setGasCost(setting, m_gasUsed);
583 	return
584 		io_test.call().expectations.gasUsed.count(setting) > 0 &&
585 		m_gasUsed == io_test.call().expectations.gasUsed.at(setting);
586 }
587 
printSource(ostream & _stream,string const & _linePrefix,bool _formatted) const588 void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
589 {
590 	if (m_sources.sources.empty())
591 		return;
592 
593 	bool outputNames = (m_sources.sources.size() - m_sources.externalSources.size() != 1 || !m_sources.sources.begin()->first.empty());
594 
595 	set<string> externals;
596 	for (auto const& [name, path]: m_sources.externalSources)
597 	{
598 		externals.insert(name);
599 		string externalSource;
600 		if (name == path)
601 			externalSource = name;
602 		else
603 			externalSource = name + "=" + path.generic_string();
604 
605 		if (_formatted)
606 			_stream << _linePrefix  << formatting::CYAN << "==== ExternalSource: " << externalSource << " ===="s << formatting::RESET << endl;
607 		else
608 			_stream << _linePrefix << "==== ExternalSource: " << externalSource << " ===="s << endl;
609 	}
610 
611 	for (auto const& [name, source]: m_sources.sources)
612 		if (externals.find(name) == externals.end())
613 		{
614 			if (_formatted)
615 			{
616 				if (source.empty())
617 					continue;
618 
619 				if (outputNames)
620 					_stream << _linePrefix << formatting::CYAN << "==== Source: " << name
621 							<< " ====" << formatting::RESET << endl;
622 
623 				vector<char const*> sourceFormatting(source.length(), formatting::RESET);
624 				_stream << _linePrefix << sourceFormatting.front() << source.front();
625 				for (size_t i = 1; i < source.length(); i++)
626 				{
627 					if (sourceFormatting[i] != sourceFormatting[i - 1])
628 						_stream << sourceFormatting[i];
629 					if (source[i] != '\n')
630 						_stream << source[i];
631 					else
632 					{
633 						_stream << formatting::RESET << endl;
634 						if (i + 1 < source.length())
635 							_stream << _linePrefix << sourceFormatting[i];
636 					}
637 				}
638 				_stream << formatting::RESET;
639 			}
640 			else
641 			{
642 				if (outputNames)
643 					_stream << _linePrefix << "==== Source: " + name << " ====" << endl;
644 				stringstream stream(source);
645 				string line;
646 				while (getline(stream, line))
647 					_stream << _linePrefix << line << endl;
648 			}
649 		}
650 }
651 
printUpdatedExpectations(ostream & _stream,string const &) const652 void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
653 {
654 	for (TestFunctionCall const& test: m_tests)
655 		_stream << test.format(
656 			"",
657 			m_gasCostFailure ? TestFunctionCall::RenderMode::ExpectedValuesActualGas : TestFunctionCall::RenderMode::ActualValuesExpectedGas,
658 			/* _highlight = */ false
659 		) << endl;
660 }
661 
printUpdatedSettings(ostream & _stream,string const & _linePrefix)662 void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePrefix)
663 {
664 	auto& settings = m_reader.settings();
665 	if (settings.empty() && !m_canEnableYulRun)
666 		return;
667 
668 	_stream << _linePrefix << "// ====" << endl;
669 	if (m_canEnableEwasmRun)
670 	{
671 		soltestAssert(m_canEnableYulRun || m_testCaseWantsYulRun, "");
672 		string compileViaYul = m_reader.stringSetting("compileViaYul", "");
673 		if (!compileViaYul.empty())
674 			_stream << _linePrefix << "// compileViaYul: " << compileViaYul << "\n";
675 		_stream << _linePrefix << "// compileToEwasm: also\n";
676 	}
677 	else if (m_canEnableYulRun)
678 		_stream << _linePrefix << "// compileViaYul: also\n";
679 
680 	for (auto const& [settingName, settingValue]: settings)
681 		if (
682 			!(settingName == "compileToEwasm" && m_canEnableEwasmRun) &&
683 			!(settingName == "compileViaYul" && (m_canEnableYulRun || m_canEnableEwasmRun))
684 		)
685 			_stream << _linePrefix << "// " << settingName << ": " << settingValue<< endl;
686 }
687 
parseExpectations(istream & _stream)688 void SemanticTest::parseExpectations(istream& _stream)
689 {
690 	m_tests += TestFileParser{_stream, m_builtins}.parseFunctionCalls(m_lineOffset);
691 }
692 
deploy(string const & _contractName,u256 const & _value,bytes const & _arguments,map<string,solidity::test::Address> const & _libraries)693 bool SemanticTest::deploy(
694 	string const& _contractName,
695 	u256 const& _value,
696 	bytes const& _arguments,
697 	map<string, solidity::test::Address> const& _libraries
698 )
699 {
700 	auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries, m_sources.mainSourceFile);
701 	return !output.empty() && m_transactionSuccessful;
702 }
703