Coverage Report

Created: 2025-06-24 07:59

/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
}