/src/solidity/libsolidity/interface/StandardCompiler.cpp
Line | Count | Source (jump to first uncovered line) |
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/ASTJsonExporter.h> |
28 | | #include <libyul/YulStack.h> |
29 | | #include <libyul/Exceptions.h> |
30 | | #include <libyul/optimiser/Suite.h> |
31 | | |
32 | | #include <libevmasm/Disassemble.h> |
33 | | #include <libevmasm/Ethdebug.h> |
34 | | #include <libevmasm/EVMAssemblyStack.h> |
35 | | |
36 | | #include <libsmtutil/Exceptions.h> |
37 | | |
38 | | #include <liblangutil/SourceReferenceFormatter.h> |
39 | | |
40 | | #include <libsolutil/JSON.h> |
41 | | #include <libsolutil/Keccak256.h> |
42 | | #include <libsolutil/CommonData.h> |
43 | | |
44 | | #include <boost/algorithm/string/predicate.hpp> |
45 | | |
46 | | #include <algorithm> |
47 | | #include <optional> |
48 | | |
49 | | using namespace solidity; |
50 | | using namespace solidity::yul; |
51 | | using namespace solidity::frontend; |
52 | | using namespace solidity::langutil; |
53 | | using namespace solidity::util; |
54 | | using namespace std::string_literals; |
55 | | |
56 | | namespace |
57 | | { |
58 | | |
59 | | Json formatError( |
60 | | Error::Type _type, |
61 | | std::string const& _component, |
62 | | std::string const& _message, |
63 | | std::string const& _formattedMessage = "", |
64 | | Json const& _sourceLocation = Json(), |
65 | | Json const& _secondarySourceLocation = Json() |
66 | | ) |
67 | 0 | { |
68 | 0 | Json error; |
69 | 0 | error["type"] = Error::formatErrorType(_type); |
70 | 0 | error["component"] = _component; |
71 | 0 | error["severity"] = Error::formatErrorSeverityLowercase(Error::errorSeverity(_type)); |
72 | 0 | error["message"] = _message; |
73 | 0 | error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message; |
74 | 0 | if (_sourceLocation.is_object()) |
75 | 0 | error["sourceLocation"] = _sourceLocation; |
76 | 0 | if (_secondarySourceLocation.is_array()) |
77 | 0 | error["secondarySourceLocations"] = _secondarySourceLocation; |
78 | 0 | return error; |
79 | 0 | } |
80 | | |
81 | | Json formatFatalError(Error::Type _type, std::string const& _message) |
82 | 0 | { |
83 | 0 | Json output; |
84 | 0 | output["errors"] = Json::array(); |
85 | 0 | output["errors"].emplace_back(formatError(_type, "general", _message)); |
86 | 0 | return output; |
87 | 0 | } |
88 | | |
89 | | Json formatSourceLocation(SourceLocation const* location) |
90 | 0 | { |
91 | 0 | if (!location || !location->sourceName) |
92 | 0 | return Json(); |
93 | | |
94 | 0 | Json sourceLocation; |
95 | 0 | sourceLocation["file"] = *location->sourceName; |
96 | 0 | sourceLocation["start"] = location->start; |
97 | 0 | sourceLocation["end"] = location->end; |
98 | 0 | return sourceLocation; |
99 | 0 | } |
100 | | |
101 | | Json formatSecondarySourceLocation(SecondarySourceLocation const* _secondaryLocation) |
102 | 0 | { |
103 | 0 | if (!_secondaryLocation) |
104 | 0 | return Json(); |
105 | | |
106 | 0 | Json secondarySourceLocation = Json::array(); |
107 | 0 | for (auto const& location: _secondaryLocation->infos) |
108 | 0 | { |
109 | 0 | Json msg = formatSourceLocation(&location.second); |
110 | 0 | msg["message"] = location.first; |
111 | 0 | secondarySourceLocation.emplace_back(msg); |
112 | 0 | } |
113 | 0 | return secondarySourceLocation; |
114 | 0 | } |
115 | | |
116 | | Json formatErrorWithException( |
117 | | CharStreamProvider const& _charStreamProvider, |
118 | | util::Exception const& _exception, |
119 | | Error::Type _type, |
120 | | std::string const& _component, |
121 | | std::string const& _message, |
122 | | std::optional<ErrorId> _errorId = std::nullopt |
123 | | ) |
124 | 0 | { |
125 | 0 | std::string message; |
126 | | // TODO: consider enabling color |
127 | 0 | std::string formattedMessage = SourceReferenceFormatter::formatExceptionInformation( |
128 | 0 | _exception, |
129 | 0 | _type, |
130 | 0 | _charStreamProvider, |
131 | 0 | false // colored |
132 | 0 | ); |
133 | |
|
134 | 0 | if (std::string const* description = _exception.comment()) |
135 | 0 | message = ((_message.length() > 0) ? (_message + ": ") : "") + *description; |
136 | 0 | else |
137 | 0 | message = _message; |
138 | |
|
139 | 0 | Json error = formatError( |
140 | 0 | _type, |
141 | 0 | _component, |
142 | 0 | message, |
143 | 0 | formattedMessage, |
144 | 0 | formatSourceLocation(boost::get_error_info<errinfo_sourceLocation>(_exception)), |
145 | 0 | formatSecondarySourceLocation(boost::get_error_info<errinfo_secondarySourceLocation>(_exception)) |
146 | 0 | ); |
147 | |
|
148 | 0 | if (_errorId) |
149 | 0 | error["errorCode"] = std::to_string(_errorId.value().error); |
150 | |
|
151 | 0 | return error; |
152 | 0 | } |
153 | | |
154 | | /// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content. |
155 | | bool hashMatchesContent(std::string const& _hash, std::string const& _content) |
156 | 0 | { |
157 | 0 | try |
158 | 0 | { |
159 | 0 | return util::h256(_hash) == util::keccak256(_content); |
160 | 0 | } |
161 | 0 | catch (util::BadHexCharacter const&) |
162 | 0 | { |
163 | 0 | return false; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | bool isArtifactRequested(Json const& _outputSelection, std::string const& _artifact, bool _wildcardMatchesExperimental) |
168 | 0 | { |
169 | 0 | static std::set<std::string> experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson", "ethdebug"}; |
170 | 0 | for (auto const& selectedArtifactJson: _outputSelection) |
171 | 0 | { |
172 | 0 | std::string const& selectedArtifact = selectedArtifactJson.get<std::string>(); |
173 | 0 | if ( |
174 | 0 | _artifact == selectedArtifact || |
175 | 0 | boost::algorithm::starts_with(_artifact, selectedArtifact + ".") |
176 | 0 | ) |
177 | 0 | { |
178 | 0 | if (_artifact.find("ethdebug") != std::string::npos) |
179 | | // only accept exact matches for ethdebug, e.g. evm.bytecode.ethdebug |
180 | 0 | return selectedArtifact == _artifact; |
181 | 0 | return true; |
182 | 0 | } |
183 | 0 | else if (selectedArtifact == "*") |
184 | 0 | { |
185 | | // TODO: yulCFGJson is only experimental now, so it should not be matched by "*". |
186 | 0 | if (_artifact == "yulCFGJson") |
187 | 0 | return false; |
188 | | // TODO: everything ethdebug related is only experimental for now, so it should not be matched by "*". |
189 | 0 | if (_artifact.find("ethdebug") != std::string::npos) |
190 | 0 | return false; |
191 | | // "ir", "irOptimized" can only be matched by "*" if activated. |
192 | 0 | if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental) |
193 | 0 | return true; |
194 | 0 | } |
195 | 0 | } |
196 | 0 | return false; |
197 | 0 | } |
198 | | |
199 | | /// |
200 | | /// @a _outputSelection is a JSON object containing a two-level hashmap, where the first level is the filename, |
201 | | /// the second level is the contract name and the value is an array of artifact names to be requested for that contract. |
202 | | /// @a _file is the current file |
203 | | /// @a _contract is the current contract |
204 | | /// @a _artifact is the current artifact name |
205 | | /// |
206 | | /// @returns true if the @a _outputSelection has a match for the requested target in the specific file / contract. |
207 | | /// |
208 | | /// In @a _outputSelection the use of '*' as a wildcard is permitted. |
209 | | /// |
210 | | /// @TODO optimise this. Perhaps flatten the structure upfront. |
211 | | /// |
212 | | bool isArtifactRequested(Json const& _outputSelection, std::string const& _file, std::string const& _contract, std::string const& _artifact, bool _wildcardMatchesExperimental) |
213 | 0 | { |
214 | 0 | if (!_outputSelection.is_object()) |
215 | 0 | return false; |
216 | | |
217 | 0 | for (auto const& file: { _file, std::string("*") }) |
218 | 0 | if (_outputSelection.contains(file) && _outputSelection[file].is_object()) |
219 | 0 | { |
220 | | /// For SourceUnit-level targets (such as AST) only allow empty name, otherwise |
221 | | /// for Contract-level targets try both contract name and wildcard |
222 | 0 | std::vector<std::string> contracts{ _contract }; |
223 | 0 | if (!_contract.empty()) |
224 | 0 | contracts.emplace_back("*"); |
225 | 0 | for (auto const& contract: contracts) |
226 | 0 | if ( |
227 | 0 | _outputSelection[file].contains(contract) && |
228 | 0 | _outputSelection[file][contract].is_array() && |
229 | 0 | isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesExperimental) |
230 | 0 | ) |
231 | 0 | return true; |
232 | 0 | } |
233 | | |
234 | 0 | return false; |
235 | 0 | } |
236 | | |
237 | | bool isArtifactRequested(Json const& _outputSelection, std::string const& _file, std::string const& _contract, std::vector<std::string> const& _artifacts, bool _wildcardMatchesExperimental) |
238 | 0 | { |
239 | 0 | for (auto const& artifact: _artifacts) |
240 | 0 | if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesExperimental)) |
241 | 0 | return true; |
242 | 0 | return false; |
243 | 0 | } |
244 | | |
245 | | /// @returns all artifact names of the EVM object, either for creation or deploy time. |
246 | | std::vector<std::string> evmObjectComponents(std::string const& _objectKind) |
247 | 0 | { |
248 | 0 | solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", ""); |
249 | 0 | std::vector<std::string> components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences", ".ethdebug"}; |
250 | 0 | if (_objectKind == "deployedBytecode") |
251 | 0 | components.push_back(".immutableReferences"); |
252 | 0 | return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; }); |
253 | 0 | } |
254 | | |
255 | | /// @returns true if any binary was requested, i.e. we actually have to perform compilation. |
256 | | bool isBinaryRequested(Json const& _outputSelection) |
257 | 0 | { |
258 | 0 | if (!_outputSelection.is_object()) |
259 | 0 | return false; |
260 | | |
261 | | // This does not include "evm.methodIdentifiers" on purpose! |
262 | 0 | static std::vector<std::string> const outputsThatRequireBinaries = std::vector<std::string>{ |
263 | 0 | "*", |
264 | 0 | "ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson", |
265 | 0 | "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly", "ethdebug" |
266 | 0 | } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); |
267 | |
|
268 | 0 | for (auto const& fileRequests: _outputSelection) |
269 | 0 | for (auto const& requests: fileRequests) |
270 | 0 | for (auto const& output: outputsThatRequireBinaries) |
271 | 0 | if (isArtifactRequested(requests, output, false)) |
272 | 0 | return true; |
273 | 0 | return false; |
274 | 0 | } |
275 | | |
276 | | /// @returns true if EVM bytecode was requested, i.e. we have to run the old code generator. |
277 | | bool isEvmBytecodeRequested(Json const& _outputSelection) |
278 | 0 | { |
279 | 0 | if (!_outputSelection.is_object()) |
280 | 0 | return false; |
281 | | |
282 | 0 | static std::vector<std::string> const outputsThatRequireEvmBinaries = std::vector<std::string>{ |
283 | 0 | "*", |
284 | 0 | "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" |
285 | 0 | } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); |
286 | |
|
287 | 0 | for (auto const& fileRequests: _outputSelection) |
288 | 0 | for (auto const& requests: fileRequests) |
289 | 0 | for (auto const& output: outputsThatRequireEvmBinaries) |
290 | 0 | if (isArtifactRequested(requests, output, false)) |
291 | 0 | return true; |
292 | 0 | return false; |
293 | 0 | } |
294 | | |
295 | | /// @returns true if ethdebug was requested. |
296 | | bool isEthdebugRequested(Json const& _outputSelection) |
297 | 0 | { |
298 | 0 | if (!_outputSelection.is_object()) |
299 | 0 | return false; |
300 | | |
301 | 0 | for (auto const& fileRequests: _outputSelection) |
302 | 0 | for (auto const& requests: fileRequests) |
303 | 0 | for (auto const& request: requests) |
304 | 0 | if (request == "evm.bytecode.ethdebug" || request == "evm.deployedBytecode.ethdebug") |
305 | 0 | return true; |
306 | | |
307 | 0 | return false; |
308 | 0 | } |
309 | | |
310 | | /// @returns The set of selected contracts, along with their compiler pipeline configuration, based |
311 | | /// on outputs requested in the JSON. Translates wildcards to the ones understood by CompilerStack. |
312 | | /// Note that as an exception, '*' does not yet match "ir", "irAst", "irOptimized" or "irOptimizedAst". |
313 | | CompilerStack::ContractSelection pipelineConfig( |
314 | | Json const& _jsonOutputSelection |
315 | | ) |
316 | 0 | { |
317 | 0 | if (!_jsonOutputSelection.is_object()) |
318 | 0 | return {}; |
319 | | |
320 | 0 | CompilerStack::ContractSelection contractSelection; |
321 | 0 | for (auto const& [sourceUnitName, jsonOutputSelectionForSource]: _jsonOutputSelection.items()) |
322 | 0 | { |
323 | 0 | solAssert(jsonOutputSelectionForSource.is_object()); |
324 | 0 | for (auto const& [contractName, jsonOutputSelectionForContract]: jsonOutputSelectionForSource.items()) |
325 | 0 | { |
326 | 0 | solAssert(jsonOutputSelectionForContract.is_array()); |
327 | 0 | CompilerStack::PipelineConfig pipelineForContract; |
328 | 0 | for (Json const& request: jsonOutputSelectionForContract) |
329 | 0 | { |
330 | 0 | solAssert(request.is_string()); |
331 | 0 | pipelineForContract.irOptimization = |
332 | 0 | pipelineForContract.irOptimization || |
333 | 0 | request == "irOptimized" || |
334 | 0 | request == "irOptimizedAst" || |
335 | 0 | request == "yulCFGJson"; |
336 | 0 | pipelineForContract.irCodegen = |
337 | 0 | pipelineForContract.irCodegen || |
338 | 0 | pipelineForContract.irOptimization || |
339 | 0 | request == "ir" || |
340 | 0 | request == "irAst"; |
341 | 0 | pipelineForContract.bytecode = isEvmBytecodeRequested(_jsonOutputSelection); |
342 | 0 | } |
343 | 0 | std::string key = (sourceUnitName == "*") ? "" : sourceUnitName; |
344 | 0 | std::string value = (contractName == "*") ? "" : contractName; |
345 | 0 | contractSelection[key][value] = pipelineForContract; |
346 | 0 | } |
347 | 0 | } |
348 | 0 | return contractSelection; |
349 | 0 | } |
350 | | |
351 | | Json formatLinkReferences(std::map<size_t, std::string> const& linkReferences) |
352 | 0 | { |
353 | 0 | Json ret = Json::object(); |
354 | |
|
355 | 0 | for (auto const& ref: linkReferences) |
356 | 0 | { |
357 | 0 | std::string const& fullname = ref.second; |
358 | | |
359 | | // If the link reference does not contain a colon, assume that the file name is missing and |
360 | | // the whole string represents the library name. |
361 | 0 | size_t colon = fullname.rfind(':'); |
362 | 0 | std::string file = (colon != std::string::npos ? fullname.substr(0, colon) : ""); |
363 | 0 | std::string name = (colon != std::string::npos ? fullname.substr(colon + 1) : fullname); |
364 | |
|
365 | 0 | Json fileObject = ret.value(file, Json::object()); |
366 | 0 | Json libraryArray = fileObject.value(name, Json::array()); |
367 | |
|
368 | 0 | Json entry; |
369 | 0 | entry["start"] = Json(ref.first); |
370 | 0 | entry["length"] = 20; |
371 | |
|
372 | 0 | libraryArray.emplace_back(entry); |
373 | 0 | fileObject[name] = libraryArray; |
374 | 0 | ret[file] = fileObject; |
375 | 0 | } |
376 | |
|
377 | 0 | return ret; |
378 | 0 | } |
379 | | |
380 | | Json formatImmutableReferences(std::map<u256, evmasm::LinkerObject::ImmutableRefs> const& _immutableReferences) |
381 | 0 | { |
382 | 0 | Json ret = Json::object(); |
383 | |
|
384 | 0 | for (auto const& immutableReference: _immutableReferences) |
385 | 0 | { |
386 | 0 | auto const& [identifier, byteOffsets] = immutableReference.second; |
387 | 0 | Json array = Json::array(); |
388 | 0 | for (size_t byteOffset: byteOffsets) |
389 | 0 | { |
390 | 0 | Json byteRange; |
391 | 0 | byteRange["start"] = Json::number_unsigned_t(byteOffset); |
392 | 0 | byteRange["length"] = Json::number_unsigned_t(32); // immutable references are currently always 32 bytes wide |
393 | 0 | array.emplace_back(byteRange); |
394 | 0 | } |
395 | 0 | ret[identifier] = array; |
396 | 0 | } |
397 | |
|
398 | 0 | return ret; |
399 | 0 | } |
400 | | |
401 | | std::optional<Json> checkKeys(Json const& _input, std::set<std::string> const& _keys, std::string const& _name) |
402 | 0 | { |
403 | 0 | if (!_input.empty() && !_input.is_object()) |
404 | 0 | return formatFatalError(Error::Type::JSONError, "\"" + _name + "\" must be an object"); |
405 | | |
406 | 0 | for (auto const& [member, _]: _input.items()) |
407 | 0 | if (!_keys.count(member)) |
408 | 0 | return formatFatalError(Error::Type::JSONError, "Unknown key \"" + member + "\""); |
409 | | |
410 | 0 | return std::nullopt; |
411 | 0 | } |
412 | | |
413 | | std::optional<Json> checkRootKeys(Json const& _input) |
414 | 0 | { |
415 | 0 | static std::set<std::string> keys{"auxiliaryInput", "language", "settings", "sources"}; |
416 | 0 | return checkKeys(_input, keys, "root"); |
417 | 0 | } |
418 | | |
419 | | std::optional<Json> checkSourceKeys(Json const& _input, std::string const& _name) |
420 | 0 | { |
421 | 0 | static std::set<std::string> keys{"content", "keccak256", "urls"}; |
422 | 0 | return checkKeys(_input, keys, "sources." + _name); |
423 | 0 | } |
424 | | |
425 | | std::optional<Json> checkAuxiliaryInputKeys(Json const& _input) |
426 | 0 | { |
427 | 0 | static std::set<std::string> keys{"smtlib2responses"}; |
428 | 0 | return checkKeys(_input, keys, "auxiliaryInput"); |
429 | 0 | } |
430 | | |
431 | | std::optional<Json> checkSettingsKeys(Json const& _input) |
432 | 0 | { |
433 | 0 | static std::set<std::string> keys{"debug", "evmVersion", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; |
434 | 0 | return checkKeys(_input, keys, "settings"); |
435 | 0 | } |
436 | | |
437 | | std::optional<Json> checkModelCheckerSettingsKeys(Json const& _input) |
438 | 0 | { |
439 | 0 | static std::set<std::string> keys{"bmcLoopIterations", "contracts", "divModNoSlacks", "engine", "extCalls", "invariants", "printQuery", "showProvedSafe", "showUnproved", "showUnsupported", "solvers", "targets", "timeout"}; |
440 | 0 | return checkKeys(_input, keys, "modelChecker"); |
441 | 0 | } |
442 | | |
443 | | std::optional<Json> checkOptimizerKeys(Json const& _input) |
444 | 0 | { |
445 | 0 | static std::set<std::string> keys{"details", "enabled", "runs"}; |
446 | 0 | return checkKeys(_input, keys, "settings.optimizer"); |
447 | 0 | } |
448 | | |
449 | | std::optional<Json> checkOptimizerDetailsKeys(Json const& _input) |
450 | 0 | { |
451 | 0 | static std::set<std::string> keys{"peephole", "inliner", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails", "simpleCounterForLoopUncheckedIncrement"}; |
452 | 0 | return checkKeys(_input, keys, "settings.optimizer.details"); |
453 | 0 | } |
454 | | |
455 | | std::optional<Json> checkOptimizerDetail(Json const& _details, std::string const& _name, bool& _setting) |
456 | 0 | { |
457 | 0 | if (_details.contains(_name)) |
458 | 0 | { |
459 | 0 | if (!_details[_name].is_boolean()) |
460 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.optimizer.details." + _name + "\" must be Boolean"); |
461 | 0 | _setting = _details[_name].get<bool>(); |
462 | 0 | } |
463 | 0 | return {}; |
464 | 0 | } |
465 | | |
466 | | std::optional<Json> checkOptimizerDetailSteps(Json const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting, bool _runYulOptimizer) |
467 | 0 | { |
468 | 0 | if (_details.contains(_name)) |
469 | 0 | { |
470 | 0 | if (_details[_name].is_string()) |
471 | 0 | { |
472 | 0 | std::string const fullSequence = _details[_name].get<std::string>(); |
473 | 0 | if (!_runYulOptimizer && !OptimiserSuite::isEmptyOptimizerSequence(fullSequence)) |
474 | 0 | { |
475 | 0 | std::string errorMessage = |
476 | 0 | "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted." |
477 | 0 | " Note that the empty optimizer sequence is properly denoted by \":\"."; |
478 | 0 | return formatFatalError(Error::Type::JSONError, errorMessage); |
479 | 0 | } |
480 | | |
481 | 0 | try |
482 | 0 | { |
483 | 0 | yul::OptimiserSuite::validateSequence(_details[_name].get<std::string>()); |
484 | 0 | } |
485 | 0 | catch (yul::OptimizerException const& _exception) |
486 | 0 | { |
487 | 0 | return formatFatalError( |
488 | 0 | Error::Type::JSONError, |
489 | 0 | "Invalid optimizer step sequence in \"settings.optimizer.details." + _name + "\": " + _exception.what() |
490 | 0 | ); |
491 | 0 | } |
492 | | |
493 | 0 | auto const delimiterPos = fullSequence.find(":"); |
494 | 0 | _optimiserSetting = fullSequence.substr(0, delimiterPos); |
495 | |
|
496 | 0 | if (delimiterPos != std::string::npos) |
497 | 0 | _cleanupSetting = fullSequence.substr(delimiterPos + 1); |
498 | 0 | else |
499 | 0 | solAssert(_cleanupSetting == OptimiserSettings::DefaultYulOptimiserCleanupSteps); |
500 | 0 | } |
501 | 0 | else |
502 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.optimizer.details." + _name + "\" must be a string"); |
503 | |
|
504 | 0 | } |
505 | 0 | return {}; |
506 | 0 | } |
507 | | |
508 | | std::optional<Json> checkMetadataKeys(Json const& _input) |
509 | 0 | { |
510 | 0 | if (_input.is_object()) |
511 | 0 | { |
512 | 0 | if (_input.contains("appendCBOR") && !_input["appendCBOR"].is_boolean()) |
513 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.metadata.appendCBOR\" must be Boolean"); |
514 | 0 | if (_input.contains("useLiteralContent") && !_input["useLiteralContent"].is_boolean()) |
515 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.metadata.useLiteralContent\" must be Boolean"); |
516 | | |
517 | 0 | static std::set<std::string> hashes{"ipfs", "bzzr1", "none"}; |
518 | 0 | if (_input.contains("bytecodeHash") && !hashes.count(_input["bytecodeHash"].get<std::string>())) |
519 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.metadata.bytecodeHash\" must be \"ipfs\", \"bzzr1\" or \"none\""); |
520 | 0 | } |
521 | 0 | static std::set<std::string> keys{"appendCBOR", "useLiteralContent", "bytecodeHash"}; |
522 | 0 | return checkKeys(_input, keys, "settings.metadata"); |
523 | 0 | } |
524 | | |
525 | | std::optional<Json> checkOutputSelection(Json const& _outputSelection) |
526 | 0 | { |
527 | 0 | if (!_outputSelection.empty() && !_outputSelection.is_object()) |
528 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.outputSelection\" must be an object"); |
529 | | |
530 | 0 | for (auto const& [sourceName, sourceVal]: _outputSelection.items()) |
531 | 0 | { |
532 | 0 | if (!sourceVal.is_object()) |
533 | 0 | return formatFatalError( |
534 | 0 | Error::Type::JSONError, |
535 | 0 | "\"settings.outputSelection." + sourceName + "\" must be an object" |
536 | 0 | ); |
537 | | |
538 | 0 | for (auto const& [contractName, contractVal]: sourceVal.items()) |
539 | 0 | { |
540 | 0 | if (!contractVal.is_array()) |
541 | 0 | return formatFatalError( |
542 | 0 | Error::Type::JSONError, |
543 | 0 | "\"settings.outputSelection." + |
544 | 0 | sourceName + |
545 | 0 | "." + |
546 | 0 | contractName + |
547 | 0 | "\" must be a string array" |
548 | 0 | ); |
549 | | |
550 | 0 | for (auto const& output: contractVal) |
551 | 0 | if (!output.is_string()) |
552 | 0 | return formatFatalError( |
553 | 0 | Error::Type::JSONError, |
554 | 0 | "\"settings.outputSelection." + |
555 | 0 | sourceName + |
556 | 0 | "." + |
557 | 0 | contractName + |
558 | 0 | "\" must be a string array" |
559 | 0 | ); |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | 0 | return std::nullopt; |
564 | 0 | } |
565 | | |
566 | | /// Validates the optimizer settings and returns them in a parsed object. |
567 | | /// On error returns the json-formatted error message. |
568 | | std::variant<OptimiserSettings, Json> parseOptimizerSettings(std::string_view const _language, Json const& _jsonInput) |
569 | 0 | { |
570 | 0 | if (auto result = checkOptimizerKeys(_jsonInput)) |
571 | 0 | return *result; |
572 | | |
573 | 0 | OptimiserSettings settings = _language == "EVMAssembly" ? OptimiserSettings::none() : OptimiserSettings::minimal(); |
574 | |
|
575 | 0 | if (_jsonInput.contains("enabled")) |
576 | 0 | { |
577 | 0 | if (!_jsonInput["enabled"].is_boolean()) |
578 | 0 | return formatFatalError(Error::Type::JSONError, "The \"enabled\" setting must be a Boolean."); |
579 | | |
580 | 0 | if (_jsonInput["enabled"].get<bool>()) |
581 | 0 | settings = OptimiserSettings::standard(); |
582 | 0 | } |
583 | | |
584 | 0 | if (_jsonInput.contains("runs")) |
585 | 0 | { |
586 | 0 | if (!_jsonInput["runs"].is_number_unsigned()) |
587 | 0 | return formatFatalError(Error::Type::JSONError, "The \"runs\" setting must be an unsigned number."); |
588 | 0 | settings.expectedExecutionsPerDeployment = _jsonInput["runs"].get<size_t>(); |
589 | 0 | } |
590 | | |
591 | 0 | if (_jsonInput.contains("details")) |
592 | 0 | { |
593 | 0 | Json const& details = _jsonInput["details"]; |
594 | 0 | if (auto result = checkOptimizerDetailsKeys(details)) |
595 | 0 | return *result; |
596 | | |
597 | 0 | if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole)) |
598 | 0 | return *error; |
599 | 0 | if (auto error = checkOptimizerDetail(details, "inliner", settings.runInliner)) |
600 | 0 | return *error; |
601 | 0 | if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover)) |
602 | 0 | return *error; |
603 | 0 | if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals)) |
604 | 0 | return *error; |
605 | 0 | if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate)) |
606 | 0 | return *error; |
607 | 0 | if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE)) |
608 | 0 | return *error; |
609 | 0 | if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser)) |
610 | 0 | return *error; |
611 | 0 | if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser)) |
612 | 0 | return *error; |
613 | 0 | if (auto error = checkOptimizerDetail(details, "simpleCounterForLoopUncheckedIncrement", settings.simpleCounterForLoopUncheckedIncrement)) |
614 | 0 | return *error; |
615 | 0 | settings.optimizeStackAllocation = settings.runYulOptimiser; |
616 | 0 | if (details.contains("yulDetails")) |
617 | 0 | { |
618 | 0 | if (!settings.runYulOptimiser) |
619 | 0 | { |
620 | 0 | if (checkKeys(details["yulDetails"], {"optimizerSteps"}, "settings.optimizer.details.yulDetails")) |
621 | 0 | return formatFatalError(Error::Type::JSONError, "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled."); |
622 | 0 | if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) |
623 | 0 | return *error; |
624 | 0 | return {std::move(settings)}; |
625 | 0 | } |
626 | | |
627 | 0 | if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails")) |
628 | 0 | return *result; |
629 | 0 | if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) |
630 | 0 | return *error; |
631 | 0 | if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) |
632 | 0 | return *error; |
633 | 0 | } |
634 | 0 | } |
635 | 0 | return {std::move(settings)}; |
636 | 0 | } |
637 | | |
638 | | } |
639 | | |
640 | | std::variant<StandardCompiler::InputsAndSettings, Json> StandardCompiler::parseInput(Json const& _input) |
641 | 0 | { |
642 | 0 | InputsAndSettings ret; |
643 | |
|
644 | 0 | if (!_input.is_object()) |
645 | 0 | return formatFatalError(Error::Type::JSONError, "Input is not a JSON object."); |
646 | | |
647 | 0 | if (auto result = checkRootKeys(_input)) |
648 | 0 | return *result; |
649 | | |
650 | 0 | ret.language = _input.value<std::string>("language", ""); |
651 | |
|
652 | 0 | Json const& sources = _input.value<Json>("sources", Json()); |
653 | |
|
654 | 0 | if (!sources.is_object() && !sources.is_null()) |
655 | 0 | return formatFatalError(Error::Type::JSONError, "\"sources\" is not a JSON object."); |
656 | | |
657 | 0 | if (sources.empty()) |
658 | 0 | return formatFatalError(Error::Type::JSONError, "No input sources specified."); |
659 | | |
660 | 0 | ret.errors = Json::array(); |
661 | 0 | ret.sources = Json::object(); |
662 | |
|
663 | 0 | if (ret.language == "Solidity" || ret.language == "Yul") |
664 | 0 | { |
665 | 0 | for (auto const& [sourceName, sourceValue]: sources.items()) |
666 | 0 | { |
667 | 0 | std::string hash; |
668 | |
|
669 | 0 | if (auto result = checkSourceKeys(sourceValue, sourceName)) |
670 | 0 | return *result; |
671 | | |
672 | 0 | if (sourceValue.contains("keccak256") && sourceValue["keccak256"].is_string()) |
673 | 0 | hash = sourceValue["keccak256"].get<std::string>(); |
674 | |
|
675 | 0 | if (sourceValue.contains("content") && sourceValue["content"].is_string()) |
676 | 0 | { |
677 | 0 | std::string content = sourceValue["content"].get<std::string>(); |
678 | 0 | if (!hash.empty() && !hashMatchesContent(hash, content)) |
679 | 0 | ret.errors.emplace_back(formatError( |
680 | 0 | Error::Type::IOError, |
681 | 0 | "general", |
682 | 0 | "Mismatch between content and supplied hash for \"" + sourceName + "\"" |
683 | 0 | )); |
684 | 0 | else |
685 | 0 | ret.sources[sourceName] = content; |
686 | 0 | } |
687 | 0 | else if (sourceValue["urls"].is_array()) |
688 | 0 | { |
689 | 0 | if (!m_readFile) |
690 | 0 | return formatFatalError( |
691 | 0 | Error::Type::JSONError, "No import callback supplied, but URL is requested." |
692 | 0 | ); |
693 | | |
694 | 0 | std::vector<std::string> failures; |
695 | 0 | bool found = false; |
696 | |
|
697 | 0 | for (auto const& url: sourceValue["urls"]) |
698 | 0 | { |
699 | 0 | if (!url.is_string()) |
700 | 0 | return formatFatalError(Error::Type::JSONError, "URL must be a string."); |
701 | 0 | ReadCallback::Result result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), url.get<std::string>()); |
702 | 0 | if (result.success) |
703 | 0 | { |
704 | 0 | if (!hash.empty() && !hashMatchesContent(hash, result.responseOrErrorMessage)) |
705 | 0 | ret.errors.emplace_back(formatError( |
706 | 0 | Error::Type::IOError, |
707 | 0 | "general", |
708 | 0 | "Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.get<std::string>() + "\"" |
709 | 0 | )); |
710 | 0 | else |
711 | 0 | { |
712 | 0 | ret.sources[sourceName] = result.responseOrErrorMessage; |
713 | 0 | found = true; |
714 | 0 | break; |
715 | 0 | } |
716 | 0 | } |
717 | 0 | else |
718 | 0 | failures.push_back( |
719 | 0 | "Cannot import url (\"" + url.get<std::string>() + "\"): " + result.responseOrErrorMessage |
720 | 0 | ); |
721 | 0 | } |
722 | | |
723 | 0 | for (auto const& failure: failures) |
724 | 0 | { |
725 | | /// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors. |
726 | 0 | ret.errors.emplace_back(formatError( |
727 | 0 | found ? Error::Type::Warning : Error::Type::IOError, |
728 | 0 | "general", |
729 | 0 | failure |
730 | 0 | )); |
731 | 0 | } |
732 | 0 | } |
733 | 0 | else |
734 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid input source specified."); |
735 | 0 | } |
736 | 0 | } |
737 | 0 | else if (ret.language == "SolidityAST") |
738 | 0 | { |
739 | 0 | for (auto const& [sourceName, sourceValue]: sources.items()) |
740 | 0 | ret.sources[sourceName] = util::jsonCompactPrint(sourceValue); |
741 | 0 | } |
742 | 0 | else if (ret.language == "EVMAssembly") |
743 | 0 | { |
744 | 0 | for (auto const& [sourceName, sourceValue]: sources.items()) |
745 | 0 | { |
746 | 0 | solAssert(sources.contains(sourceName)); |
747 | 0 | if ( |
748 | 0 | !sourceValue.contains("assemblyJson") || |
749 | 0 | !sourceValue["assemblyJson"].is_object() || |
750 | 0 | sourceValue.size() != 1 |
751 | 0 | ) |
752 | 0 | return formatFatalError( |
753 | 0 | Error::Type::JSONError, |
754 | 0 | "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources." + sourceName |
755 | 0 | ); |
756 | | |
757 | 0 | ret.jsonSources[sourceName] = sourceValue["assemblyJson"]; |
758 | 0 | } |
759 | 0 | if (ret.jsonSources.size() != 1) |
760 | 0 | return formatFatalError( |
761 | 0 | Error::Type::JSONError, |
762 | 0 | "EVMAssembly import only supports exactly one input file." |
763 | 0 | ); |
764 | 0 | } |
765 | 0 | Json const& auxInputs = _input.value("auxiliaryInput", Json::object()); |
766 | |
|
767 | 0 | if (auto result = checkAuxiliaryInputKeys(auxInputs)) |
768 | 0 | return *result; |
769 | | |
770 | 0 | if (!auxInputs.empty()) |
771 | 0 | { |
772 | 0 | Json const& smtlib2Responses = auxInputs.value("smtlib2responses", Json::object()); |
773 | 0 | if (!smtlib2Responses.empty()) |
774 | 0 | { |
775 | 0 | if (!smtlib2Responses.is_object()) |
776 | 0 | return formatFatalError(Error::Type::JSONError, "\"auxiliaryInput.smtlib2responses\" must be an object."); |
777 | | |
778 | 0 | for (auto const& [hashString, response]: smtlib2Responses.items()) |
779 | 0 | { |
780 | 0 | util::h256 hash; |
781 | 0 | try |
782 | 0 | { |
783 | 0 | hash = util::h256(hashString); |
784 | 0 | } |
785 | 0 | catch (util::BadHexCharacter const&) |
786 | 0 | { |
787 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid hex encoding of SMTLib2 auxiliary input."); |
788 | 0 | } |
789 | | |
790 | 0 | if (!response.is_string()) |
791 | 0 | return formatFatalError( |
792 | 0 | Error::Type::JSONError, |
793 | 0 | "\"smtlib2Responses." + hashString + "\" must be a string." |
794 | 0 | ); |
795 | | |
796 | 0 | ret.smtLib2Responses[hash] = response.get<std::string>(); |
797 | 0 | } |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | 0 | Json const& settings = _input.value("settings", Json::object()); |
802 | |
|
803 | 0 | if (auto result = checkSettingsKeys(settings)) |
804 | 0 | return *result; |
805 | | |
806 | 0 | if (settings.contains("stopAfter")) |
807 | 0 | { |
808 | 0 | if (!settings["stopAfter"].is_string()) |
809 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.stopAfter\" must be a string."); |
810 | | |
811 | 0 | if (settings["stopAfter"].get<std::string>() != "parsing") |
812 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\"."); |
813 | | |
814 | 0 | ret.stopAfter = CompilerStack::State::Parsed; |
815 | 0 | } |
816 | | |
817 | 0 | if (settings.contains("viaIR")) |
818 | 0 | { |
819 | 0 | if (!settings["viaIR"].is_boolean()) |
820 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.viaIR\" must be a Boolean."); |
821 | 0 | ret.viaIR = settings["viaIR"].get<bool>(); |
822 | 0 | } |
823 | | |
824 | 0 | if (settings.contains("evmVersion")) |
825 | 0 | { |
826 | 0 | if (!settings["evmVersion"].is_string()) |
827 | 0 | return formatFatalError(Error::Type::JSONError, "evmVersion must be a string."); |
828 | 0 | std::optional<langutil::EVMVersion> version = langutil::EVMVersion::fromString(settings["evmVersion"].get<std::string>()); |
829 | 0 | if (!version) |
830 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid EVM version requested."); |
831 | 0 | if (version < EVMVersion::constantinople()) |
832 | 0 | ret.errors.emplace_back(formatError( |
833 | 0 | Error::Type::Warning, |
834 | 0 | "general", |
835 | 0 | "Support for EVM versions older than constantinople is deprecated and will be removed in the future." |
836 | 0 | )); |
837 | 0 | ret.evmVersion = *version; |
838 | 0 | } |
839 | | |
840 | 0 | if (settings.contains("eofVersion")) |
841 | 0 | { |
842 | 0 | if (!settings["eofVersion"].is_number_unsigned()) |
843 | 0 | return formatFatalError(Error::Type::JSONError, "eofVersion must be an unsigned integer."); |
844 | 0 | auto eofVersion = settings["eofVersion"].get<uint8_t>(); |
845 | 0 | if (eofVersion != 1) |
846 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid EOF version requested."); |
847 | 0 | ret.eofVersion = 1; |
848 | 0 | } |
849 | | |
850 | 0 | if (ret.eofVersion.has_value() && !ret.evmVersion.supportsEOF()) |
851 | 0 | return formatFatalError(Error::Type::JSONError, "EOF is not supported by EVM versions earlier than " + EVMVersion::firstWithEOF().name() + "."); |
852 | | |
853 | 0 | if (settings.contains("debug")) |
854 | 0 | { |
855 | 0 | if (auto result = checkKeys(settings["debug"], {"revertStrings", "debugInfo"}, "settings.debug")) |
856 | 0 | return *result; |
857 | | |
858 | 0 | if (settings["debug"].contains("revertStrings")) |
859 | 0 | { |
860 | 0 | if (!settings["debug"]["revertStrings"].is_string()) |
861 | 0 | return formatFatalError(Error::Type::JSONError, "settings.debug.revertStrings must be a string."); |
862 | 0 | std::optional<RevertStrings> revertStrings = revertStringsFromString(settings["debug"]["revertStrings"].get<std::string>()); |
863 | 0 | if (!revertStrings) |
864 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid value for settings.debug.revertStrings."); |
865 | 0 | if (*revertStrings == RevertStrings::VerboseDebug) |
866 | 0 | return formatFatalError( |
867 | 0 | Error::Type::UnimplementedFeatureError, |
868 | 0 | "Only \"default\", \"strip\" and \"debug\" are implemented for settings.debug.revertStrings for now." |
869 | 0 | ); |
870 | 0 | ret.revertStrings = *revertStrings; |
871 | 0 | } |
872 | | |
873 | 0 | if (settings["debug"].contains("debugInfo")) |
874 | 0 | { |
875 | 0 | if (!settings["debug"]["debugInfo"].is_array()) |
876 | 0 | return formatFatalError(Error::Type::JSONError, "settings.debug.debugInfo must be an array."); |
877 | | |
878 | 0 | std::vector<std::string> components; |
879 | 0 | for (Json const& arrayValue: settings["debug"]["debugInfo"]) |
880 | 0 | components.push_back(arrayValue.get<std::string>()); |
881 | |
|
882 | 0 | std::optional<DebugInfoSelection> debugInfoSelection = DebugInfoSelection::fromComponents( |
883 | 0 | components, |
884 | 0 | true /* _acceptWildcards */ |
885 | 0 | ); |
886 | 0 | if (!debugInfoSelection.has_value()) |
887 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid value in settings.debug.debugInfo."); |
888 | | |
889 | 0 | if (debugInfoSelection->snippet && !debugInfoSelection->location) |
890 | 0 | return formatFatalError( |
891 | 0 | Error::Type::JSONError, |
892 | 0 | "To use 'snippet' with settings.debug.debugInfo you must select also 'location'." |
893 | 0 | ); |
894 | | |
895 | 0 | ret.debugInfoSelection = debugInfoSelection.value(); |
896 | 0 | } |
897 | 0 | } |
898 | | |
899 | 0 | if (settings.contains("remappings") && !settings["remappings"].is_array()) |
900 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.remappings\" must be an array of strings."); |
901 | | |
902 | 0 | for (auto const& remapping: settings.value("remappings", Json::object())) |
903 | 0 | { |
904 | 0 | if (!remapping.is_string()) |
905 | 0 | return formatFatalError(Error::Type::JSONError, "\"settings.remappings\" must be an array of strings"); |
906 | 0 | if (auto r = ImportRemapper::parseRemapping(remapping.get<std::string>())) |
907 | 0 | ret.remappings.emplace_back(std::move(*r)); |
908 | 0 | else |
909 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid remapping: \"" + remapping.get<std::string>() + "\""); |
910 | 0 | } |
911 | | |
912 | 0 | if (settings.contains("optimizer")) |
913 | 0 | { |
914 | 0 | auto optimiserSettings = parseOptimizerSettings(ret.language, settings["optimizer"]); |
915 | 0 | if (std::holds_alternative<Json>(optimiserSettings)) |
916 | 0 | return std::get<Json>(std::move(optimiserSettings)); // was an error |
917 | 0 | else |
918 | 0 | ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings)); |
919 | 0 | } |
920 | 0 | else if (ret.language == "EVMAssembly") |
921 | 0 | ret.optimiserSettings = OptimiserSettings::none(); |
922 | 0 | else |
923 | 0 | ret.optimiserSettings = OptimiserSettings::minimal(); |
924 | | |
925 | 0 | Json const& jsonLibraries = settings.value("libraries", Json::object()); |
926 | 0 | if (!jsonLibraries.is_object()) |
927 | 0 | return formatFatalError(Error::Type::JSONError, "\"libraries\" is not a JSON object."); |
928 | 0 | for (auto const& [sourceName, jsonSourceName]: jsonLibraries.items()) |
929 | 0 | { |
930 | 0 | if (!jsonSourceName.is_object()) |
931 | 0 | return formatFatalError(Error::Type::JSONError, "Library entry is not a JSON object."); |
932 | 0 | for (auto const& [library, libraryValue]: jsonSourceName.items()) |
933 | 0 | { |
934 | 0 | if (!libraryValue.is_string()) |
935 | 0 | return formatFatalError(Error::Type::JSONError, "Library address must be a string."); |
936 | 0 | std::string address = libraryValue.get<std::string>(); |
937 | |
|
938 | 0 | if (!boost::starts_with(address, "0x")) |
939 | 0 | return formatFatalError( |
940 | 0 | Error::Type::JSONError, |
941 | 0 | "Library address is not prefixed with \"0x\"." |
942 | 0 | ); |
943 | | |
944 | 0 | if (address.length() != 42) |
945 | 0 | return formatFatalError( |
946 | 0 | Error::Type::JSONError, |
947 | 0 | "Library address is of invalid length." |
948 | 0 | ); |
949 | | |
950 | 0 | try |
951 | 0 | { |
952 | 0 | ret.libraries[sourceName + ":" + library] = util::h160(address); |
953 | 0 | } |
954 | 0 | catch (util::BadHexCharacter const&) |
955 | 0 | { |
956 | 0 | return formatFatalError( |
957 | 0 | Error::Type::JSONError, |
958 | 0 | "Invalid library address (\"" + address + "\") supplied." |
959 | 0 | ); |
960 | 0 | } |
961 | 0 | } |
962 | 0 | } |
963 | | |
964 | 0 | Json const& metadataSettings = settings.value("metadata", Json::object()); |
965 | |
|
966 | 0 | if (auto result = checkMetadataKeys(metadataSettings)) |
967 | 0 | return *result; |
968 | | |
969 | 0 | solAssert(CompilerStack::defaultMetadataFormat() != CompilerStack::MetadataFormat::NoMetadata, ""); |
970 | 0 | ret.metadataFormat = |
971 | 0 | metadataSettings.value("appendCBOR", Json(true)) ? |
972 | 0 | CompilerStack::defaultMetadataFormat() : |
973 | 0 | CompilerStack::MetadataFormat::NoMetadata; |
974 | |
|
975 | 0 | ret.metadataLiteralSources = |
976 | 0 | metadataSettings.contains("useLiteralContent") && |
977 | 0 | metadataSettings["useLiteralContent"].is_boolean() && |
978 | 0 | metadataSettings["useLiteralContent"].get<bool>(); |
979 | 0 | if (metadataSettings.contains("bytecodeHash")) |
980 | 0 | { |
981 | 0 | auto metadataHash = metadataSettings["bytecodeHash"].get<std::string>(); |
982 | 0 | ret.metadataHash = |
983 | 0 | metadataHash == "ipfs" ? |
984 | 0 | CompilerStack::MetadataHash::IPFS : |
985 | 0 | metadataHash == "bzzr1" ? |
986 | 0 | CompilerStack::MetadataHash::Bzzr1 : |
987 | 0 | CompilerStack::MetadataHash::None; |
988 | 0 | if (ret.metadataFormat == CompilerStack::MetadataFormat::NoMetadata && ret.metadataHash != CompilerStack::MetadataHash::None) |
989 | 0 | return formatFatalError( |
990 | 0 | Error::Type::JSONError, |
991 | 0 | "When the parameter \"appendCBOR\" is set to false, the parameter \"bytecodeHash\" cannot be set to \"" + |
992 | 0 | metadataHash + |
993 | 0 | "\". The parameter \"bytecodeHash\" should either be skipped, or set to \"none\"." |
994 | 0 | ); |
995 | 0 | } |
996 | | |
997 | 0 | Json const& outputSelection = settings.value("outputSelection", Json::object()); |
998 | |
|
999 | 0 | if (auto jsonError = checkOutputSelection(outputSelection)) |
1000 | 0 | return *jsonError; |
1001 | | |
1002 | 0 | ret.outputSelection = outputSelection; |
1003 | |
|
1004 | 0 | if (ret.stopAfter != CompilerStack::State::CompilationSuccessful && isBinaryRequested(ret.outputSelection)) |
1005 | 0 | return formatFatalError( |
1006 | 0 | Error::Type::JSONError, |
1007 | 0 | "Requested output selection conflicts with \"settings.stopAfter\"." |
1008 | 0 | ); |
1009 | | |
1010 | 0 | Json const& modelCheckerSettings = settings.value("modelChecker", Json::object()); |
1011 | |
|
1012 | 0 | if (auto result = checkModelCheckerSettingsKeys(modelCheckerSettings)) |
1013 | 0 | return *result; |
1014 | | |
1015 | 0 | if (modelCheckerSettings.contains("contracts")) |
1016 | 0 | { |
1017 | 0 | auto const& sources = modelCheckerSettings["contracts"]; |
1018 | 0 | if (!sources.is_object() && !sources.is_null()) |
1019 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.contracts is not a JSON object."); |
1020 | | |
1021 | 0 | std::map<std::string, std::set<std::string>> sourceContracts; |
1022 | 0 | for (auto const& [source, contracts]: sources.items()) |
1023 | 0 | { |
1024 | 0 | if (source.empty()) |
1025 | 0 | return formatFatalError(Error::Type::JSONError, "Source name cannot be empty."); |
1026 | | |
1027 | 0 | if (!contracts.is_array()) |
1028 | 0 | return formatFatalError(Error::Type::JSONError, "Source contracts must be an array."); |
1029 | | |
1030 | 0 | for (auto const& contract: contracts) |
1031 | 0 | { |
1032 | 0 | if (!contract.is_string()) |
1033 | 0 | return formatFatalError(Error::Type::JSONError, "Every contract in settings.modelChecker.contracts must be a string."); |
1034 | 0 | if (contract.get<std::string>().empty()) |
1035 | 0 | return formatFatalError(Error::Type::JSONError, "Contract name cannot be empty."); |
1036 | 0 | sourceContracts[source].insert(contract.get<std::string>()); |
1037 | 0 | } |
1038 | | |
1039 | 0 | if (sourceContracts[source].empty()) |
1040 | 0 | return formatFatalError(Error::Type::JSONError, "Source contracts must be a non-empty array."); |
1041 | 0 | } |
1042 | 0 | ret.modelCheckerSettings.contracts = {std::move(sourceContracts)}; |
1043 | 0 | } |
1044 | | |
1045 | 0 | if (modelCheckerSettings.contains("divModNoSlacks")) |
1046 | 0 | { |
1047 | 0 | auto const& divModNoSlacks = modelCheckerSettings["divModNoSlacks"]; |
1048 | 0 | if (!divModNoSlacks.is_boolean()) |
1049 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.divModNoSlacks must be a Boolean."); |
1050 | 0 | ret.modelCheckerSettings.divModNoSlacks = divModNoSlacks.get<bool>(); |
1051 | 0 | } |
1052 | | |
1053 | 0 | if (modelCheckerSettings.contains("engine")) |
1054 | 0 | { |
1055 | 0 | if (!modelCheckerSettings["engine"].is_string()) |
1056 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.engine must be a string."); |
1057 | 0 | std::optional<ModelCheckerEngine> engine = ModelCheckerEngine::fromString(modelCheckerSettings["engine"].get<std::string>()); |
1058 | 0 | if (!engine) |
1059 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid model checker engine requested."); |
1060 | 0 | ret.modelCheckerSettings.engine = *engine; |
1061 | 0 | } |
1062 | | |
1063 | 0 | if (modelCheckerSettings.contains("bmcLoopIterations")) |
1064 | 0 | { |
1065 | 0 | if (!ret.modelCheckerSettings.engine.bmc) |
1066 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.bmcLoopIterations requires the BMC engine to be enabled."); |
1067 | 0 | if (modelCheckerSettings["bmcLoopIterations"].is_number_unsigned()) |
1068 | 0 | ret.modelCheckerSettings.bmcLoopIterations = modelCheckerSettings["bmcLoopIterations"].get<unsigned>(); |
1069 | 0 | else |
1070 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.bmcLoopIterations must be an unsigned integer."); |
1071 | 0 | } |
1072 | | |
1073 | 0 | if (modelCheckerSettings.contains("extCalls")) |
1074 | 0 | { |
1075 | 0 | if (!modelCheckerSettings["extCalls"].is_string()) |
1076 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.extCalls must be a string."); |
1077 | 0 | std::optional<ModelCheckerExtCalls> extCalls = ModelCheckerExtCalls::fromString(modelCheckerSettings["extCalls"].get<std::string>()); |
1078 | 0 | if (!extCalls) |
1079 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid model checker extCalls requested."); |
1080 | 0 | ret.modelCheckerSettings.externalCalls = *extCalls; |
1081 | 0 | } |
1082 | | |
1083 | 0 | if (modelCheckerSettings.contains("invariants")) |
1084 | 0 | { |
1085 | 0 | auto const& invariantsArray = modelCheckerSettings["invariants"]; |
1086 | 0 | if (!invariantsArray.is_array()) |
1087 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.invariants must be an array."); |
1088 | | |
1089 | 0 | ModelCheckerInvariants invariants; |
1090 | 0 | for (auto const& i: invariantsArray) |
1091 | 0 | { |
1092 | 0 | if (!i.is_string()) |
1093 | 0 | return formatFatalError(Error::Type::JSONError, "Every invariant type in settings.modelChecker.invariants must be a string."); |
1094 | 0 | if (!invariants.setFromString(i.get<std::string>())) |
1095 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid model checker invariants requested."); |
1096 | 0 | } |
1097 | | |
1098 | 0 | if (invariants.invariants.empty()) |
1099 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.invariants must be a non-empty array."); |
1100 | | |
1101 | 0 | ret.modelCheckerSettings.invariants = invariants; |
1102 | 0 | } |
1103 | | |
1104 | 0 | if (modelCheckerSettings.contains("showProvedSafe")) |
1105 | 0 | { |
1106 | 0 | auto const& showProvedSafe = modelCheckerSettings["showProvedSafe"]; |
1107 | 0 | if (!showProvedSafe.is_boolean()) |
1108 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.showProvedSafe must be a Boolean value."); |
1109 | 0 | ret.modelCheckerSettings.showProvedSafe = showProvedSafe.get<bool>(); |
1110 | 0 | } |
1111 | | |
1112 | 0 | if (modelCheckerSettings.contains("showUnproved")) |
1113 | 0 | { |
1114 | 0 | auto const& showUnproved = modelCheckerSettings["showUnproved"]; |
1115 | 0 | if (!showUnproved.is_boolean()) |
1116 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.showUnproved must be a Boolean value."); |
1117 | 0 | ret.modelCheckerSettings.showUnproved = showUnproved.get<bool>(); |
1118 | 0 | } |
1119 | | |
1120 | 0 | if (modelCheckerSettings.contains("showUnsupported")) |
1121 | 0 | { |
1122 | 0 | auto const& showUnsupported = modelCheckerSettings["showUnsupported"]; |
1123 | 0 | if (!showUnsupported.is_boolean()) |
1124 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.showUnsupported must be a Boolean value."); |
1125 | 0 | ret.modelCheckerSettings.showUnsupported = showUnsupported.get<bool>(); |
1126 | 0 | } |
1127 | | |
1128 | 0 | if (modelCheckerSettings.contains("solvers")) |
1129 | 0 | { |
1130 | 0 | auto const& solversArray = modelCheckerSettings["solvers"]; |
1131 | 0 | if (!solversArray.is_array()) |
1132 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.solvers must be an array."); |
1133 | | |
1134 | 0 | smtutil::SMTSolverChoice solvers; |
1135 | 0 | for (auto const& s: solversArray) |
1136 | 0 | { |
1137 | 0 | if (!s.is_string()) |
1138 | 0 | return formatFatalError(Error::Type::JSONError, "Every target in settings.modelChecker.solvers must be a string."); |
1139 | 0 | if (!solvers.setSolver(s.get<std::string>())) |
1140 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid model checker solvers requested."); |
1141 | 0 | } |
1142 | | |
1143 | 0 | ret.modelCheckerSettings.solvers = solvers; |
1144 | 0 | } |
1145 | | |
1146 | 0 | if (modelCheckerSettings.contains("printQuery")) |
1147 | 0 | { |
1148 | 0 | auto const& printQuery = modelCheckerSettings["printQuery"]; |
1149 | 0 | if (!printQuery.is_boolean()) |
1150 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.printQuery must be a Boolean value."); |
1151 | | |
1152 | 0 | ret.modelCheckerSettings.printQuery = printQuery.get<bool>(); |
1153 | 0 | } |
1154 | | |
1155 | 0 | if (modelCheckerSettings.contains("targets")) |
1156 | 0 | { |
1157 | 0 | auto const& targetsArray = modelCheckerSettings["targets"]; |
1158 | 0 | if (!targetsArray.is_array()) |
1159 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.targets must be an array."); |
1160 | | |
1161 | 0 | ModelCheckerTargets targets; |
1162 | 0 | for (auto const& t: targetsArray) |
1163 | 0 | { |
1164 | 0 | if (!t.is_string()) |
1165 | 0 | return formatFatalError(Error::Type::JSONError, "Every target in settings.modelChecker.targets must be a string."); |
1166 | 0 | if (!targets.setFromString(t.get<std::string>())) |
1167 | 0 | return formatFatalError(Error::Type::JSONError, "Invalid model checker targets requested."); |
1168 | 0 | } |
1169 | | |
1170 | 0 | if (targets.targets.empty()) |
1171 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.targets must be a non-empty array."); |
1172 | | |
1173 | 0 | ret.modelCheckerSettings.targets = targets; |
1174 | 0 | } |
1175 | | |
1176 | 0 | if (modelCheckerSettings.contains("timeout")) |
1177 | 0 | { |
1178 | 0 | if (!modelCheckerSettings["timeout"].is_number_unsigned()) |
1179 | 0 | return formatFatalError(Error::Type::JSONError, "settings.modelChecker.timeout must be an unsigned integer."); |
1180 | 0 | ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].get<Json::number_unsigned_t>(); |
1181 | 0 | } |
1182 | | |
1183 | 0 | if ((ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug) || isEthdebugRequested(ret.outputSelection)) |
1184 | 0 | { |
1185 | 0 | if (ret.language != "Solidity" && ret.language != "Yul") |
1186 | 0 | return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' 'ethdebug' is only supported for languages 'Solidity' and 'Yul'."); |
1187 | 0 | } |
1188 | | |
1189 | 0 | if (isEthdebugRequested(ret.outputSelection)) |
1190 | 0 | { |
1191 | 0 | if (ret.language == "Solidity" && !ret.viaIR) |
1192 | 0 | return formatFatalError(Error::Type::FatalError, "'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' can only be selected as output, if 'viaIR' was set."); |
1193 | | |
1194 | 0 | if (!ret.debugInfoSelection.has_value()) |
1195 | 0 | { |
1196 | 0 | ret.debugInfoSelection = DebugInfoSelection::Default(); |
1197 | 0 | ret.debugInfoSelection->enable("ethdebug"); |
1198 | 0 | } |
1199 | 0 | else |
1200 | 0 | { |
1201 | 0 | if (!ret.debugInfoSelection->ethdebug && ret.language == "Solidity") |
1202 | 0 | return formatFatalError(Error::Type::FatalError, "'ethdebug' needs to be enabled in 'settings.debug.debugInfo', if 'evm.bytecode.ethdebug' or 'evm.deployedBytecode.ethdebug' was selected as output."); |
1203 | 0 | } |
1204 | 0 | } |
1205 | | |
1206 | 0 | if ( |
1207 | 0 | ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug && (ret.language == "Solidity" || ret.language == "Yul") && |
1208 | 0 | !pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection) |
1209 | 0 | ) |
1210 | 0 | return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected."); |
1211 | | |
1212 | 0 | if (isEthdebugRequested(ret.outputSelection)) |
1213 | 0 | if (ret.optimiserSettings.runYulOptimiser) |
1214 | 0 | solUnimplemented("Optimization is not yet supported with ethdebug."); |
1215 | | |
1216 | 0 | return {std::move(ret)}; |
1217 | 0 | } |
1218 | | |
1219 | | std::map<std::string, Json> StandardCompiler::parseAstFromInput(StringMap const& _sources) |
1220 | 0 | { |
1221 | 0 | std::map<std::string, Json> sourceJsons; |
1222 | 0 | for (auto const& [sourceName, sourceCode]: _sources) |
1223 | 0 | { |
1224 | 0 | Json ast; |
1225 | 0 | astAssert(util::jsonParseStrict(sourceCode, ast), "Input file could not be parsed to JSON"); |
1226 | 0 | std::string astKey = ast.contains("ast") ? "ast" : "AST"; |
1227 | |
|
1228 | 0 | astAssert(ast.contains(astKey), "astkey is not member"); |
1229 | 0 | astAssert(ast[astKey]["nodeType"].get<std::string>() == "SourceUnit", "Top-level node should be a 'SourceUnit'"); |
1230 | 0 | astAssert(sourceJsons.count(sourceName) == 0, "All sources must have unique names"); |
1231 | 0 | sourceJsons.emplace(sourceName, std::move(ast[astKey])); |
1232 | 0 | } |
1233 | 0 | return sourceJsons; |
1234 | 0 | } |
1235 | | |
1236 | | Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _inputsAndSettings) |
1237 | 0 | { |
1238 | 0 | solAssert(_inputsAndSettings.language == "EVMAssembly"); |
1239 | 0 | solAssert(_inputsAndSettings.sources.empty()); |
1240 | 0 | solAssert(_inputsAndSettings.jsonSources.size() == 1); |
1241 | | |
1242 | 0 | if (!isBinaryRequested(_inputsAndSettings.outputSelection)) |
1243 | 0 | return Json::object(); |
1244 | | |
1245 | 0 | evmasm::EVMAssemblyStack stack( |
1246 | 0 | _inputsAndSettings.evmVersion, |
1247 | 0 | _inputsAndSettings.eofVersion, |
1248 | 0 | evmasm::Assembly::OptimiserSettings::translateSettings( |
1249 | 0 | _inputsAndSettings.optimiserSettings |
1250 | 0 | ) |
1251 | 0 | ); |
1252 | 0 | std::string const& sourceName = _inputsAndSettings.jsonSources.begin()->first; // result of structured binding can only be used within lambda from C++20 on. |
1253 | 0 | Json const& sourceJson = _inputsAndSettings.jsonSources.begin()->second; |
1254 | 0 | try |
1255 | 0 | { |
1256 | 0 | stack.analyze(sourceName, sourceJson); |
1257 | 0 | stack.assemble(); |
1258 | 0 | } |
1259 | 0 | catch (evmasm::AssemblyImportException const& e) |
1260 | 0 | { |
1261 | 0 | return formatFatalError(Error::Type::Exception, "Assembly import error: " + std::string(e.what())); |
1262 | 0 | } |
1263 | 0 | catch (...) |
1264 | 0 | { |
1265 | 0 | return formatError( |
1266 | 0 | Error::Type::Exception, |
1267 | 0 | "general", |
1268 | 0 | "Unknown exception during assembly import: " + boost::current_exception_diagnostic_information() |
1269 | 0 | ); |
1270 | 0 | } |
1271 | 0 | if (!stack.compilationSuccessful()) |
1272 | 0 | return Json::object(); |
1273 | | |
1274 | | // EVM |
1275 | 0 | bool const wildcardMatchesExperimental = false; |
1276 | 0 | Json evmData; |
1277 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.assembly", wildcardMatchesExperimental)) |
1278 | 0 | evmData["assembly"] = stack.assemblyString(sourceName, {}); |
1279 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.legacyAssembly", wildcardMatchesExperimental)) |
1280 | 0 | evmData["legacyAssembly"] = stack.assemblyJSON(sourceName); |
1281 | |
|
1282 | 0 | if (isArtifactRequested( |
1283 | 0 | _inputsAndSettings.outputSelection, |
1284 | 0 | sourceName, |
1285 | 0 | "", |
1286 | 0 | evmObjectComponents("bytecode"), |
1287 | 0 | wildcardMatchesExperimental |
1288 | 0 | )) |
1289 | 0 | { |
1290 | 0 | auto const evmCreationArtifactRequested = [&](std::string const& _element) { |
1291 | 0 | return isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.bytecode." + _element, wildcardMatchesExperimental); |
1292 | 0 | }; |
1293 | |
|
1294 | 0 | Json creationJSON; |
1295 | 0 | if (evmCreationArtifactRequested("object")) |
1296 | 0 | creationJSON["object"] = stack.object(sourceName).toHex(); |
1297 | 0 | if (evmCreationArtifactRequested("opcodes")) |
1298 | 0 | creationJSON["opcodes"] = evmasm::disassemble(stack.object(sourceName).bytecode, _inputsAndSettings.evmVersion); |
1299 | 0 | if (evmCreationArtifactRequested("sourceMap")) |
1300 | 0 | creationJSON["sourceMap"] = stack.sourceMapping(sourceName) ? *stack.sourceMapping(sourceName) : ""; |
1301 | 0 | if (evmCreationArtifactRequested("functionDebugData")) |
1302 | 0 | creationJSON["functionDebugData"] = formatFunctionDebugData(stack.object(sourceName).functionDebugData); |
1303 | 0 | if (evmCreationArtifactRequested("linkReferences")) |
1304 | 0 | creationJSON["linkReferences"] = formatLinkReferences(stack.object(sourceName).linkReferences); |
1305 | 0 | if (evmCreationArtifactRequested("ethdebug")) |
1306 | 0 | creationJSON["ethdebug"] = stack.ethdebug(sourceName); |
1307 | 0 | evmData["bytecode"] = creationJSON; |
1308 | 0 | } |
1309 | |
|
1310 | 0 | if (isArtifactRequested( |
1311 | 0 | _inputsAndSettings.outputSelection, |
1312 | 0 | sourceName, |
1313 | 0 | "", |
1314 | 0 | evmObjectComponents("deployedBytecode"), |
1315 | 0 | wildcardMatchesExperimental |
1316 | 0 | )) |
1317 | 0 | { |
1318 | 0 | auto const evmDeployedArtifactRequested = [&](std::string const& _element) { |
1319 | 0 | return isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.deployedBytecode." + _element, wildcardMatchesExperimental); |
1320 | 0 | }; |
1321 | |
|
1322 | 0 | Json deployedJSON; |
1323 | 0 | if (evmDeployedArtifactRequested("object")) |
1324 | 0 | deployedJSON["object"] = stack.runtimeObject(sourceName).toHex(); |
1325 | 0 | if (evmDeployedArtifactRequested("opcodes")) |
1326 | 0 | deployedJSON["opcodes"] = evmasm::disassemble(stack.runtimeObject(sourceName).bytecode, _inputsAndSettings.evmVersion); |
1327 | 0 | if (evmDeployedArtifactRequested("sourceMap")) |
1328 | 0 | deployedJSON["sourceMap"] = stack.runtimeSourceMapping(sourceName) ? *stack.runtimeSourceMapping(sourceName) : ""; |
1329 | 0 | if (evmDeployedArtifactRequested("functionDebugData")) |
1330 | 0 | deployedJSON["functionDebugData"] = formatFunctionDebugData(stack.runtimeObject(sourceName).functionDebugData); |
1331 | 0 | if (evmDeployedArtifactRequested("linkReferences")) |
1332 | 0 | deployedJSON["linkReferences"] = formatLinkReferences(stack.runtimeObject(sourceName).linkReferences); |
1333 | 0 | if (evmDeployedArtifactRequested("immutableReferences")) |
1334 | 0 | deployedJSON["immutableReferences"] = formatImmutableReferences(stack.runtimeObject(sourceName).immutableReferences); |
1335 | 0 | if (evmDeployedArtifactRequested("ethdebug")) |
1336 | 0 | deployedJSON["ethdebug"] = stack.ethdebugRuntime(sourceName); |
1337 | 0 | evmData["deployedBytecode"] = deployedJSON; |
1338 | 0 | } |
1339 | |
|
1340 | 0 | Json contractData; |
1341 | 0 | if (!evmData.empty()) |
1342 | 0 | contractData["evm"] = evmData; |
1343 | |
|
1344 | 0 | Json contractsOutput; |
1345 | 0 | contractsOutput[sourceName][""] = contractData; |
1346 | 0 | Json output; |
1347 | 0 | output["contracts"] = contractsOutput; |
1348 | 0 | return util::removeNullMembers(output); |
1349 | 0 | } |
1350 | | |
1351 | | Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inputsAndSettings) |
1352 | 0 | { |
1353 | 0 | solAssert(_inputsAndSettings.jsonSources.empty()); |
1354 | | |
1355 | 0 | CompilerStack compilerStack(m_readFile); |
1356 | |
|
1357 | 0 | StringMap sourceList = std::move(_inputsAndSettings.sources); |
1358 | 0 | if (_inputsAndSettings.language == "Solidity") |
1359 | 0 | compilerStack.setSources(sourceList); |
1360 | 0 | for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses) |
1361 | 0 | compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); |
1362 | 0 | compilerStack.setViaIR(_inputsAndSettings.viaIR); |
1363 | 0 | compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); |
1364 | 0 | compilerStack.setEOFVersion(_inputsAndSettings.eofVersion); |
1365 | 0 | compilerStack.setRemappings(std::move(_inputsAndSettings.remappings)); |
1366 | 0 | compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings)); |
1367 | 0 | compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings); |
1368 | 0 | if (_inputsAndSettings.debugInfoSelection.has_value()) |
1369 | 0 | compilerStack.selectDebugInfo(_inputsAndSettings.debugInfoSelection.value()); |
1370 | 0 | compilerStack.setLibraries(_inputsAndSettings.libraries); |
1371 | 0 | compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources); |
1372 | 0 | compilerStack.setMetadataFormat(_inputsAndSettings.metadataFormat); |
1373 | 0 | compilerStack.setMetadataHash(_inputsAndSettings.metadataHash); |
1374 | 0 | compilerStack.selectContracts(pipelineConfig(_inputsAndSettings.outputSelection)); |
1375 | 0 | compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings); |
1376 | |
|
1377 | 0 | Json errors = std::move(_inputsAndSettings.errors); |
1378 | |
|
1379 | 0 | bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection); |
1380 | |
|
1381 | 0 | try |
1382 | 0 | { |
1383 | 0 | if (_inputsAndSettings.language == "SolidityAST") |
1384 | 0 | { |
1385 | 0 | try |
1386 | 0 | { |
1387 | 0 | compilerStack.importASTs(parseAstFromInput(sourceList)); |
1388 | 0 | if (!compilerStack.analyze()) |
1389 | 0 | errors.emplace_back(formatError(Error::Type::FatalError, "general", "Analysis of the AST failed.")); |
1390 | 0 | if (binariesRequested) |
1391 | 0 | compilerStack.compile(); |
1392 | 0 | } |
1393 | 0 | catch (util::Exception const& _exc) |
1394 | 0 | { |
1395 | 0 | solThrow(util::Exception, "Failed to import AST: "s + _exc.what()); |
1396 | 0 | } |
1397 | 0 | } |
1398 | 0 | else |
1399 | 0 | { |
1400 | 0 | if (binariesRequested) |
1401 | 0 | compilerStack.compile(); |
1402 | 0 | else |
1403 | 0 | compilerStack.parseAndAnalyze(_inputsAndSettings.stopAfter); |
1404 | |
|
1405 | 0 | for (auto const& error: compilerStack.errors()) |
1406 | 0 | errors.emplace_back(formatErrorWithException( |
1407 | 0 | compilerStack, |
1408 | 0 | *error, |
1409 | 0 | error->type(), |
1410 | 0 | "general", |
1411 | 0 | "", |
1412 | 0 | error->errorId() |
1413 | 0 | )); |
1414 | 0 | } |
1415 | 0 | } |
1416 | | // NOTE: This includes langutil::StackTooDeepError. |
1417 | 0 | catch (CompilerError const& _exception) |
1418 | 0 | { |
1419 | 0 | errors.emplace_back(formatErrorWithException( |
1420 | 0 | compilerStack, |
1421 | 0 | _exception, |
1422 | 0 | Error::Type::CompilerError, |
1423 | 0 | "general", |
1424 | 0 | "Compiler error (" + _exception.lineInfo() + ")" |
1425 | 0 | )); |
1426 | 0 | } |
1427 | 0 | catch (yul::StackTooDeepError const& _exception) |
1428 | 0 | { |
1429 | 0 | errors.emplace_back(formatErrorWithException( |
1430 | 0 | compilerStack, |
1431 | 0 | _exception, |
1432 | 0 | Error::Type::YulException, |
1433 | 0 | "general", |
1434 | 0 | "" // No prefix needed. These messages already say it's a "stack too deep" error. |
1435 | 0 | )); |
1436 | 0 | } |
1437 | 0 | catch (InternalCompilerError const&) |
1438 | 0 | { |
1439 | 0 | errors.emplace_back(formatError( |
1440 | 0 | Error::Type::InternalCompilerError, |
1441 | 0 | "general", |
1442 | 0 | "Internal compiler error:\n" + boost::current_exception_diagnostic_information() |
1443 | 0 | )); |
1444 | 0 | } |
1445 | 0 | catch (UnimplementedFeatureError const& _exception) |
1446 | 0 | { |
1447 | | // let StandardCompiler::compile handle this |
1448 | 0 | throw _exception; |
1449 | 0 | } |
1450 | 0 | catch (YulAssertion const&) |
1451 | 0 | { |
1452 | 0 | errors.emplace_back(formatError( |
1453 | 0 | Error::Type::YulException, |
1454 | 0 | "general", |
1455 | 0 | "Yul assertion failed:\n" + boost::current_exception_diagnostic_information() |
1456 | 0 | )); |
1457 | 0 | } |
1458 | 0 | catch (smtutil::SMTLogicError const&) |
1459 | 0 | { |
1460 | 0 | errors.emplace_back(formatError( |
1461 | 0 | Error::Type::SMTLogicException, |
1462 | 0 | "general", |
1463 | 0 | "SMT logic error:\n" + boost::current_exception_diagnostic_information() |
1464 | 0 | )); |
1465 | 0 | } |
1466 | 0 | catch (...) |
1467 | 0 | { |
1468 | 0 | errors.emplace_back(formatError( |
1469 | 0 | Error::Type::Exception, |
1470 | 0 | "general", |
1471 | 0 | "Unknown exception during compilation: " + boost::current_exception_diagnostic_information() |
1472 | 0 | )); |
1473 | 0 | } |
1474 | | |
1475 | 0 | bool parsingSuccess = compilerStack.state() >= CompilerStack::State::Parsed; |
1476 | 0 | bool analysisSuccess = compilerStack.state() >= CompilerStack::State::AnalysisSuccessful; |
1477 | 0 | bool compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful; |
1478 | | |
1479 | | // If analysis fails, the artifacts inside CompilerStack are potentially incomplete and must not be returned. |
1480 | | // Note that not completing analysis due to stopAfter does not count as a failure. It's neither failure nor success. |
1481 | 0 | bool analysisFailed = !analysisSuccess && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisSuccessful; |
1482 | 0 | bool compilationFailed = !compilationSuccess && binariesRequested; |
1483 | 0 | if (compilationFailed || analysisFailed || !parsingSuccess) |
1484 | 0 | solAssert(!errors.empty(), "No error reported, but compilation failed."); |
1485 | | |
1486 | 0 | Json output; |
1487 | |
|
1488 | 0 | if (errors.size() > 0) |
1489 | 0 | output["errors"] = std::move(errors); |
1490 | |
|
1491 | 0 | if (!compilerStack.unhandledSMTLib2Queries().empty()) |
1492 | 0 | for (std::string const& query: compilerStack.unhandledSMTLib2Queries()) |
1493 | 0 | output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + util::keccak256(query).hex()] = query; |
1494 | |
|
1495 | 0 | bool const wildcardMatchesExperimental = false; |
1496 | |
|
1497 | 0 | output["sources"] = Json::object(); |
1498 | 0 | unsigned sourceIndex = 0; |
1499 | | // NOTE: A case that will pass `parsingSuccess && !analysisFailed` but not `analysisSuccess` is |
1500 | | // stopAfter: parsing with no parsing errors. |
1501 | 0 | if (parsingSuccess && !analysisFailed) |
1502 | 0 | for (std::string const& sourceName: compilerStack.sourceNames()) |
1503 | 0 | { |
1504 | 0 | Json sourceResult; |
1505 | 0 | sourceResult["id"] = sourceIndex++; |
1506 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental)) |
1507 | 0 | sourceResult["ast"] = ASTJsonExporter(compilerStack.state(), compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName)); |
1508 | 0 | output["sources"][sourceName] = sourceResult; |
1509 | 0 | } |
1510 | |
|
1511 | 0 | Json contractsOutput; |
1512 | 0 | for (std::string const& contractName: analysisSuccess ? compilerStack.contractNames() : std::vector<std::string>()) |
1513 | 0 | { |
1514 | 0 | size_t colon = contractName.rfind(':'); |
1515 | 0 | solAssert(colon != std::string::npos, ""); |
1516 | 0 | std::string file = contractName.substr(0, colon); |
1517 | 0 | std::string name = contractName.substr(colon + 1); |
1518 | | |
1519 | | // ABI, storage layout, documentation and metadata |
1520 | 0 | Json contractData; |
1521 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental)) |
1522 | 0 | contractData["abi"] = compilerStack.contractABI(contractName); |
1523 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false)) |
1524 | 0 | contractData["storageLayout"] = compilerStack.storageLayout(contractName); |
1525 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "transientStorageLayout", false)) |
1526 | 0 | contractData["transientStorageLayout"] = compilerStack.transientStorageLayout(contractName); |
1527 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental)) |
1528 | 0 | contractData["metadata"] = compilerStack.metadata(contractName); |
1529 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental)) |
1530 | 0 | contractData["userdoc"] = compilerStack.natspecUser(contractName); |
1531 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesExperimental)) |
1532 | 0 | contractData["devdoc"] = compilerStack.natspecDev(contractName); |
1533 | | |
1534 | | // IR |
1535 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesExperimental)) |
1536 | 0 | contractData["ir"] = compilerStack.yulIR(contractName).value_or(""); |
1537 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irAst", wildcardMatchesExperimental)) |
1538 | 0 | contractData["irAst"] = compilerStack.yulIRAst(contractName).value_or(Json{}); |
1539 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental)) |
1540 | 0 | contractData["irOptimized"] = compilerStack.yulIROptimized(contractName).value_or(""); |
1541 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimizedAst", wildcardMatchesExperimental)) |
1542 | 0 | contractData["irOptimizedAst"] = compilerStack.yulIROptimizedAst(contractName).value_or(Json{}); |
1543 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "yulCFGJson", wildcardMatchesExperimental)) |
1544 | 0 | contractData["yulCFGJson"] = compilerStack.yulCFGJson(contractName).value_or(Json{}); |
1545 | | |
1546 | | // EVM |
1547 | 0 | Json evmData; |
1548 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental)) |
1549 | 0 | evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList); |
1550 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental)) |
1551 | 0 | evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName); |
1552 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental)) |
1553 | 0 | evmData["methodIdentifiers"] = compilerStack.interfaceSymbols(contractName)["methods"]; |
1554 | 0 | if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental)) |
1555 | 0 | evmData["gasEstimates"] = compilerStack.gasEstimates(contractName); |
1556 | |
|
1557 | 0 | if (compilationSuccess && isArtifactRequested( |
1558 | 0 | _inputsAndSettings.outputSelection, |
1559 | 0 | file, |
1560 | 0 | name, |
1561 | 0 | evmObjectComponents("bytecode"), |
1562 | 0 | wildcardMatchesExperimental |
1563 | 0 | )) |
1564 | 0 | { |
1565 | 0 | auto const evmCreationArtifactRequested = [&](std::string const& _element) { |
1566 | 0 | return isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.bytecode." + _element, wildcardMatchesExperimental); |
1567 | 0 | }; |
1568 | |
|
1569 | 0 | Json creationJSON; |
1570 | 0 | if (evmCreationArtifactRequested("object")) |
1571 | 0 | creationJSON["object"] = compilerStack.object(contractName).toHex(); |
1572 | 0 | if (evmCreationArtifactRequested("opcodes")) |
1573 | 0 | creationJSON["opcodes"] = evmasm::disassemble(compilerStack.object(contractName).bytecode, _inputsAndSettings.evmVersion); |
1574 | 0 | if (evmCreationArtifactRequested("sourceMap")) |
1575 | 0 | creationJSON["sourceMap"] = compilerStack.sourceMapping(contractName) ? *compilerStack.sourceMapping(contractName) : ""; |
1576 | 0 | if (evmCreationArtifactRequested("functionDebugData")) |
1577 | 0 | creationJSON["functionDebugData"] = formatFunctionDebugData(compilerStack.object(contractName).functionDebugData); |
1578 | 0 | if (evmCreationArtifactRequested("linkReferences")) |
1579 | 0 | creationJSON["linkReferences"] = formatLinkReferences(compilerStack.object(contractName).linkReferences); |
1580 | 0 | if (evmCreationArtifactRequested("generatedSources")) |
1581 | 0 | creationJSON["generatedSources"] = compilerStack.generatedSources(contractName, /* _runtime */ false); |
1582 | 0 | if (evmCreationArtifactRequested("ethdebug")) |
1583 | 0 | creationJSON["ethdebug"] = compilerStack.ethdebug(contractName); |
1584 | 0 | evmData["bytecode"] = creationJSON; |
1585 | 0 | } |
1586 | |
|
1587 | 0 | if (compilationSuccess && isArtifactRequested( |
1588 | 0 | _inputsAndSettings.outputSelection, |
1589 | 0 | file, |
1590 | 0 | name, |
1591 | 0 | evmObjectComponents("deployedBytecode"), |
1592 | 0 | wildcardMatchesExperimental |
1593 | 0 | )) |
1594 | 0 | { |
1595 | 0 | auto const evmDeployedArtifactRequested = [&](std::string const& _element) { |
1596 | 0 | return isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.deployedBytecode." + _element, wildcardMatchesExperimental); |
1597 | 0 | }; |
1598 | |
|
1599 | 0 | Json deployedJSON; |
1600 | 0 | if (evmDeployedArtifactRequested("object")) |
1601 | 0 | deployedJSON["object"] = compilerStack.runtimeObject(contractName).toHex(); |
1602 | 0 | if (evmDeployedArtifactRequested("opcodes")) |
1603 | 0 | deployedJSON["opcodes"] = evmasm::disassemble(compilerStack.runtimeObject(contractName).bytecode, _inputsAndSettings.evmVersion); |
1604 | 0 | if (evmDeployedArtifactRequested("sourceMap")) |
1605 | 0 | deployedJSON["sourceMap"] = compilerStack.runtimeSourceMapping(contractName) ? *compilerStack.runtimeSourceMapping(contractName) : ""; |
1606 | 0 | if (evmDeployedArtifactRequested("functionDebugData")) |
1607 | 0 | deployedJSON["functionDebugData"] = formatFunctionDebugData(compilerStack.runtimeObject(contractName).functionDebugData); |
1608 | 0 | if (evmDeployedArtifactRequested("linkReferences")) |
1609 | 0 | deployedJSON["linkReferences"] = formatLinkReferences(compilerStack.runtimeObject(contractName).linkReferences); |
1610 | 0 | if (evmDeployedArtifactRequested("immutableReferences")) |
1611 | 0 | deployedJSON["immutableReferences"] = formatImmutableReferences(compilerStack.runtimeObject(contractName).immutableReferences); |
1612 | 0 | if (evmDeployedArtifactRequested("generatedSources")) |
1613 | 0 | deployedJSON["generatedSources"] = compilerStack.generatedSources(contractName, /* _runtime */ true); |
1614 | 0 | if (evmDeployedArtifactRequested("ethdebug")) |
1615 | 0 | deployedJSON["ethdebug"] = compilerStack.ethdebugRuntime(contractName); |
1616 | 0 | evmData["deployedBytecode"] = deployedJSON; |
1617 | 0 | } |
1618 | |
|
1619 | 0 | if (!evmData.empty()) |
1620 | 0 | contractData["evm"] = evmData; |
1621 | |
|
1622 | 0 | if (!contractData.empty()) |
1623 | 0 | { |
1624 | 0 | if (!contractsOutput.contains(file)) |
1625 | 0 | contractsOutput[file] = Json::object(); |
1626 | 0 | contractsOutput[file][name] = contractData; |
1627 | 0 | } |
1628 | 0 | } |
1629 | | |
1630 | 0 | if (isEthdebugRequested(_inputsAndSettings.outputSelection)) |
1631 | 0 | output["ethdebug"] = compilerStack.ethdebug(); |
1632 | |
|
1633 | 0 | if (!contractsOutput.empty()) |
1634 | 0 | output["contracts"] = contractsOutput; |
1635 | |
|
1636 | 0 | return output; |
1637 | 0 | } |
1638 | | |
1639 | | |
1640 | | Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) |
1641 | 0 | { |
1642 | 0 | solAssert(_inputsAndSettings.jsonSources.empty()); |
1643 | | |
1644 | 0 | Json output; |
1645 | 0 | output["errors"] = std::move(_inputsAndSettings.errors); |
1646 | |
|
1647 | 0 | if (_inputsAndSettings.sources.size() != 1) |
1648 | 0 | { |
1649 | 0 | output["errors"].emplace_back(formatError( |
1650 | 0 | Error::Type::JSONError, |
1651 | 0 | "general", |
1652 | 0 | "Yul mode only supports exactly one input file." |
1653 | 0 | )); |
1654 | 0 | return output; |
1655 | 0 | } |
1656 | 0 | if (!_inputsAndSettings.smtLib2Responses.empty()) |
1657 | 0 | { |
1658 | 0 | output["errors"].emplace_back(formatError( |
1659 | 0 | Error::Type::JSONError, |
1660 | 0 | "general", |
1661 | 0 | "Yul mode does not support smtlib2responses." |
1662 | 0 | )); |
1663 | 0 | return output; |
1664 | 0 | } |
1665 | 0 | if (!_inputsAndSettings.remappings.empty()) |
1666 | 0 | { |
1667 | 0 | output["errors"].emplace_back(formatError( |
1668 | 0 | Error::Type::JSONError, |
1669 | 0 | "general", |
1670 | 0 | "Field \"settings.remappings\" cannot be used for Yul." |
1671 | 0 | )); |
1672 | 0 | return output; |
1673 | 0 | } |
1674 | 0 | if (_inputsAndSettings.revertStrings != RevertStrings::Default) |
1675 | 0 | { |
1676 | 0 | output["errors"].emplace_back(formatError( |
1677 | 0 | Error::Type::JSONError, |
1678 | 0 | "general", |
1679 | 0 | "Field \"settings.debug.revertStrings\" cannot be used for Yul." |
1680 | 0 | )); |
1681 | 0 | return output; |
1682 | 0 | } |
1683 | | |
1684 | 0 | YulStack stack( |
1685 | 0 | _inputsAndSettings.evmVersion, |
1686 | 0 | _inputsAndSettings.eofVersion, |
1687 | 0 | YulStack::Language::StrictAssembly, |
1688 | 0 | _inputsAndSettings.optimiserSettings, |
1689 | 0 | _inputsAndSettings.debugInfoSelection.has_value() ? |
1690 | 0 | _inputsAndSettings.debugInfoSelection.value() : |
1691 | 0 | DebugInfoSelection::Default() |
1692 | 0 | ); |
1693 | 0 | std::string const& sourceName = _inputsAndSettings.sources.begin()->first; |
1694 | 0 | std::string const& sourceContents = _inputsAndSettings.sources.begin()->second; |
1695 | |
|
1696 | 0 | std::string contractName; |
1697 | 0 | bool const wildcardMatchesExperimental = true; |
1698 | 0 | MachineAssemblyObject object; |
1699 | 0 | MachineAssemblyObject deployedObject; |
1700 | |
|
1701 | 0 | bool successful = stack.parseAndAnalyze(sourceName, sourceContents); |
1702 | 0 | if (!successful) |
1703 | | // Inconsistent state - stop here to receive error reports from users |
1704 | 0 | solAssert(stack.hasErrors(), "No error reported, but parsing/analysis failed."); |
1705 | 0 | else |
1706 | 0 | { |
1707 | 0 | contractName = stack.parserResult()->name; |
1708 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental)) |
1709 | 0 | output["contracts"][sourceName][contractName]["ir"] = stack.print(); |
1710 | |
|
1711 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ast", wildcardMatchesExperimental)) |
1712 | 0 | { |
1713 | 0 | Json sourceResult; |
1714 | 0 | sourceResult["id"] = 0; |
1715 | 0 | sourceResult["ast"] = stack.astJson(); |
1716 | 0 | output["sources"][sourceName] = sourceResult; |
1717 | 0 | } |
1718 | 0 | stack.optimize(); |
1719 | 0 | std::tie(object, deployedObject) = stack.assembleWithDeployed(); |
1720 | 0 | if (object.bytecode) |
1721 | 0 | object.bytecode->link(_inputsAndSettings.libraries); |
1722 | 0 | if (deployedObject.bytecode) |
1723 | 0 | deployedObject.bytecode->link(_inputsAndSettings.libraries); |
1724 | 0 | } |
1725 | | |
1726 | 0 | for (auto const& error: stack.errors()) |
1727 | 0 | { |
1728 | 0 | auto err = std::dynamic_pointer_cast<Error const>(error); |
1729 | |
|
1730 | 0 | output["errors"].emplace_back(formatErrorWithException( |
1731 | 0 | stack, |
1732 | 0 | *error, |
1733 | 0 | err->type(), |
1734 | 0 | "general", |
1735 | 0 | "" |
1736 | 0 | )); |
1737 | 0 | } |
1738 | 0 | if (stack.hasErrors()) |
1739 | 0 | return output; |
1740 | | |
1741 | 0 | for (auto&& [kind, isDeployed]: {make_pair("bytecode"s, false), make_pair("deployedBytecode"s, true)}) |
1742 | 0 | if (isArtifactRequested( |
1743 | 0 | _inputsAndSettings.outputSelection, |
1744 | 0 | sourceName, |
1745 | 0 | contractName, |
1746 | 0 | evmObjectComponents(kind), |
1747 | 0 | wildcardMatchesExperimental |
1748 | 0 | )) |
1749 | 0 | { |
1750 | 0 | auto const evmArtifactRequested = [&](std::string const& _kind, std::string const& _element) { |
1751 | 0 | return isArtifactRequested( |
1752 | 0 | _inputsAndSettings.outputSelection, |
1753 | 0 | sourceName, |
1754 | 0 | contractName, |
1755 | 0 | "evm." + _kind + "." + _element, |
1756 | 0 | wildcardMatchesExperimental |
1757 | 0 | ); |
1758 | 0 | }; |
1759 | |
|
1760 | 0 | MachineAssemblyObject const& selectedObject = isDeployed ? deployedObject : object; |
1761 | 0 | if (selectedObject.bytecode) |
1762 | 0 | { |
1763 | 0 | Json bytecodeJSON; |
1764 | 0 | if (evmArtifactRequested(kind, "object")) |
1765 | 0 | bytecodeJSON["object"] = selectedObject.bytecode->toHex(); |
1766 | 0 | if (evmArtifactRequested(kind, "opcodes")) |
1767 | 0 | bytecodeJSON["opcodes"] = evmasm::disassemble(selectedObject.bytecode->bytecode, _inputsAndSettings.evmVersion); |
1768 | 0 | if (evmArtifactRequested(kind, "sourceMap")) |
1769 | 0 | bytecodeJSON["sourceMap"] = selectedObject.sourceMappings ? *selectedObject.sourceMappings : ""; |
1770 | 0 | if (evmArtifactRequested(kind, "functionDebugData")) |
1771 | 0 | bytecodeJSON["functionDebugData"] = formatFunctionDebugData(selectedObject.bytecode->functionDebugData); |
1772 | 0 | if (evmArtifactRequested(kind, "linkReferences")) |
1773 | 0 | bytecodeJSON["linkReferences"] = formatLinkReferences(selectedObject.bytecode->linkReferences); |
1774 | 0 | if (evmArtifactRequested(kind, "ethdebug")) |
1775 | 0 | bytecodeJSON["ethdebug"] = selectedObject.ethdebug; |
1776 | 0 | if (isDeployed && evmArtifactRequested(kind, "immutableReferences")) |
1777 | 0 | bytecodeJSON["immutableReferences"] = formatImmutableReferences(selectedObject.bytecode->immutableReferences); |
1778 | 0 | output["contracts"][sourceName][contractName]["evm"][kind] = bytecodeJSON; |
1779 | 0 | } |
1780 | 0 | } |
1781 | |
|
1782 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) |
1783 | 0 | output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); |
1784 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental)) |
1785 | 0 | output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly->assemblyString(stack.debugInfoSelection()); |
1786 | 0 | if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "yulCFGJson", wildcardMatchesExperimental)) |
1787 | 0 | output["contracts"][sourceName][contractName]["yulCFGJson"] = stack.cfgJson(); |
1788 | |
|
1789 | 0 | if (isEthdebugRequested(_inputsAndSettings.outputSelection)) |
1790 | 0 | output["ethdebug"] = evmasm::ethdebug::resources({sourceName}, VersionString); |
1791 | |
|
1792 | 0 | return output; |
1793 | 0 | } |
1794 | | |
1795 | | Json StandardCompiler::compile(Json const& _input) noexcept |
1796 | 0 | { |
1797 | 0 | YulStringRepository::reset(); |
1798 | |
|
1799 | 0 | try |
1800 | 0 | { |
1801 | 0 | auto parsed = parseInput(_input); |
1802 | 0 | if (std::holds_alternative<Json>(parsed)) |
1803 | 0 | return std::get<Json>(std::move(parsed)); |
1804 | 0 | InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed)); |
1805 | 0 | if (settings.language == "Solidity") |
1806 | 0 | return compileSolidity(std::move(settings)); |
1807 | 0 | else if (settings.language == "Yul") |
1808 | 0 | return compileYul(std::move(settings)); |
1809 | 0 | else if (settings.language == "SolidityAST") |
1810 | 0 | return compileSolidity(std::move(settings)); |
1811 | 0 | else if (settings.language == "EVMAssembly") |
1812 | 0 | return importEVMAssembly(std::move(settings)); |
1813 | 0 | else |
1814 | 0 | return formatFatalError(Error::Type::JSONError, "Only \"Solidity\", \"Yul\", \"SolidityAST\" or \"EVMAssembly\" is supported as a language."); |
1815 | 0 | } |
1816 | 0 | catch (UnimplementedFeatureError const& _exception) |
1817 | 0 | { |
1818 | 0 | solAssert(_exception.comment(), "Unimplemented feature errors must include a message for the user"); |
1819 | 0 | return formatFatalError(Error::Type::UnimplementedFeatureError, stringOrDefault(_exception.comment())); |
1820 | 0 | } |
1821 | 0 | catch (...) |
1822 | 0 | { |
1823 | 0 | return formatFatalError( |
1824 | 0 | Error::Type::InternalCompilerError, |
1825 | 0 | "Uncaught exception:\n" + boost::current_exception_diagnostic_information() |
1826 | 0 | ); |
1827 | 0 | } |
1828 | 0 | } |
1829 | | |
1830 | | std::string StandardCompiler::compile(std::string const& _input) noexcept |
1831 | 0 | { |
1832 | 0 | Json input; |
1833 | 0 | std::string errors; |
1834 | 0 | try |
1835 | 0 | { |
1836 | 0 | if (!util::jsonParseStrict(_input, input, &errors)) |
1837 | 0 | return util::jsonPrint(formatFatalError(Error::Type::JSONError, errors), m_jsonPrintingFormat); |
1838 | 0 | } |
1839 | 0 | catch (...) |
1840 | 0 | { |
1841 | 0 | if (errors.empty()) |
1842 | 0 | return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}"; |
1843 | 0 | else |
1844 | 0 | return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON: " + errors + "\"}]}"; |
1845 | 0 | } |
1846 | | |
1847 | | // std::cout << "Input: " << solidity::util::jsonPrettyPrint(input) << std::endl; |
1848 | 0 | Json output = compile(input); |
1849 | | // std::cout << "Output: " << solidity::util::jsonPrettyPrint(output) << std::endl; |
1850 | |
|
1851 | 0 | try |
1852 | 0 | { |
1853 | 0 | return util::jsonPrint(output, m_jsonPrintingFormat); |
1854 | 0 | } |
1855 | 0 | catch (...) |
1856 | 0 | { |
1857 | 0 | return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; |
1858 | 0 | } |
1859 | 0 | } |
1860 | | |
1861 | | Json StandardCompiler::formatFunctionDebugData( |
1862 | | std::map<std::string, evmasm::LinkerObject::FunctionDebugData> const& _debugInfo |
1863 | | ) |
1864 | 0 | { |
1865 | 0 | static_assert(std::is_same_v<Json::number_unsigned_t, uint64_t>); |
1866 | 0 | Json ret = Json::object(); |
1867 | 0 | for (auto const& [name, info]: _debugInfo) |
1868 | 0 | { |
1869 | 0 | Json fun = Json::object(); |
1870 | 0 | if (info.sourceID) |
1871 | 0 | fun["id"] = Json::number_unsigned_t(*info.sourceID); |
1872 | 0 | else |
1873 | 0 | fun["id"] = Json(); |
1874 | 0 | if (info.bytecodeOffset) |
1875 | 0 | fun["entryPoint"] = Json::number_unsigned_t(*info.bytecodeOffset); |
1876 | 0 | else |
1877 | 0 | fun["entryPoint"] = Json(); |
1878 | 0 | fun["parameterSlots"] = Json::number_unsigned_t(info.params); |
1879 | 0 | fun["returnSlots"] = Json::number_unsigned_t(info.returns); |
1880 | 0 | ret[name] = std::move(fun); |
1881 | 0 | } |
1882 | |
|
1883 | 0 | return ret; |
1884 | 0 | } |