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  * @author Alex Beregszaszi
20  * @date 2016
21  * Standard JSON compiler interface.
22  */
23 
24 #include <libsolidity/interface/StandardCompiler.h>
25 #include <libsolidity/interface/ImportRemapper.h>
26 
27 #include <libsolidity/ast/ASTJsonConverter.h>
28 #include <libyul/AssemblyStack.h>
29 #include <libyul/Exceptions.h>
30 #include <libyul/optimiser/Suite.h>
31 
32 #include <libevmasm/Instruction.h>
33 
34 #include <libsmtutil/Exceptions.h>
35 
36 #include <liblangutil/SourceReferenceFormatter.h>
37 
38 #include <libsolutil/JSON.h>
39 #include <libsolutil/Keccak256.h>
40 #include <libsolutil/CommonData.h>
41 
42 #include <boost/algorithm/string/predicate.hpp>
43 
44 #include <algorithm>
45 #include <optional>
46 
47 using namespace std;
48 using namespace solidity;
49 using namespace solidity::yul;
50 using namespace solidity::frontend;
51 using namespace solidity::langutil;
52 
53 namespace
54 {
55 
formatError(Error::Severity _severity,string const & _type,string const & _component,string const & _message,string const & _formattedMessage="",Json::Value const & _sourceLocation=Json::Value (),Json::Value const & _secondarySourceLocation=Json::Value ())56 Json::Value formatError(
57 	Error::Severity _severity,
58 	string const& _type,
59 	string const& _component,
60 	string const& _message,
61 	string const& _formattedMessage = "",
62 	Json::Value const& _sourceLocation = Json::Value(),
63 	Json::Value const& _secondarySourceLocation = Json::Value()
64 )
65 {
66 	Json::Value error{Json::objectValue};
67 	error["type"] = _type;
68 	error["component"] = _component;
69 	error["severity"] = Error::formatErrorSeverityLowercase(_severity);
70 	error["message"] = _message;
71 	error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
72 	if (_sourceLocation.isObject())
73 		error["sourceLocation"] = _sourceLocation;
74 	if (_secondarySourceLocation.isArray())
75 		error["secondarySourceLocations"] = _secondarySourceLocation;
76 	return error;
77 }
78 
formatFatalError(string const & _type,string const & _message)79 Json::Value formatFatalError(string const& _type, string const& _message)
80 {
81 	Json::Value output{Json::objectValue};
82 	output["errors"] = Json::arrayValue;
83 	output["errors"].append(formatError(Error::Severity::Error, _type, "general", _message));
84 	return output;
85 }
86 
formatSourceLocation(SourceLocation const * location)87 Json::Value formatSourceLocation(SourceLocation const* location)
88 {
89 	if (!location || !location->sourceName)
90 		return Json::nullValue;
91 
92 	Json::Value sourceLocation{Json::objectValue};
93 	sourceLocation["file"] = *location->sourceName;
94 	sourceLocation["start"] = location->start;
95 	sourceLocation["end"] = location->end;
96 	return sourceLocation;
97 }
98 
formatSecondarySourceLocation(SecondarySourceLocation const * _secondaryLocation)99 Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _secondaryLocation)
100 {
101 	if (!_secondaryLocation)
102 		return Json::nullValue;
103 
104 	Json::Value secondarySourceLocation{Json::arrayValue};
105 	for (auto const& location: _secondaryLocation->infos)
106 	{
107 		Json::Value msg = formatSourceLocation(&location.second);
108 		msg["message"] = location.first;
109 		secondarySourceLocation.append(msg);
110 	}
111 	return secondarySourceLocation;
112 }
113 
formatErrorWithException(CharStreamProvider const & _charStreamProvider,util::Exception const & _exception,Error::Severity _severity,string const & _type,string const & _component,string const & _message,optional<ErrorId> _errorId=nullopt)114 Json::Value formatErrorWithException(
115 	CharStreamProvider const& _charStreamProvider,
116 	util::Exception const& _exception,
117 	Error::Severity _severity,
118 	string const& _type,
119 	string const& _component,
120 	string const& _message,
121 	optional<ErrorId> _errorId = nullopt
122 )
123 {
124 	string message;
125 	// TODO: consider enabling color
126 	string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(
127 		_exception,
128 		_type,
129 		_charStreamProvider
130 	);
131 
132 	if (string const* description = _exception.comment())
133 		message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
134 	else
135 		message = _message;
136 
137 	Json::Value error = formatError(
138 		_severity,
139 		_type,
140 		_component,
141 		message,
142 		formattedMessage,
143 		formatSourceLocation(boost::get_error_info<errinfo_sourceLocation>(_exception)),
144 		formatSecondarySourceLocation(boost::get_error_info<errinfo_secondarySourceLocation>(_exception))
145 	);
146 
147 	if (_errorId)
148 		error["errorCode"] = to_string(_errorId.value().error);
149 
150 	return error;
151 }
152 
requestedContractNames(Json::Value const & _outputSelection)153 map<string, set<string>> requestedContractNames(Json::Value const& _outputSelection)
154 {
155 	map<string, set<string>> contracts;
156 	for (auto const& sourceName: _outputSelection.getMemberNames())
157 	{
158 		string key = (sourceName == "*") ? "" : sourceName;
159 		for (auto const& contractName: _outputSelection[sourceName].getMemberNames())
160 		{
161 			string value = (contractName == "*") ? "" : contractName;
162 			contracts[key].insert(value);
163 		}
164 	}
165 	return contracts;
166 }
167 
168 /// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content.
hashMatchesContent(string const & _hash,string const & _content)169 bool hashMatchesContent(string const& _hash, string const& _content)
170 {
171 	try
172 	{
173 		return util::h256(_hash) == util::keccak256(_content);
174 	}
175 	catch (util::BadHexCharacter const&)
176 	{
177 		return false;
178 	}
179 }
180 
isArtifactRequested(Json::Value const & _outputSelection,string const & _artifact,bool _wildcardMatchesExperimental)181 bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental)
182 {
183 	static set<string> experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"};
184 	for (auto const& selectedArtifactJson: _outputSelection)
185 	{
186 		string const& selectedArtifact = selectedArtifactJson.asString();
187 		if (
188 			_artifact == selectedArtifact ||
189 			boost::algorithm::starts_with(_artifact, selectedArtifact + ".")
190 		)
191 			return true;
192 		else if (selectedArtifact == "*")
193 		{
194 			// "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated.
195 			if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
196 				return true;
197 		}
198 	}
199 	return false;
200 }
201 
202 ///
203 /// @a _outputSelection is a JSON object containing a two-level hashmap, where the first level is the filename,
204 /// the second level is the contract name and the value is an array of artifact names to be requested for that contract.
205 /// @a _file is the current file
206 /// @a _contract is the current contract
207 /// @a _artifact is the current artifact name
208 ///
209 /// @returns true if the @a _outputSelection has a match for the requested target in the specific file / contract.
210 ///
211 /// In @a _outputSelection the use of '*' as a wildcard is permitted.
212 ///
213 /// @TODO optimise this. Perhaps flatten the structure upfront.
214 ///
isArtifactRequested(Json::Value const & _outputSelection,string const & _file,string const & _contract,string const & _artifact,bool _wildcardMatchesExperimental)215 bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact, bool _wildcardMatchesExperimental)
216 {
217 	if (!_outputSelection.isObject())
218 		return false;
219 
220 	for (auto const& file: { _file, string("*") })
221 		if (_outputSelection.isMember(file) && _outputSelection[file].isObject())
222 		{
223 			/// For SourceUnit-level targets (such as AST) only allow empty name, otherwise
224 			/// for Contract-level targets try both contract name and wildcard
225 			vector<string> contracts{ _contract };
226 			if (!_contract.empty())
227 				contracts.emplace_back("*");
228 			for (auto const& contract: contracts)
229 				if (
230 					_outputSelection[file].isMember(contract) &&
231 					_outputSelection[file][contract].isArray() &&
232 					isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesExperimental)
233 				)
234 					return true;
235 		}
236 
237 	return false;
238 }
239 
isArtifactRequested(Json::Value const & _outputSelection,string const & _file,string const & _contract,vector<string> const & _artifacts,bool _wildcardMatchesExperimental)240 bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector<string> const& _artifacts, bool _wildcardMatchesExperimental)
241 {
242 	for (auto const& artifact: _artifacts)
243 		if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesExperimental))
244 			return true;
245 	return false;
246 }
247 
248 /// @returns all artifact names of the EVM object, either for creation or deploy time.
evmObjectComponents(string const & _objectKind)249 vector<string> evmObjectComponents(string const& _objectKind)
250 {
251 	solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", "");
252 	vector<string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences"};
253 	if (_objectKind == "deployedBytecode")
254 		components.push_back(".immutableReferences");
255 	return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; });
256 }
257 
258 /// @returns true if any binary was requested, i.e. we actually have to perform compilation.
isBinaryRequested(Json::Value const & _outputSelection)259 bool isBinaryRequested(Json::Value const& _outputSelection)
260 {
261 	if (!_outputSelection.isObject())
262 		return false;
263 
264 	// This does not include "evm.methodIdentifiers" on purpose!
265 	static vector<string> const outputsThatRequireBinaries = vector<string>{
266 		"*",
267 		"ir", "irOptimized",
268 		"wast", "wasm", "ewasm.wast", "ewasm.wasm",
269 		"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
270 	} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
271 
272 	for (auto const& fileRequests: _outputSelection)
273 		for (auto const& requests: fileRequests)
274 			for (auto const& output: outputsThatRequireBinaries)
275 				if (isArtifactRequested(requests, output, false))
276 					return true;
277 	return false;
278 }
279 
280 /// @returns true if EVM bytecode was requested, i.e. we have to run the old code generator.
isEvmBytecodeRequested(Json::Value const & _outputSelection)281 bool isEvmBytecodeRequested(Json::Value const& _outputSelection)
282 {
283 	if (!_outputSelection.isObject())
284 		return false;
285 
286 	static vector<string> const outputsThatRequireEvmBinaries = vector<string>{
287 		"*",
288 		"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
289 	} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
290 
291 	for (auto const& fileRequests: _outputSelection)
292 		for (auto const& requests: fileRequests)
293 			for (auto const& output: outputsThatRequireEvmBinaries)
294 				if (isArtifactRequested(requests, output, false))
295 					return true;
296 	return false;
297 }
298 
299 /// @returns true if any Ewasm code was requested. Note that as an exception, '*' does not
300 /// yet match "ewasm.wast" or "ewasm"
isEwasmRequested(Json::Value const & _outputSelection)301 bool isEwasmRequested(Json::Value const& _outputSelection)
302 {
303 	if (!_outputSelection.isObject())
304 		return false;
305 
306 	for (auto const& fileRequests: _outputSelection)
307 		for (auto const& requests: fileRequests)
308 			for (auto const& request: requests)
309 				if (request == "ewasm" || request == "ewasm.wast")
310 					return true;
311 
312 	return false;
313 }
314 
315 /// @returns true if any Yul IR was requested. Note that as an exception, '*' does not
316 /// yet match "ir" or "irOptimized"
isIRRequested(Json::Value const & _outputSelection)317 bool isIRRequested(Json::Value const& _outputSelection)
318 {
319 	if (isEwasmRequested(_outputSelection))
320 		return true;
321 
322 	if (!_outputSelection.isObject())
323 		return false;
324 
325 	for (auto const& fileRequests: _outputSelection)
326 		for (auto const& requests: fileRequests)
327 			for (auto const& request: requests)
328 				if (request == "ir" || request == "irOptimized")
329 					return true;
330 
331 	return false;
332 }
333 
formatLinkReferences(std::map<size_t,std::string> const & linkReferences)334 Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
335 {
336 	Json::Value ret{Json::objectValue};
337 
338 	for (auto const& ref: linkReferences)
339 	{
340 		string const& fullname = ref.second;
341 
342 		// If the link reference does not contain a colon, assume that the file name is missing and
343 		// the whole string represents the library name.
344 		size_t colon = fullname.rfind(':');
345 		string file = (colon != string::npos ? fullname.substr(0, colon) : "");
346 		string name = (colon != string::npos ? fullname.substr(colon + 1) : fullname);
347 
348 		Json::Value fileObject = ret.get(file, Json::objectValue);
349 		Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
350 
351 		Json::Value entry{Json::objectValue};
352 		entry["start"] = Json::UInt(ref.first);
353 		entry["length"] = 20;
354 
355 		libraryArray.append(entry);
356 		fileObject[name] = libraryArray;
357 		ret[file] = fileObject;
358 	}
359 
360 	return ret;
361 }
362 
formatImmutableReferences(map<u256,pair<string,vector<size_t>>> const & _immutableReferences)363 Json::Value formatImmutableReferences(map<u256, pair<string, vector<size_t>>> const& _immutableReferences)
364 {
365 	Json::Value ret{Json::objectValue};
366 
367 	for (auto const& immutableReference: _immutableReferences)
368 	{
369 		auto const& [identifier, byteOffsets] = immutableReference.second;
370 		Json::Value array(Json::arrayValue);
371 		for (size_t byteOffset: byteOffsets)
372 		{
373 			Json::Value byteRange{Json::objectValue};
374 			byteRange["start"] = Json::UInt(byteOffset);
375 			byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide
376 			array.append(byteRange);
377 		}
378 		ret[identifier] = array;
379 	}
380 
381 	return ret;
382 }
383 
collectEVMObject(evmasm::LinkerObject const & _object,string const * _sourceMap,Json::Value _generatedSources,bool _runtimeObject,function<bool (string)> const & _artifactRequested)384 Json::Value collectEVMObject(
385 	evmasm::LinkerObject const& _object,
386 	string const* _sourceMap,
387 	Json::Value _generatedSources,
388 	bool _runtimeObject,
389 	function<bool(string)> const& _artifactRequested
390 )
391 {
392 	Json::Value output{Json::objectValue};
393 	if (_artifactRequested("object"))
394 		output["object"] = _object.toHex();
395 	if (_artifactRequested("opcodes"))
396 		output["opcodes"] = evmasm::disassemble(_object.bytecode);
397 	if (_artifactRequested("sourceMap"))
398 		output["sourceMap"] = _sourceMap ? *_sourceMap : "";
399 	if (_artifactRequested("functionDebugData"))
400 		output["functionDebugData"] = StandardCompiler::formatFunctionDebugData(_object.functionDebugData);
401 	if (_artifactRequested("linkReferences"))
402 		output["linkReferences"] = formatLinkReferences(_object.linkReferences);
403 	if (_runtimeObject && _artifactRequested("immutableReferences"))
404 		output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences);
405 	if (_artifactRequested("generatedSources"))
406 		output["generatedSources"] = move(_generatedSources);
407 	return output;
408 }
409 
checkKeys(Json::Value const & _input,set<string> const & _keys,string const & _name)410 std::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
411 {
412 	if (!!_input && !_input.isObject())
413 		return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
414 
415 	for (auto const& member: _input.getMemberNames())
416 		if (!_keys.count(member))
417 			return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
418 
419 	return std::nullopt;
420 }
421 
checkRootKeys(Json::Value const & _input)422 std::optional<Json::Value> checkRootKeys(Json::Value const& _input)
423 {
424 	static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
425 	return checkKeys(_input, keys, "root");
426 }
427 
checkSourceKeys(Json::Value const & _input,string const & _name)428 std::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
429 {
430 	static set<string> keys{"content", "keccak256", "urls"};
431 	return checkKeys(_input, keys, "sources." + _name);
432 }
433 
checkAuxiliaryInputKeys(Json::Value const & _input)434 std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
435 {
436 	static set<string> keys{"smtlib2responses"};
437 	return checkKeys(_input, keys, "auxiliaryInput");
438 }
439 
checkSettingsKeys(Json::Value const & _input)440 std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
441 {
442 	static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"};
443 	return checkKeys(_input, keys, "settings");
444 }
445 
checkModelCheckerSettingsKeys(Json::Value const & _input)446 std::optional<Json::Value> checkModelCheckerSettingsKeys(Json::Value const& _input)
447 {
448 	static set<string> keys{"contracts", "divModNoSlacks", "engine", "invariants", "showUnproved", "solvers", "targets", "timeout"};
449 	return checkKeys(_input, keys, "modelChecker");
450 }
451 
checkOptimizerKeys(Json::Value const & _input)452 std::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
453 {
454 	static set<string> keys{"details", "enabled", "runs"};
455 	return checkKeys(_input, keys, "settings.optimizer");
456 }
457 
checkOptimizerDetailsKeys(Json::Value const & _input)458 std::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
459 {
460 	static set<string> keys{"peephole", "inliner", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
461 	return checkKeys(_input, keys, "settings.optimizer.details");
462 }
463 
checkOptimizerDetail(Json::Value const & _details,std::string const & _name,bool & _setting)464 std::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
465 {
466 	if (_details.isMember(_name))
467 	{
468 		if (!_details[_name].isBool())
469 			return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be Boolean");
470 		_setting = _details[_name].asBool();
471 	}
472 	return {};
473 }
474 
checkOptimizerDetailSteps(Json::Value const & _details,std::string const & _name,string & _setting)475 std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _setting)
476 {
477 	if (_details.isMember(_name))
478 	{
479 		if (_details[_name].isString())
480 		{
481 			try
482 			{
483 				yul::OptimiserSuite::validateSequence(_details[_name].asString());
484 			}
485 			catch (yul::OptimizerException const& _exception)
486 			{
487 				return formatFatalError(
488 					"JSONError",
489 					"Invalid optimizer step sequence in \"settings.optimizer.details." + _name + "\": " + _exception.what()
490 				);
491 			}
492 
493 			_setting = _details[_name].asString();
494 		}
495 		else
496 			return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be a string");
497 
498 	}
499 	return {};
500 }
501 
checkMetadataKeys(Json::Value const & _input)502 std::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
503 {
504 	if (_input.isObject())
505 	{
506 		if (_input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
507 			return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean");
508 
509 		static set<string> hashes{"ipfs", "bzzr1", "none"};
510 		if (_input.isMember("bytecodeHash") && !hashes.count(_input["bytecodeHash"].asString()))
511 			return formatFatalError("JSONError", "\"settings.metadata.bytecodeHash\" must be \"ipfs\", \"bzzr1\" or \"none\"");
512 	}
513 	static set<string> keys{"useLiteralContent", "bytecodeHash"};
514 	return checkKeys(_input, keys, "settings.metadata");
515 }
516 
checkOutputSelection(Json::Value const & _outputSelection)517 std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
518 {
519 	if (!!_outputSelection && !_outputSelection.isObject())
520 		return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
521 
522 	for (auto const& sourceName: _outputSelection.getMemberNames())
523 	{
524 		auto const& sourceVal = _outputSelection[sourceName];
525 
526 		if (!sourceVal.isObject())
527 			return formatFatalError(
528 				"JSONError",
529 				"\"settings.outputSelection." + sourceName + "\" must be an object"
530 			);
531 
532 		for (auto const& contractName: sourceVal.getMemberNames())
533 		{
534 			auto const& contractVal = sourceVal[contractName];
535 
536 			if (!contractVal.isArray())
537 				return formatFatalError(
538 					"JSONError",
539 					"\"settings.outputSelection." +
540 					sourceName +
541 					"." +
542 					contractName +
543 					"\" must be a string array"
544 				);
545 
546 			for (auto const& output: contractVal)
547 				if (!output.isString())
548 					return formatFatalError(
549 						"JSONError",
550 						"\"settings.outputSelection." +
551 						sourceName +
552 						"." +
553 						contractName +
554 						"\" must be a string array"
555 					);
556 		}
557 	}
558 
559 	return std::nullopt;
560 }
561 
562 /// Validates the optimizer settings and returns them in a parsed object.
563 /// On error returns the json-formatted error message.
parseOptimizerSettings(Json::Value const & _jsonInput)564 std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
565 {
566 	if (auto result = checkOptimizerKeys(_jsonInput))
567 		return *result;
568 
569 	OptimiserSettings settings = OptimiserSettings::minimal();
570 
571 	if (_jsonInput.isMember("enabled"))
572 	{
573 		if (!_jsonInput["enabled"].isBool())
574 			return formatFatalError("JSONError", "The \"enabled\" setting must be a Boolean.");
575 
576 		if (_jsonInput["enabled"].asBool())
577 			settings = OptimiserSettings::standard();
578 	}
579 
580 	if (_jsonInput.isMember("runs"))
581 	{
582 		if (!_jsonInput["runs"].isUInt())
583 			return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
584 		settings.expectedExecutionsPerDeployment = _jsonInput["runs"].asUInt();
585 	}
586 
587 	if (_jsonInput.isMember("details"))
588 	{
589 		Json::Value const& details = _jsonInput["details"];
590 		if (auto result = checkOptimizerDetailsKeys(details))
591 			return *result;
592 
593 		if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
594 			return *error;
595 		if (auto error = checkOptimizerDetail(details, "inliner", settings.runInliner))
596 			return *error;
597 		if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
598 			return *error;
599 		if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
600 			return *error;
601 		if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate))
602 			return *error;
603 		if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE))
604 			return *error;
605 		if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser))
606 			return *error;
607 		if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
608 			return *error;
609 		settings.optimizeStackAllocation = settings.runYulOptimiser;
610 		if (details.isMember("yulDetails"))
611 		{
612 			if (!settings.runYulOptimiser)
613 				return formatFatalError("JSONError", "\"Providing yulDetails requires Yul optimizer to be enabled.");
614 
615 			if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails"))
616 				return *result;
617 			if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation))
618 				return *error;
619 			if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps))
620 				return *error;
621 		}
622 	}
623 	return { std::move(settings) };
624 }
625 
626 }
627 
628 
parseInput(Json::Value const & _input)629 std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
630 {
631 	InputsAndSettings ret;
632 
633 	if (!_input.isObject())
634 		return formatFatalError("JSONError", "Input is not a JSON object.");
635 
636 	if (auto result = checkRootKeys(_input))
637 		return *result;
638 
639 	ret.language = _input["language"].asString();
640 
641 	Json::Value const& sources = _input["sources"];
642 
643 	if (!sources.isObject() && !sources.isNull())
644 		return formatFatalError("JSONError", "\"sources\" is not a JSON object.");
645 
646 	if (sources.empty())
647 		return formatFatalError("JSONError", "No input sources specified.");
648 
649 	ret.errors = Json::arrayValue;
650 
651 	for (auto const& sourceName: sources.getMemberNames())
652 	{
653 		string hash;
654 
655 		if (auto result = checkSourceKeys(sources[sourceName], sourceName))
656 			return *result;
657 
658 		if (sources[sourceName]["keccak256"].isString())
659 			hash = sources[sourceName]["keccak256"].asString();
660 
661 		if (sources[sourceName]["content"].isString())
662 		{
663 			string content = sources[sourceName]["content"].asString();
664 			if (!hash.empty() && !hashMatchesContent(hash, content))
665 				ret.errors.append(formatError(
666 					Error::Severity::Error,
667 					"IOError",
668 					"general",
669 					"Mismatch between content and supplied hash for \"" + sourceName + "\""
670 				));
671 			else
672 				ret.sources[sourceName] = content;
673 		}
674 		else if (sources[sourceName]["urls"].isArray())
675 		{
676 			if (!m_readFile)
677 				return formatFatalError("JSONError", "No import callback supplied, but URL is requested.");
678 
679 			bool found = false;
680 			vector<string> failures;
681 
682 			for (auto const& url: sources[sourceName]["urls"])
683 			{
684 				if (!url.isString())
685 					return formatFatalError("JSONError", "URL must be a string.");
686 				ReadCallback::Result result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), url.asString());
687 				if (result.success)
688 				{
689 					if (!hash.empty() && !hashMatchesContent(hash, result.responseOrErrorMessage))
690 						ret.errors.append(formatError(
691 							Error::Severity::Error,
692 							"IOError",
693 							"general",
694 							"Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\""
695 						));
696 					else
697 					{
698 						ret.sources[sourceName] = result.responseOrErrorMessage;
699 						found = true;
700 						break;
701 					}
702 				}
703 				else
704 					failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.responseOrErrorMessage);
705 			}
706 
707 			for (auto const& failure: failures)
708 			{
709 				/// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors.
710 				ret.errors.append(formatError(
711 					found ? Error::Severity::Warning : Error::Severity::Error,
712 					"IOError",
713 					"general",
714 					failure
715 				));
716 			}
717 		}
718 		else
719 			return formatFatalError("JSONError", "Invalid input source specified.");
720 	}
721 
722 	Json::Value const& auxInputs = _input["auxiliaryInput"];
723 
724 	if (auto result = checkAuxiliaryInputKeys(auxInputs))
725 		return *result;
726 
727 	if (!!auxInputs)
728 	{
729 		Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"];
730 		if (!!smtlib2Responses)
731 		{
732 			if (!smtlib2Responses.isObject())
733 				return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.");
734 
735 			for (auto const& hashString: smtlib2Responses.getMemberNames())
736 			{
737 				util::h256 hash;
738 				try
739 				{
740 					hash = util::h256(hashString);
741 				}
742 				catch (util::BadHexCharacter const&)
743 				{
744 					return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input.");
745 				}
746 
747 				if (!smtlib2Responses[hashString].isString())
748 					return formatFatalError(
749 						"JSONError",
750 						"\"smtlib2Responses." + hashString + "\" must be a string."
751 					);
752 
753 				ret.smtLib2Responses[hash] = smtlib2Responses[hashString].asString();
754 			}
755 		}
756 	}
757 
758 	Json::Value const& settings = _input.get("settings", Json::Value());
759 
760 	if (auto result = checkSettingsKeys(settings))
761 		return *result;
762 
763 	if (settings.isMember("stopAfter"))
764 	{
765 		if (!settings["stopAfter"].isString())
766 			return formatFatalError("JSONError", "\"settings.stopAfter\" must be a string.");
767 
768 		if (settings["stopAfter"].asString() != "parsing")
769 			return formatFatalError("JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\".");
770 
771 		ret.stopAfter = CompilerStack::State::Parsed;
772 	}
773 
774 	if (settings.isMember("parserErrorRecovery"))
775 	{
776 		if (!settings["parserErrorRecovery"].isBool())
777 			return formatFatalError("JSONError", "\"settings.parserErrorRecovery\" must be a Boolean.");
778 		ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool();
779 	}
780 
781 	if (settings.isMember("viaIR"))
782 	{
783 		if (!settings["viaIR"].isBool())
784 			return formatFatalError("JSONError", "\"settings.viaIR\" must be a Boolean.");
785 		ret.viaIR = settings["viaIR"].asBool();
786 	}
787 
788 	if (settings.isMember("evmVersion"))
789 	{
790 		if (!settings["evmVersion"].isString())
791 			return formatFatalError("JSONError", "evmVersion must be a string.");
792 		std::optional<langutil::EVMVersion> version = langutil::EVMVersion::fromString(settings["evmVersion"].asString());
793 		if (!version)
794 			return formatFatalError("JSONError", "Invalid EVM version requested.");
795 		ret.evmVersion = *version;
796 	}
797 
798 	if (settings.isMember("debug"))
799 	{
800 		if (auto result = checkKeys(settings["debug"], {"revertStrings", "debugInfo"}, "settings.debug"))
801 			return *result;
802 
803 		if (settings["debug"].isMember("revertStrings"))
804 		{
805 			if (!settings["debug"]["revertStrings"].isString())
806 				return formatFatalError("JSONError", "settings.debug.revertStrings must be a string.");
807 			std::optional<RevertStrings> revertStrings = revertStringsFromString(settings["debug"]["revertStrings"].asString());
808 			if (!revertStrings)
809 				return formatFatalError("JSONError", "Invalid value for settings.debug.revertStrings.");
810 			if (*revertStrings == RevertStrings::VerboseDebug)
811 				return formatFatalError(
812 					"UnimplementedFeatureError",
813 					"Only \"default\", \"strip\" and \"debug\" are implemented for settings.debug.revertStrings for now."
814 				);
815 			ret.revertStrings = *revertStrings;
816 		}
817 
818 		if (settings["debug"].isMember("debugInfo"))
819 		{
820 			if (!settings["debug"]["debugInfo"].isArray())
821 				return formatFatalError("JSONError", "settings.debug.debugInfo must be an array.");
822 
823 			vector<string> components;
824 			for (Json::Value const& arrayValue: settings["debug"]["debugInfo"])
825 				components.push_back(arrayValue.asString());
826 
827 			optional<DebugInfoSelection> debugInfoSelection = DebugInfoSelection::fromComponents(
828 				components,
829 				true /* _acceptWildcards */
830 			);
831 			if (!debugInfoSelection.has_value())
832 				return formatFatalError("JSONError", "Invalid value in settings.debug.debugInfo.");
833 
834 			if (debugInfoSelection->snippet && !debugInfoSelection->location)
835 				return formatFatalError(
836 					"JSONError",
837 					"To use 'snippet' with settings.debug.debugInfo you must select also 'location'."
838 				);
839 
840 			ret.debugInfoSelection = debugInfoSelection.value();
841 		}
842 	}
843 
844 	if (settings.isMember("remappings") && !settings["remappings"].isArray())
845 		return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings.");
846 
847 	for (auto const& remapping: settings.get("remappings", Json::Value()))
848 	{
849 		if (!remapping.isString())
850 			return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings");
851 		if (auto r = ImportRemapper::parseRemapping(remapping.asString()))
852 			ret.remappings.emplace_back(std::move(*r));
853 		else
854 			return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\"");
855 	}
856 
857 	if (settings.isMember("optimizer"))
858 	{
859 		auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
860 		if (std::holds_alternative<Json::Value>(optimiserSettings))
861 			return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
862 		else
863 			ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
864 	}
865 
866 	Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
867 	if (!jsonLibraries.isObject())
868 		return formatFatalError("JSONError", "\"libraries\" is not a JSON object.");
869 	for (auto const& sourceName: jsonLibraries.getMemberNames())
870 	{
871 		auto const& jsonSourceName = jsonLibraries[sourceName];
872 		if (!jsonSourceName.isObject())
873 			return formatFatalError("JSONError", "Library entry is not a JSON object.");
874 		for (auto const& library: jsonSourceName.getMemberNames())
875 		{
876 			if (!jsonSourceName[library].isString())
877 				return formatFatalError("JSONError", "Library address must be a string.");
878 			string address = jsonSourceName[library].asString();
879 
880 			if (!boost::starts_with(address, "0x"))
881 				return formatFatalError(
882 					"JSONError",
883 					"Library address is not prefixed with \"0x\"."
884 				);
885 
886 			if (address.length() != 42)
887 				return formatFatalError(
888 					"JSONError",
889 					"Library address is of invalid length."
890 				);
891 
892 			try
893 			{
894 				ret.libraries[sourceName + ":" + library] = util::h160(address);
895 			}
896 			catch (util::BadHexCharacter const&)
897 			{
898 				return formatFatalError(
899 					"JSONError",
900 					"Invalid library address (\"" + address + "\") supplied."
901 				);
902 			}
903 		}
904 	}
905 
906 	Json::Value metadataSettings = settings.get("metadata", Json::Value());
907 
908 	if (auto result = checkMetadataKeys(metadataSettings))
909 		return *result;
910 
911 	ret.metadataLiteralSources = metadataSettings.get("useLiteralContent", Json::Value(false)).asBool();
912 	if (metadataSettings.isMember("bytecodeHash"))
913 	{
914 		auto metadataHash = metadataSettings["bytecodeHash"].asString();
915 		ret.metadataHash =
916 			metadataHash == "ipfs" ?
917 			CompilerStack::MetadataHash::IPFS :
918 				metadataHash == "bzzr1" ?
919 				CompilerStack::MetadataHash::Bzzr1 :
920 				CompilerStack::MetadataHash::None;
921 	}
922 
923 	Json::Value outputSelection = settings.get("outputSelection", Json::Value());
924 
925 	if (auto jsonError = checkOutputSelection(outputSelection))
926 		return *jsonError;
927 
928 	ret.outputSelection = std::move(outputSelection);
929 
930 	if (ret.stopAfter != CompilerStack::State::CompilationSuccessful && isBinaryRequested(ret.outputSelection))
931 		return formatFatalError(
932 			"JSONError",
933 			"Requested output selection conflicts with \"settings.stopAfter\"."
934 		);
935 
936 	Json::Value const& modelCheckerSettings = settings.get("modelChecker", Json::Value());
937 
938 	if (auto result = checkModelCheckerSettingsKeys(modelCheckerSettings))
939 		return *result;
940 
941 	if (modelCheckerSettings.isMember("contracts"))
942 	{
943 		auto const& sources = modelCheckerSettings["contracts"];
944 		if (!sources.isObject() && !sources.isNull())
945 			return formatFatalError("JSONError", "settings.modelChecker.contracts is not a JSON object.");
946 
947 		map<string, set<string>> sourceContracts;
948 		for (auto const& source: sources.getMemberNames())
949 		{
950 			if (source.empty())
951 				return formatFatalError("JSONError", "Source name cannot be empty.");
952 
953 			auto const& contracts = sources[source];
954 			if (!contracts.isArray())
955 				return formatFatalError("JSONError", "Source contracts must be an array.");
956 
957 			for (auto const& contract: contracts)
958 			{
959 				if (!contract.isString())
960 					return formatFatalError("JSONError", "Every contract in settings.modelChecker.contracts must be a string.");
961 				if (contract.asString().empty())
962 					return formatFatalError("JSONError", "Contract name cannot be empty.");
963 				sourceContracts[source].insert(contract.asString());
964 			}
965 
966 			if (sourceContracts[source].empty())
967 				return formatFatalError("JSONError", "Source contracts must be a non-empty array.");
968 		}
969 		ret.modelCheckerSettings.contracts = {move(sourceContracts)};
970 	}
971 
972 	if (modelCheckerSettings.isMember("divModNoSlacks"))
973 	{
974 		auto const& divModNoSlacks = modelCheckerSettings["divModNoSlacks"];
975 		if (!divModNoSlacks.isBool())
976 			return formatFatalError("JSONError", "settings.modelChecker.divModNoSlacks must be a Boolean.");
977 		ret.modelCheckerSettings.divModNoSlacks = divModNoSlacks.asBool();
978 	}
979 
980 	if (modelCheckerSettings.isMember("engine"))
981 	{
982 		if (!modelCheckerSettings["engine"].isString())
983 			return formatFatalError("JSONError", "settings.modelChecker.engine must be a string.");
984 		std::optional<ModelCheckerEngine> engine = ModelCheckerEngine::fromString(modelCheckerSettings["engine"].asString());
985 		if (!engine)
986 			return formatFatalError("JSONError", "Invalid model checker engine requested.");
987 		ret.modelCheckerSettings.engine = *engine;
988 	}
989 
990 	if (modelCheckerSettings.isMember("invariants"))
991 	{
992 		auto const& invariantsArray = modelCheckerSettings["invariants"];
993 		if (!invariantsArray.isArray())
994 			return formatFatalError("JSONError", "settings.modelChecker.invariants must be an array.");
995 
996 		ModelCheckerInvariants invariants;
997 		for (auto const& i: invariantsArray)
998 		{
999 			if (!i.isString())
1000 				return formatFatalError("JSONError", "Every invariant type in settings.modelChecker.invariants must be a string.");
1001 			if (!invariants.setFromString(i.asString()))
1002 				return formatFatalError("JSONError", "Invalid model checker invariants requested.");
1003 		}
1004 
1005 		if (invariants.invariants.empty())
1006 			return formatFatalError("JSONError", "settings.modelChecker.invariants must be a non-empty array.");
1007 
1008 		ret.modelCheckerSettings.invariants = invariants;
1009 	}
1010 
1011 	if (modelCheckerSettings.isMember("showUnproved"))
1012 	{
1013 		auto const& showUnproved = modelCheckerSettings["showUnproved"];
1014 		if (!showUnproved.isBool())
1015 			return formatFatalError("JSONError", "settings.modelChecker.showUnproved must be a Boolean value.");
1016 		ret.modelCheckerSettings.showUnproved = showUnproved.asBool();
1017 	}
1018 
1019 	if (modelCheckerSettings.isMember("solvers"))
1020 	{
1021 		auto const& solversArray = modelCheckerSettings["solvers"];
1022 		if (!solversArray.isArray())
1023 			return formatFatalError("JSONError", "settings.modelChecker.solvers must be an array.");
1024 
1025 		smtutil::SMTSolverChoice solvers;
1026 		for (auto const& s: solversArray)
1027 		{
1028 			if (!s.isString())
1029 				return formatFatalError("JSONError", "Every target in settings.modelChecker.solvers must be a string.");
1030 			if (!solvers.setSolver(s.asString()))
1031 				return formatFatalError("JSONError", "Invalid model checker solvers requested.");
1032 		}
1033 
1034 		ret.modelCheckerSettings.solvers = solvers;
1035 	}
1036 
1037 	if (modelCheckerSettings.isMember("targets"))
1038 	{
1039 		auto const& targetsArray = modelCheckerSettings["targets"];
1040 		if (!targetsArray.isArray())
1041 			return formatFatalError("JSONError", "settings.modelChecker.targets must be an array.");
1042 
1043 		ModelCheckerTargets targets;
1044 		for (auto const& t: targetsArray)
1045 		{
1046 			if (!t.isString())
1047 				return formatFatalError("JSONError", "Every target in settings.modelChecker.targets must be a string.");
1048 			if (!targets.setFromString(t.asString()))
1049 				return formatFatalError("JSONError", "Invalid model checker targets requested.");
1050 		}
1051 
1052 		if (targets.targets.empty())
1053 			return formatFatalError("JSONError", "settings.modelChecker.targets must be a non-empty array.");
1054 
1055 		ret.modelCheckerSettings.targets = targets;
1056 	}
1057 
1058 	if (modelCheckerSettings.isMember("timeout"))
1059 	{
1060 		if (!modelCheckerSettings["timeout"].isUInt())
1061 			return formatFatalError("JSONError", "settings.modelChecker.timeout must be an unsigned integer.");
1062 		ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].asUInt();
1063 	}
1064 
1065 	return { std::move(ret) };
1066 }
1067 
compileSolidity(StandardCompiler::InputsAndSettings _inputsAndSettings)1068 Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inputsAndSettings)
1069 {
1070 	CompilerStack compilerStack(m_readFile);
1071 
1072 	StringMap sourceList = std::move(_inputsAndSettings.sources);
1073 	compilerStack.setSources(sourceList);
1074 	for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses)
1075 		compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second);
1076 	compilerStack.setViaIR(_inputsAndSettings.viaIR);
1077 	compilerStack.setEVMVersion(_inputsAndSettings.evmVersion);
1078 	compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery);
1079 	compilerStack.setRemappings(move(_inputsAndSettings.remappings));
1080 	compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
1081 	compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);
1082 	if (_inputsAndSettings.debugInfoSelection.has_value())
1083 		compilerStack.selectDebugInfo(_inputsAndSettings.debugInfoSelection.value());
1084 	compilerStack.setLibraries(_inputsAndSettings.libraries);
1085 	compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
1086 	compilerStack.setMetadataHash(_inputsAndSettings.metadataHash);
1087 	compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection));
1088 	compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings);
1089 
1090 	compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
1091 	compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection));
1092 	compilerStack.enableEwasmGeneration(isEwasmRequested(_inputsAndSettings.outputSelection));
1093 
1094 	Json::Value errors = std::move(_inputsAndSettings.errors);
1095 
1096 	bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection);
1097 
1098 	try
1099 	{
1100 		if (binariesRequested)
1101 			compilerStack.compile();
1102 		else
1103 			compilerStack.parseAndAnalyze(_inputsAndSettings.stopAfter);
1104 
1105 		for (auto const& error: compilerStack.errors())
1106 		{
1107 			Error const& err = dynamic_cast<Error const&>(*error);
1108 
1109 			errors.append(formatErrorWithException(
1110 				compilerStack,
1111 				*error,
1112 				Error::errorSeverity(err.type()),
1113 				err.typeName(),
1114 				"general",
1115 				"",
1116 				err.errorId()
1117 			));
1118 		}
1119 	}
1120 	/// This is only thrown in a very few locations.
1121 	catch (Error const& _error)
1122 	{
1123 		errors.append(formatErrorWithException(
1124 			compilerStack,
1125 			_error,
1126 			Error::Severity::Error,
1127 			_error.typeName(),
1128 			"general",
1129 			"Uncaught error: "
1130 		));
1131 	}
1132 	/// This should not be leaked from compile().
1133 	catch (FatalError const& _exception)
1134 	{
1135 		errors.append(formatError(
1136 			Error::Severity::Error,
1137 			"FatalError",
1138 			"general",
1139 			"Uncaught fatal error: " + boost::diagnostic_information(_exception)
1140 		));
1141 	}
1142 	catch (CompilerError const& _exception)
1143 	{
1144 		errors.append(formatErrorWithException(
1145 			compilerStack,
1146 			_exception,
1147 			Error::Severity::Error,
1148 			"CompilerError",
1149 			"general",
1150 			"Compiler error (" + _exception.lineInfo() + ")"
1151 		));
1152 	}
1153 	catch (InternalCompilerError const& _exception)
1154 	{
1155 		errors.append(formatErrorWithException(
1156 			compilerStack,
1157 			_exception,
1158 			Error::Severity::Error,
1159 			"InternalCompilerError",
1160 			"general",
1161 			"Internal compiler error (" + _exception.lineInfo() + ")"
1162 		));
1163 	}
1164 	catch (UnimplementedFeatureError const& _exception)
1165 	{
1166 		errors.append(formatErrorWithException(
1167 			compilerStack,
1168 			_exception,
1169 			Error::Severity::Error,
1170 			"UnimplementedFeatureError",
1171 			"general",
1172 			"Unimplemented feature (" + _exception.lineInfo() + ")"
1173 		));
1174 	}
1175 	catch (yul::YulException const& _exception)
1176 	{
1177 		errors.append(formatErrorWithException(
1178 			compilerStack,
1179 			_exception,
1180 			Error::Severity::Error,
1181 			"YulException",
1182 			"general",
1183 			"Yul exception"
1184 		));
1185 	}
1186 	catch (smtutil::SMTLogicError const& _exception)
1187 	{
1188 		errors.append(formatErrorWithException(
1189 			compilerStack,
1190 			_exception,
1191 			Error::Severity::Error,
1192 			"SMTLogicException",
1193 			"general",
1194 			"SMT logic exception"
1195 		));
1196 	}
1197 	catch (util::Exception const& _exception)
1198 	{
1199 		errors.append(formatError(
1200 			Error::Severity::Error,
1201 			"Exception",
1202 			"general",
1203 			"Exception during compilation: " + boost::diagnostic_information(_exception)
1204 		));
1205 	}
1206 	catch (std::exception const& _exception)
1207 	{
1208 		errors.append(formatError(
1209 			Error::Severity::Error,
1210 			"Exception",
1211 			"general",
1212 			"Unknown exception during compilation: " + boost::diagnostic_information(_exception)
1213 		));
1214 	}
1215 	catch (...)
1216 	{
1217 		errors.append(formatError(
1218 			Error::Severity::Error,
1219 			"Exception",
1220 			"general",
1221 			"Unknown exception during compilation: " + boost::current_exception_diagnostic_information()
1222 		));
1223 	}
1224 
1225 	bool analysisPerformed = compilerStack.state() >= CompilerStack::State::AnalysisPerformed;
1226 	bool const compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful;
1227 
1228 	if (compilerStack.hasError() && !_inputsAndSettings.parserErrorRecovery)
1229 		analysisPerformed = false;
1230 
1231 	/// Inconsistent state - stop here to receive error reports from users
1232 	if (
1233 		((binariesRequested && !compilationSuccess) || !analysisPerformed) &&
1234 		(errors.empty() && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed)
1235 	)
1236 		return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
1237 
1238 	Json::Value output = Json::objectValue;
1239 
1240 	if (errors.size() > 0)
1241 		output["errors"] = std::move(errors);
1242 
1243 	if (!compilerStack.unhandledSMTLib2Queries().empty())
1244 		for (string const& query: compilerStack.unhandledSMTLib2Queries())
1245 			output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + util::keccak256(query).hex()] = query;
1246 
1247 	bool const wildcardMatchesExperimental = false;
1248 
1249 	output["sources"] = Json::objectValue;
1250 	unsigned sourceIndex = 0;
1251 	if (compilerStack.state() >= CompilerStack::State::Parsed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery))
1252 		for (string const& sourceName: compilerStack.sourceNames())
1253 		{
1254 			Json::Value sourceResult = Json::objectValue;
1255 			sourceResult["id"] = sourceIndex++;
1256 			if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental))
1257 				sourceResult["ast"] = ASTJsonConverter(compilerStack.state(), compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
1258 			output["sources"][sourceName] = sourceResult;
1259 		}
1260 
1261 	Json::Value contractsOutput = Json::objectValue;
1262 	for (string const& contractName: analysisPerformed ? compilerStack.contractNames() : vector<string>())
1263 	{
1264 		size_t colon = contractName.rfind(':');
1265 		solAssert(colon != string::npos, "");
1266 		string file = contractName.substr(0, colon);
1267 		string name = contractName.substr(colon + 1);
1268 
1269 		// ABI, storage layout, documentation and metadata
1270 		Json::Value contractData(Json::objectValue);
1271 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
1272 			contractData["abi"] = compilerStack.contractABI(contractName);
1273 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
1274 			contractData["storageLayout"] = compilerStack.storageLayout(contractName);
1275 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
1276 			contractData["metadata"] = compilerStack.metadata(contractName);
1277 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
1278 			contractData["userdoc"] = compilerStack.natspecUser(contractName);
1279 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesExperimental))
1280 			contractData["devdoc"] = compilerStack.natspecDev(contractName);
1281 
1282 		// IR
1283 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesExperimental))
1284 			contractData["ir"] = compilerStack.yulIR(contractName);
1285 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental))
1286 			contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
1287 
1288 		// Ewasm
1289 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
1290 			contractData["ewasm"]["wast"] = compilerStack.ewasm(contractName);
1291 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental))
1292 			contractData["ewasm"]["wasm"] = compilerStack.ewasmObject(contractName).toHex();
1293 
1294 		// EVM
1295 		Json::Value evmData(Json::objectValue);
1296 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))
1297 			evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
1298 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental))
1299 			evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName);
1300 		if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
1301 			evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
1302 		if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental))
1303 			evmData["gasEstimates"] = compilerStack.gasEstimates(contractName);
1304 
1305 		if (compilationSuccess && isArtifactRequested(
1306 			_inputsAndSettings.outputSelection,
1307 			file,
1308 			name,
1309 			evmObjectComponents("bytecode"),
1310 			wildcardMatchesExperimental
1311 		))
1312 			evmData["bytecode"] = collectEVMObject(
1313 				compilerStack.object(contractName),
1314 				compilerStack.sourceMapping(contractName),
1315 				compilerStack.generatedSources(contractName),
1316 				false,
1317 				[&](string const& _element) { return isArtifactRequested(
1318 					_inputsAndSettings.outputSelection,
1319 					file,
1320 					name,
1321 					"evm.bytecode." + _element,
1322 					wildcardMatchesExperimental
1323 				); }
1324 			);
1325 
1326 		if (compilationSuccess && isArtifactRequested(
1327 			_inputsAndSettings.outputSelection,
1328 			file,
1329 			name,
1330 			evmObjectComponents("deployedBytecode"),
1331 			wildcardMatchesExperimental
1332 		))
1333 			evmData["deployedBytecode"] = collectEVMObject(
1334 				compilerStack.runtimeObject(contractName),
1335 				compilerStack.runtimeSourceMapping(contractName),
1336 				compilerStack.generatedSources(contractName, true),
1337 				true,
1338 				[&](string const& _element) { return isArtifactRequested(
1339 					_inputsAndSettings.outputSelection,
1340 					file,
1341 					name,
1342 					"evm.deployedBytecode." + _element,
1343 					wildcardMatchesExperimental
1344 				); }
1345 			);
1346 
1347 		if (!evmData.empty())
1348 			contractData["evm"] = evmData;
1349 
1350 		if (!contractData.empty())
1351 		{
1352 			if (!contractsOutput.isMember(file))
1353 				contractsOutput[file] = Json::objectValue;
1354 			contractsOutput[file][name] = contractData;
1355 		}
1356 	}
1357 	if (!contractsOutput.empty())
1358 		output["contracts"] = contractsOutput;
1359 
1360 	return output;
1361 }
1362 
1363 
compileYul(InputsAndSettings _inputsAndSettings)1364 Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
1365 {
1366 	Json::Value output = Json::objectValue;
1367 	output["errors"] = std::move(_inputsAndSettings.errors);
1368 
1369 	if (_inputsAndSettings.sources.size() != 1)
1370 	{
1371 		output["errors"].append(formatError(
1372 			Error::Severity::Error,
1373 			"JSONError",
1374 			"general",
1375 			"Yul mode only supports exactly one input file."
1376 		));
1377 		return output;
1378 	}
1379 	if (!_inputsAndSettings.smtLib2Responses.empty())
1380 	{
1381 		output["errors"].append(formatError(
1382 			Error::Severity::Error,
1383 			"JSONError",
1384 			"general",
1385 			"Yul mode does not support smtlib2responses."
1386 		));
1387 		return output;
1388 	}
1389 	if (!_inputsAndSettings.remappings.empty())
1390 	{
1391 		output["errors"].append(formatError(
1392 			Error::Severity::Error,
1393 			"JSONError",
1394 			"general",
1395 			"Field \"settings.remappings\" cannot be used for Yul."
1396 		));
1397 		return output;
1398 	}
1399 	if (_inputsAndSettings.revertStrings != RevertStrings::Default)
1400 	{
1401 		output["errors"].append(formatError(
1402 			Error::Severity::Error,
1403 			"JSONError",
1404 			"general",
1405 			"Field \"settings.debug.revertStrings\" cannot be used for Yul."
1406 		));
1407 		return output;
1408 	}
1409 
1410 	AssemblyStack stack(
1411 		_inputsAndSettings.evmVersion,
1412 		AssemblyStack::Language::StrictAssembly,
1413 		_inputsAndSettings.optimiserSettings,
1414 		_inputsAndSettings.debugInfoSelection.has_value() ?
1415 			_inputsAndSettings.debugInfoSelection.value() :
1416 			DebugInfoSelection::Default()
1417 	);
1418 	string const& sourceName = _inputsAndSettings.sources.begin()->first;
1419 	string const& sourceContents = _inputsAndSettings.sources.begin()->second;
1420 
1421 	// Inconsistent state - stop here to receive error reports from users
1422 	if (!stack.parseAndAnalyze(sourceName, sourceContents) && stack.errors().empty())
1423 	{
1424 		output["errors"].append(formatError(
1425 			Error::Severity::Error,
1426 			"InternalCompilerError",
1427 			"general",
1428 			"No error reported, but compilation failed."
1429 		));
1430 		return output;
1431 	}
1432 
1433 	if (!stack.errors().empty())
1434 	{
1435 		for (auto const& error: stack.errors())
1436 		{
1437 			auto err = dynamic_pointer_cast<Error const>(error);
1438 
1439 			output["errors"].append(formatErrorWithException(
1440 				stack,
1441 				*error,
1442 				Error::errorSeverity(err->type()),
1443 				err->typeName(),
1444 				"general",
1445 				""
1446 			));
1447 		}
1448 		return output;
1449 	}
1450 
1451 	// TODO: move this warning to AssemblyStack
1452 	output["errors"] = Json::arrayValue;
1453 	output["errors"].append(formatError(Error::Severity::Warning, "Warning", "general", "Yul is still experimental. Please use the output with care."));
1454 
1455 	string contractName = stack.parserResult()->name.str();
1456 
1457 	bool const wildcardMatchesExperimental = true;
1458 	if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
1459 		output["contracts"][sourceName][contractName]["ir"] = stack.print();
1460 
1461 	stack.optimize();
1462 
1463 	MachineAssemblyObject object;
1464 	MachineAssemblyObject runtimeObject;
1465 	tie(object, runtimeObject) = stack.assembleWithDeployed();
1466 
1467 	if (object.bytecode)
1468 		object.bytecode->link(_inputsAndSettings.libraries);
1469 	if (runtimeObject.bytecode)
1470 		runtimeObject.bytecode->link(_inputsAndSettings.libraries);
1471 
1472 	for (string const& objectKind: vector<string>{"bytecode", "deployedBytecode"})
1473 		if (isArtifactRequested(
1474 			_inputsAndSettings.outputSelection,
1475 			sourceName,
1476 			contractName,
1477 			evmObjectComponents(objectKind),
1478 			wildcardMatchesExperimental
1479 		))
1480 		{
1481 			MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject;
1482 			if (o.bytecode)
1483 				output["contracts"][sourceName][contractName]["evm"][objectKind] =
1484 					collectEVMObject(
1485 						*o.bytecode,
1486 						o.sourceMappings.get(),
1487 						Json::arrayValue,
1488 						false,
1489 						[&](string const& _element) { return isArtifactRequested(
1490 							_inputsAndSettings.outputSelection,
1491 							sourceName,
1492 							contractName,
1493 							"evm." + objectKind + "." + _element,
1494 							wildcardMatchesExperimental
1495 						); }
1496 					);
1497 		}
1498 
1499 	if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))
1500 		output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
1501 	if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental))
1502 		output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly;
1503 
1504 	return output;
1505 }
1506 
1507 
compile(Json::Value const & _input)1508 Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
1509 {
1510 	YulStringRepository::reset();
1511 
1512 	try
1513 	{
1514 		auto parsed = parseInput(_input);
1515 		if (std::holds_alternative<Json::Value>(parsed))
1516 			return std::get<Json::Value>(std::move(parsed));
1517 		InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
1518 		if (settings.language == "Solidity")
1519 			return compileSolidity(std::move(settings));
1520 		else if (settings.language == "Yul")
1521 			return compileYul(std::move(settings));
1522 		else
1523 			return formatFatalError("JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language.");
1524 	}
1525 	catch (Json::LogicError const& _exception)
1526 	{
1527 		return formatFatalError("InternalCompilerError", string("JSON logic exception: ") + _exception.what());
1528 	}
1529 	catch (Json::RuntimeError const& _exception)
1530 	{
1531 		return formatFatalError("InternalCompilerError", string("JSON runtime exception: ") + _exception.what());
1532 	}
1533 	catch (util::Exception const& _exception)
1534 	{
1535 		return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile: " + boost::diagnostic_information(_exception));
1536 	}
1537 	catch (...)
1538 	{
1539 		return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile: " +  boost::current_exception_diagnostic_information());
1540 	}
1541 }
1542 
compile(string const & _input)1543 string StandardCompiler::compile(string const& _input) noexcept
1544 {
1545 	Json::Value input;
1546 	string errors;
1547 	try
1548 	{
1549 		if (!util::jsonParseStrict(_input, input, &errors))
1550 			return util::jsonPrint(formatFatalError("JSONError", errors), m_jsonPrintingFormat);
1551 	}
1552 	catch (...)
1553 	{
1554 		return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
1555 	}
1556 
1557 	// cout << "Input: " << input.toStyledString() << endl;
1558 	Json::Value output = compile(input);
1559 	// cout << "Output: " << output.toStyledString() << endl;
1560 
1561 	try
1562 	{
1563 		return util::jsonPrint(output, m_jsonPrintingFormat);
1564 	}
1565 	catch (...)
1566 	{
1567 		return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
1568 	}
1569 }
1570 
formatFunctionDebugData(map<string,evmasm::LinkerObject::FunctionDebugData> const & _debugInfo)1571 Json::Value StandardCompiler::formatFunctionDebugData(
1572 	map<string, evmasm::LinkerObject::FunctionDebugData> const& _debugInfo
1573 )
1574 {
1575 	Json::Value ret(Json::objectValue);
1576 	for (auto const& [name, info]: _debugInfo)
1577 	{
1578 		Json::Value fun;
1579 		if (info.sourceID)
1580 			fun["id"] = Json::UInt64(*info.sourceID);
1581 		else
1582 			fun["id"] = Json::nullValue;
1583 		if (info.bytecodeOffset)
1584 			fun["entryPoint"] = Json::UInt64(*info.bytecodeOffset);
1585 		else
1586 			fun["entryPoint"] = Json::nullValue;
1587 		fun["parameterSlots"] = Json::UInt64(info.params);
1588 		fun["returnSlots"] = Json::UInt64(info.returns);
1589 		ret[name] = move(fun);
1590 	}
1591 
1592 	return ret;
1593 }
1594