Coverage Report

Created: 2026-10-26 06:53

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