Coverage Report

Created: 2022-08-24 06:31

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