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