2017-02-09 21:56:49 +00:00
|
|
|
/*
|
|
|
|
This file is part of solidity.
|
|
|
|
|
|
|
|
solidity is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
solidity is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2020-07-17 14:54:12 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2017-02-09 21:56:49 +00:00
|
|
|
/**
|
|
|
|
* @author Alex Beregszaszi
|
|
|
|
* @date 2016
|
|
|
|
* Standard JSON compiler interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <libsolidity/interface/StandardCompiler.h>
|
2018-12-17 18:24:42 +00:00
|
|
|
|
2017-03-29 13:59:00 +00:00
|
|
|
#include <libsolidity/ast/ASTJsonConverter.h>
|
2017-04-21 14:34:40 +00:00
|
|
|
#include <libyul/AssemblyStack.h>
|
2019-12-03 18:05:58 +00:00
|
|
|
#include <libyul/Exceptions.h>
|
2020-04-24 12:26:31 +00:00
|
|
|
#include <libyul/optimiser/Suite.h>
|
2018-12-17 18:24:42 +00:00
|
|
|
#include <liblangutil/SourceReferenceFormatter.h>
|
2017-02-10 13:18:21 +00:00
|
|
|
#include <libevmasm/Instruction.h>
|
2020-05-20 10:55:12 +00:00
|
|
|
#include <libsmtutil/Exceptions.h>
|
2020-01-06 10:52:23 +00:00
|
|
|
#include <libsolutil/JSON.h>
|
|
|
|
#include <libsolutil/Keccak256.h>
|
2020-05-12 14:33:05 +00:00
|
|
|
#include <libsolutil/CommonData.h>
|
2017-02-09 21:56:49 +00:00
|
|
|
|
2020-05-11 11:38:49 +00:00
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
2019-10-28 10:39:30 +00:00
|
|
|
|
2018-12-17 18:24:42 +00:00
|
|
|
#include <algorithm>
|
2019-10-28 10:39:30 +00:00
|
|
|
#include <optional>
|
2018-02-28 15:57:35 +00:00
|
|
|
|
2017-02-09 21:56:49 +00:00
|
|
|
using namespace std;
|
2019-12-11 16:31:36 +00:00
|
|
|
using namespace solidity;
|
|
|
|
using namespace solidity::yul;
|
|
|
|
using namespace solidity::frontend;
|
|
|
|
using namespace solidity::langutil;
|
2017-02-09 21:56:49 +00:00
|
|
|
|
2019-07-03 12:21:09 +00:00
|
|
|
namespace
|
|
|
|
{
|
2017-04-19 13:11:31 +00:00
|
|
|
|
2017-03-29 14:40:15 +00:00
|
|
|
Json::Value formatError(
|
|
|
|
bool _warning,
|
|
|
|
string const& _type,
|
|
|
|
string const& _component,
|
|
|
|
string const& _message,
|
|
|
|
string const& _formattedMessage = "",
|
2019-07-03 12:21:09 +00:00
|
|
|
Json::Value const& _sourceLocation = Json::Value(),
|
|
|
|
Json::Value const& _secondarySourceLocation = Json::Value()
|
2017-03-29 14:40:15 +00:00
|
|
|
)
|
2017-03-21 11:18:00 +00:00
|
|
|
{
|
|
|
|
Json::Value error = Json::objectValue;
|
|
|
|
error["type"] = _type;
|
2017-03-29 14:40:15 +00:00
|
|
|
error["component"] = _component;
|
|
|
|
error["severity"] = _warning ? "warning" : "error";
|
|
|
|
error["message"] = _message;
|
|
|
|
error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message;
|
|
|
|
if (_sourceLocation.isObject())
|
|
|
|
error["sourceLocation"] = _sourceLocation;
|
2019-07-03 12:21:09 +00:00
|
|
|
if (_secondarySourceLocation.isArray())
|
|
|
|
error["secondarySourceLocations"] = _secondarySourceLocation;
|
2017-03-29 14:40:15 +00:00
|
|
|
return error;
|
|
|
|
}
|
2017-03-21 11:18:00 +00:00
|
|
|
|
2017-03-29 14:40:15 +00:00
|
|
|
Json::Value formatFatalError(string const& _type, string const& _message)
|
|
|
|
{
|
2017-03-21 11:18:00 +00:00
|
|
|
Json::Value output = Json::objectValue;
|
|
|
|
output["errors"] = Json::arrayValue;
|
2017-03-29 14:40:15 +00:00
|
|
|
output["errors"].append(formatError(false, _type, "general", _message));
|
2017-03-21 11:18:00 +00:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2019-07-03 12:21:09 +00:00
|
|
|
Json::Value formatSourceLocation(SourceLocation const* location)
|
|
|
|
{
|
|
|
|
Json::Value sourceLocation;
|
|
|
|
if (location && location->source && !location->source->name().empty())
|
|
|
|
{
|
|
|
|
sourceLocation["file"] = location->source->name();
|
|
|
|
sourceLocation["start"] = location->start;
|
|
|
|
sourceLocation["end"] = location->end;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sourceLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _secondaryLocation)
|
|
|
|
{
|
|
|
|
if (!_secondaryLocation)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
Json::Value secondarySourceLocation = Json::arrayValue;
|
|
|
|
for (auto const& location: _secondaryLocation->infos)
|
|
|
|
{
|
|
|
|
Json::Value msg = formatSourceLocation(&location.second);
|
|
|
|
msg["message"] = location.first;
|
|
|
|
secondarySourceLocation.append(msg);
|
|
|
|
}
|
|
|
|
return secondarySourceLocation;
|
|
|
|
}
|
|
|
|
|
2017-03-29 20:50:53 +00:00
|
|
|
Json::Value formatErrorWithException(
|
2019-12-11 16:31:36 +00:00
|
|
|
util::Exception const& _exception,
|
2017-03-29 20:50:53 +00:00
|
|
|
bool const& _warning,
|
|
|
|
string const& _type,
|
|
|
|
string const& _component,
|
2020-05-29 23:42:36 +00:00
|
|
|
string const& _message,
|
|
|
|
optional<ErrorId> _errorId = nullopt
|
2017-03-29 20:50:53 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
string message;
|
2018-11-30 13:34:08 +00:00
|
|
|
string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type);
|
2017-03-29 20:50:53 +00:00
|
|
|
|
2019-12-11 16:31:36 +00:00
|
|
|
if (string const* description = boost::get_error_info<util::errinfo_comment>(_exception))
|
2017-03-29 20:50:53 +00:00
|
|
|
message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
|
|
|
|
else
|
|
|
|
message = _message;
|
|
|
|
|
2020-05-29 23:42:36 +00:00
|
|
|
Json::Value error = formatError(
|
2019-07-03 12:21:09 +00:00
|
|
|
_warning,
|
|
|
|
_type,
|
|
|
|
_component,
|
|
|
|
message,
|
|
|
|
formattedMessage,
|
|
|
|
formatSourceLocation(boost::get_error_info<errinfo_sourceLocation>(_exception)),
|
|
|
|
formatSecondarySourceLocation(boost::get_error_info<errinfo_secondarySourceLocation>(_exception))
|
|
|
|
);
|
2020-05-29 23:42:36 +00:00
|
|
|
|
|
|
|
if (_errorId)
|
|
|
|
error["errorCode"] = to_string(_errorId.value().error);
|
|
|
|
|
|
|
|
return error;
|
2017-03-29 20:50:53 +00:00
|
|
|
}
|
|
|
|
|
2019-06-28 14:14:31 +00:00
|
|
|
map<string, set<string>> requestedContractNames(Json::Value const& _outputSelection)
|
2017-09-29 18:05:39 +00:00
|
|
|
{
|
2019-06-28 14:14:31 +00:00
|
|
|
map<string, set<string>> contracts;
|
2017-09-29 18:05:39 +00:00
|
|
|
for (auto const& sourceName: _outputSelection.getMemberNames())
|
|
|
|
{
|
2019-06-28 14:14:31 +00:00
|
|
|
string key = (sourceName == "*") ? "" : sourceName;
|
2017-09-29 18:05:39 +00:00
|
|
|
for (auto const& contractName: _outputSelection[sourceName].getMemberNames())
|
|
|
|
{
|
2019-06-28 14:14:31 +00:00
|
|
|
string value = (contractName == "*") ? "" : contractName;
|
|
|
|
contracts[key].insert(value);
|
2017-09-29 18:05:39 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-28 14:14:31 +00:00
|
|
|
return contracts;
|
2017-09-29 18:05:39 +00:00
|
|
|
}
|
|
|
|
|
2017-04-22 13:43:10 +00:00
|
|
|
/// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content.
|
|
|
|
bool hashMatchesContent(string const& _hash, string const& _content)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2019-12-11 16:31:36 +00:00
|
|
|
return util::h256(_hash) == util::keccak256(_content);
|
2017-04-22 13:43:10 +00:00
|
|
|
}
|
2019-12-11 16:31:36 +00:00
|
|
|
catch (util::BadHexCharacter const&)
|
2017-04-22 13:43:10 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 16:11:04 +00:00
|
|
|
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental)
|
2017-03-30 10:16:28 +00:00
|
|
|
{
|
2019-06-18 16:11:04 +00:00
|
|
|
static set<string> experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"};
|
2017-11-22 12:55:44 +00:00
|
|
|
for (auto const& artifact: _outputSelection)
|
2017-03-30 10:16:28 +00:00
|
|
|
/// @TODO support sub-matching, e.g "evm" matches "evm.assembly"
|
2019-03-04 22:26:46 +00:00
|
|
|
if (artifact == _artifact)
|
2017-03-30 10:16:28 +00:00
|
|
|
return true;
|
2019-03-04 22:26:46 +00:00
|
|
|
else if (artifact == "*")
|
|
|
|
{
|
2019-06-18 16:11:04 +00:00
|
|
|
// "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated.
|
|
|
|
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
|
2019-03-04 22:26:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-03-30 10:16:28 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
2020-05-24 17:41:15 +00:00
|
|
|
/// @a _outputSelection is a JSON object containing a two-level hashmap, where the first level is the filename,
|
2017-11-22 12:55:44 +00:00
|
|
|
/// the second level is the contract name and the value is an array of artifact names to be requested for that contract.
|
2017-03-30 10:16:28 +00:00
|
|
|
/// @a _file is the current file
|
|
|
|
/// @a _contract is the current contract
|
2017-11-22 12:55:44 +00:00
|
|
|
/// @a _artifact is the current artifact name
|
2017-03-30 10:16:28 +00:00
|
|
|
///
|
2017-11-22 12:55:44 +00:00
|
|
|
/// @returns true if the @a _outputSelection has a match for the requested target in the specific file / contract.
|
2017-03-30 10:16:28 +00:00
|
|
|
///
|
2017-11-22 12:55:44 +00:00
|
|
|
/// In @a _outputSelection the use of '*' as a wildcard is permitted.
|
2017-03-30 10:16:28 +00:00
|
|
|
///
|
|
|
|
/// @TODO optimise this. Perhaps flatten the structure upfront.
|
|
|
|
///
|
2019-06-18 16:11:04 +00:00
|
|
|
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact, bool _wildcardMatchesExperimental)
|
2017-03-30 10:16:28 +00:00
|
|
|
{
|
2017-11-22 12:55:44 +00:00
|
|
|
if (!_outputSelection.isObject())
|
2017-03-30 10:16:28 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
for (auto const& file: { _file, string("*") })
|
2017-11-22 12:55:44 +00:00
|
|
|
if (_outputSelection.isMember(file) && _outputSelection[file].isObject())
|
2017-03-30 10:16:28 +00:00
|
|
|
{
|
2017-11-22 13:35:01 +00:00
|
|
|
/// For SourceUnit-level targets (such as AST) only allow empty name, otherwise
|
|
|
|
/// for Contract-level targets try both contract name and wildcard
|
|
|
|
vector<string> contracts{ _contract };
|
|
|
|
if (!_contract.empty())
|
2020-04-01 02:39:38 +00:00
|
|
|
contracts.emplace_back("*");
|
2017-11-22 13:35:01 +00:00
|
|
|
for (auto const& contract: contracts)
|
2017-03-30 10:16:28 +00:00
|
|
|
if (
|
2017-11-22 13:35:01 +00:00
|
|
|
_outputSelection[file].isMember(contract) &&
|
|
|
|
_outputSelection[file][contract].isArray() &&
|
2019-06-18 16:11:04 +00:00
|
|
|
isArtifactRequested(_outputSelection[file][contract], _artifact, _wildcardMatchesExperimental)
|
2017-03-30 10:16:28 +00:00
|
|
|
)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-18 16:11:04 +00:00
|
|
|
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector<string> const& _artifacts, bool _wildcardMatchesExperimental)
|
2017-10-19 13:09:40 +00:00
|
|
|
{
|
2017-11-22 12:55:44 +00:00
|
|
|
for (auto const& artifact: _artifacts)
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_outputSelection, _file, _contract, artifact, _wildcardMatchesExperimental))
|
2017-10-19 13:09:40 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-15 09:21:13 +00:00
|
|
|
/// @returns all artifact names of the EVM object, either for creation or deploy time.
|
|
|
|
vector<string> evmObjectComponents(string const& _objectKind)
|
|
|
|
{
|
|
|
|
solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", "");
|
|
|
|
vector<string> components{"", ".object", ".opcodes", ".sourceMap", ".generatedSources", ".linkReferences"};
|
|
|
|
if (_objectKind == "deployedBytecode")
|
|
|
|
components.push_back(".immutableReferences");
|
|
|
|
return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; });
|
|
|
|
}
|
|
|
|
|
2018-12-18 17:28:14 +00:00
|
|
|
/// @returns true if any binary was requested, i.e. we actually have to perform compilation.
|
|
|
|
bool isBinaryRequested(Json::Value const& _outputSelection)
|
|
|
|
{
|
|
|
|
if (!_outputSelection.isObject())
|
|
|
|
return false;
|
|
|
|
|
2020-05-24 17:41:15 +00:00
|
|
|
// This does not include "evm.methodIdentifiers" on purpose!
|
2020-09-15 09:21:13 +00:00
|
|
|
static vector<string> const outputsThatRequireBinaries = vector<string>{
|
2018-12-18 17:28:14 +00:00
|
|
|
"*",
|
2019-03-04 22:26:46 +00:00
|
|
|
"ir", "irOptimized",
|
2019-06-18 16:11:04 +00:00
|
|
|
"wast", "wasm", "ewasm.wast", "ewasm.wasm",
|
2018-12-18 17:28:14 +00:00
|
|
|
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
|
2020-09-15 09:21:13 +00:00
|
|
|
} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
|
2018-12-18 17:28:14 +00:00
|
|
|
|
|
|
|
for (auto const& fileRequests: _outputSelection)
|
|
|
|
for (auto const& requests: fileRequests)
|
|
|
|
for (auto const& output: outputsThatRequireBinaries)
|
2019-03-04 22:26:46 +00:00
|
|
|
if (isArtifactRequested(requests, output, false))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-06 09:43:36 +00:00
|
|
|
/// @returns true if EVM bytecode was requested, i.e. we have to run the old code generator.
|
|
|
|
bool isEvmBytecodeRequested(Json::Value const& _outputSelection)
|
|
|
|
{
|
|
|
|
if (!_outputSelection.isObject())
|
|
|
|
return false;
|
|
|
|
|
2020-09-15 09:21:13 +00:00
|
|
|
static vector<string> const outputsThatRequireEvmBinaries = vector<string>{
|
2020-08-06 09:43:36 +00:00
|
|
|
"*",
|
|
|
|
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
|
2020-09-15 09:21:13 +00:00
|
|
|
} + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode");
|
2020-08-06 09:43:36 +00:00
|
|
|
|
|
|
|
for (auto const& fileRequests: _outputSelection)
|
|
|
|
for (auto const& requests: fileRequests)
|
|
|
|
for (auto const& output: outputsThatRequireEvmBinaries)
|
|
|
|
if (isArtifactRequested(requests, output, false))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-09 16:36:12 +00:00
|
|
|
/// @returns true if any Ewasm code was requested. Note that as an exception, '*' does not
|
2019-06-18 16:11:04 +00:00
|
|
|
/// yet match "ewasm.wast" or "ewasm"
|
2019-12-09 16:36:12 +00:00
|
|
|
bool isEwasmRequested(Json::Value const& _outputSelection)
|
2019-06-18 16:11:04 +00:00
|
|
|
{
|
|
|
|
if (!_outputSelection.isObject())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (auto const& fileRequests: _outputSelection)
|
|
|
|
for (auto const& requests: fileRequests)
|
|
|
|
for (auto const& request: requests)
|
|
|
|
if (request == "ewasm" || request == "ewasm.wast")
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-04 22:26:46 +00:00
|
|
|
/// @returns true if any Yul IR was requested. Note that as an exception, '*' does not
|
|
|
|
/// yet match "ir" or "irOptimized"
|
|
|
|
bool isIRRequested(Json::Value const& _outputSelection)
|
|
|
|
{
|
2019-12-09 16:36:12 +00:00
|
|
|
if (isEwasmRequested(_outputSelection))
|
2019-06-18 16:11:04 +00:00
|
|
|
return true;
|
|
|
|
|
2019-03-04 22:26:46 +00:00
|
|
|
if (!_outputSelection.isObject())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (auto const& fileRequests: _outputSelection)
|
|
|
|
for (auto const& requests: fileRequests)
|
|
|
|
for (auto const& request: requests)
|
|
|
|
if (request == "ir" || request == "irOptimized")
|
2018-12-18 17:28:14 +00:00
|
|
|
return true;
|
2019-03-04 22:26:46 +00:00
|
|
|
|
2018-12-18 17:28:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-30 01:09:42 +00:00
|
|
|
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
|
|
|
|
{
|
|
|
|
Json::Value ret(Json::objectValue);
|
|
|
|
|
|
|
|
for (auto const& ref: linkReferences)
|
|
|
|
{
|
|
|
|
string const& fullname = ref.second;
|
2018-01-05 13:24:07 +00:00
|
|
|
size_t colon = fullname.rfind(':');
|
2017-03-30 01:09:42 +00:00
|
|
|
solAssert(colon != string::npos, "");
|
|
|
|
string file = fullname.substr(0, colon);
|
|
|
|
string name = fullname.substr(colon + 1);
|
|
|
|
|
|
|
|
Json::Value fileObject = ret.get(file, Json::objectValue);
|
|
|
|
Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
|
|
|
|
|
|
|
|
Json::Value entry = Json::objectValue;
|
|
|
|
entry["start"] = Json::UInt(ref.first);
|
|
|
|
entry["length"] = 20;
|
|
|
|
|
|
|
|
libraryArray.append(entry);
|
|
|
|
fileObject[name] = libraryArray;
|
|
|
|
ret[file] = fileObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-02 15:27:35 +00:00
|
|
|
Json::Value formatImmutableReferences(map<u256, pair<string, vector<size_t>>> const& _immutableReferences)
|
|
|
|
{
|
|
|
|
Json::Value ret(Json::objectValue);
|
|
|
|
|
|
|
|
for (auto const& immutableReference: _immutableReferences)
|
|
|
|
{
|
|
|
|
auto const& [identifier, byteOffsets] = immutableReference.second;
|
|
|
|
Json::Value array(Json::arrayValue);
|
|
|
|
for (size_t byteOffset: byteOffsets)
|
|
|
|
{
|
2020-04-06 09:21:53 +00:00
|
|
|
Json::Value byteRange(Json::objectValue);
|
|
|
|
byteRange["start"] = Json::UInt(byteOffset);
|
|
|
|
byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide
|
2020-04-02 15:27:35 +00:00
|
|
|
array.append(byteRange);
|
|
|
|
}
|
|
|
|
ret[identifier] = array;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-28 11:17:16 +00:00
|
|
|
Json::Value collectEVMObject(
|
|
|
|
evmasm::LinkerObject const& _object,
|
|
|
|
string const* _sourceMap,
|
|
|
|
Json::Value _generatedSources,
|
|
|
|
bool _runtimeObject
|
|
|
|
)
|
2017-04-19 14:52:54 +00:00
|
|
|
{
|
|
|
|
Json::Value output = Json::objectValue;
|
|
|
|
output["object"] = _object.toHex();
|
2019-12-11 16:31:36 +00:00
|
|
|
output["opcodes"] = evmasm::disassemble(_object.bytecode);
|
2017-04-19 14:52:54 +00:00
|
|
|
output["sourceMap"] = _sourceMap ? *_sourceMap : "";
|
|
|
|
output["linkReferences"] = formatLinkReferences(_object.linkReferences);
|
2020-04-02 15:27:35 +00:00
|
|
|
if (_runtimeObject)
|
|
|
|
output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences);
|
2020-05-28 11:17:16 +00:00
|
|
|
output["generatedSources"] = move(_generatedSources);
|
2017-04-19 14:52:54 +00:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
2018-12-12 14:49:10 +00:00
|
|
|
if (!!_input && !_input.isObject())
|
|
|
|
return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
|
|
|
|
|
2018-12-11 14:47:19 +00:00
|
|
|
for (auto const& member: _input.getMemberNames())
|
|
|
|
if (!_keys.count(member))
|
|
|
|
return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
|
2018-12-12 14:49:10 +00:00
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
return std::nullopt;
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkRootKeys(Json::Value const& _input)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
2020-10-19 09:37:23 +00:00
|
|
|
static set<string> keys{"auxiliaryInput", "language", "modelCheckerSettings", "settings", "sources"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "root");
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
|
|
|
static set<string> keys{"content", "keccak256", "urls"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "sources." + _name);
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
|
|
|
static set<string> keys{"smtlib2responses"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "auxiliaryInput");
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
2020-10-19 09:37:23 +00:00
|
|
|
static set<string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings", "stopAfter"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "settings");
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 09:37:23 +00:00
|
|
|
std::optional<Json::Value> checkModelCheckerSettingsKeys(Json::Value const& _input)
|
|
|
|
{
|
2020-11-02 20:20:20 +00:00
|
|
|
static set<string> keys{"engine", "timeout"};
|
2020-10-19 09:37:23 +00:00
|
|
|
return checkKeys(_input, keys, "modelCheckerSettings");
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
2019-02-21 16:39:47 +00:00
|
|
|
static set<string> keys{"details", "enabled", "runs"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "settings.optimizer");
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
|
2019-02-21 16:39:47 +00:00
|
|
|
{
|
|
|
|
static set<string> keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
|
|
|
|
return checkKeys(_input, keys, "settings.optimizer.details");
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
|
2019-02-21 16:39:47 +00:00
|
|
|
{
|
|
|
|
if (_details.isMember(_name))
|
|
|
|
{
|
|
|
|
if (!_details[_name].isBool())
|
|
|
|
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be Boolean");
|
|
|
|
_setting = _details[_name].asBool();
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-04-24 12:26:31 +00:00
|
|
|
std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _setting)
|
|
|
|
{
|
|
|
|
if (_details.isMember(_name))
|
|
|
|
{
|
|
|
|
if (_details[_name].isString())
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
yul::OptimiserSuite::validateSequence(_details[_name].asString());
|
|
|
|
}
|
|
|
|
catch (yul::OptimizerException const& _exception)
|
|
|
|
{
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"Invalid optimizer step sequence in \"settings.optimizer.details." + _name + "\": " + _exception.what()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_setting = _details[_name].asString();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be a string");
|
|
|
|
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
2018-12-11 14:47:19 +00:00
|
|
|
{
|
2019-09-06 17:11:07 +00:00
|
|
|
if (_input.isObject())
|
|
|
|
{
|
|
|
|
if (_input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
|
|
|
return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean");
|
|
|
|
|
|
|
|
static set<string> hashes{"ipfs", "bzzr1", "none"};
|
|
|
|
if (_input.isMember("bytecodeHash") && !hashes.count(_input["bytecodeHash"].asString()))
|
|
|
|
return formatFatalError("JSONError", "\"settings.metadata.bytecodeHash\" must be \"ipfs\", \"bzzr1\" or \"none\"");
|
|
|
|
}
|
|
|
|
static set<string> keys{"useLiteralContent", "bytecodeHash"};
|
2018-12-12 14:49:10 +00:00
|
|
|
return checkKeys(_input, keys, "settings.metadata");
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
|
2018-12-12 14:49:10 +00:00
|
|
|
{
|
|
|
|
if (!!_outputSelection && !_outputSelection.isObject())
|
|
|
|
return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
|
|
|
|
|
|
|
|
for (auto const& sourceName: _outputSelection.getMemberNames())
|
|
|
|
{
|
|
|
|
auto const& sourceVal = _outputSelection[sourceName];
|
|
|
|
|
|
|
|
if (!sourceVal.isObject())
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"\"settings.outputSelection." + sourceName + "\" must be an object"
|
|
|
|
);
|
|
|
|
|
|
|
|
for (auto const& contractName: sourceVal.getMemberNames())
|
|
|
|
{
|
|
|
|
auto const& contractVal = sourceVal[contractName];
|
|
|
|
|
|
|
|
if (!contractVal.isArray())
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"\"settings.outputSelection." +
|
|
|
|
sourceName +
|
|
|
|
"." +
|
|
|
|
contractName +
|
|
|
|
"\" must be a string array"
|
|
|
|
);
|
|
|
|
|
|
|
|
for (auto const& output: contractVal)
|
|
|
|
if (!output.isString())
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"\"settings.outputSelection." +
|
|
|
|
sourceName +
|
|
|
|
"." +
|
|
|
|
contractName +
|
|
|
|
"\" must be a string array"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-28 10:39:30 +00:00
|
|
|
return std::nullopt;
|
2018-12-11 14:47:19 +00:00
|
|
|
}
|
2020-10-14 15:44:40 +00:00
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
/// Validates the optimizer settings and returns them in a parsed object.
|
|
|
|
/// On error returns the json-formatted error message.
|
2020-05-11 11:38:49 +00:00
|
|
|
std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
2019-02-21 16:39:47 +00:00
|
|
|
{
|
|
|
|
if (auto result = checkOptimizerKeys(_jsonInput))
|
|
|
|
return *result;
|
|
|
|
|
|
|
|
OptimiserSettings settings = OptimiserSettings::none();
|
|
|
|
|
|
|
|
if (_jsonInput.isMember("enabled"))
|
|
|
|
{
|
|
|
|
if (!_jsonInput["enabled"].isBool())
|
|
|
|
return formatFatalError("JSONError", "The \"enabled\" setting must be a Boolean.");
|
|
|
|
|
2019-03-20 15:15:07 +00:00
|
|
|
settings = _jsonInput["enabled"].asBool() ? OptimiserSettings::standard() : OptimiserSettings::minimal();
|
2019-02-21 16:39:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_jsonInput.isMember("runs"))
|
|
|
|
{
|
|
|
|
if (!_jsonInput["runs"].isUInt())
|
|
|
|
return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
|
|
|
|
settings.expectedExecutionsPerDeployment = _jsonInput["runs"].asUInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_jsonInput.isMember("details"))
|
|
|
|
{
|
|
|
|
Json::Value const& details = _jsonInput["details"];
|
|
|
|
if (auto result = checkOptimizerDetailsKeys(details))
|
|
|
|
return *result;
|
|
|
|
|
|
|
|
if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser))
|
|
|
|
return *error;
|
|
|
|
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
|
|
|
|
return *error;
|
2019-11-26 15:41:58 +00:00
|
|
|
settings.optimizeStackAllocation = settings.runYulOptimiser;
|
2019-02-21 16:39:47 +00:00
|
|
|
if (details.isMember("yulDetails"))
|
|
|
|
{
|
2019-02-26 18:55:13 +00:00
|
|
|
if (!settings.runYulOptimiser)
|
|
|
|
return formatFatalError("JSONError", "\"Providing yulDetails requires Yul optimizer to be enabled.");
|
|
|
|
|
2020-04-24 12:26:31 +00:00
|
|
|
if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails"))
|
2019-02-26 18:55:13 +00:00
|
|
|
return *result;
|
|
|
|
if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation))
|
|
|
|
return *error;
|
2020-04-24 12:26:31 +00:00
|
|
|
if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps))
|
|
|
|
return *error;
|
2019-02-21 16:39:47 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-14 14:44:28 +00:00
|
|
|
return { std::move(settings) };
|
2019-02-21 16:39:47 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
}
|
2019-02-21 16:39:47 +00:00
|
|
|
|
2020-05-11 11:38:49 +00:00
|
|
|
std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
2017-02-10 13:18:21 +00:00
|
|
|
{
|
2019-03-07 14:35:31 +00:00
|
|
|
InputsAndSettings ret;
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2017-04-19 14:53:11 +00:00
|
|
|
if (!_input.isObject())
|
|
|
|
return formatFatalError("JSONError", "Input is not a JSON object.");
|
|
|
|
|
2018-12-11 14:47:19 +00:00
|
|
|
if (auto result = checkRootKeys(_input))
|
|
|
|
return *result;
|
|
|
|
|
2017-04-21 14:34:40 +00:00
|
|
|
ret.language = _input["language"].asString();
|
2017-04-19 14:53:11 +00:00
|
|
|
|
2017-02-10 13:18:21 +00:00
|
|
|
Json::Value const& sources = _input["sources"];
|
2018-02-13 20:40:16 +00:00
|
|
|
|
|
|
|
if (!sources.isObject() && !sources.isNull())
|
|
|
|
return formatFatalError("JSONError", "\"sources\" is not a JSON object.");
|
|
|
|
|
|
|
|
if (sources.empty())
|
2017-03-29 14:19:10 +00:00
|
|
|
return formatFatalError("JSONError", "No input sources specified.");
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.errors = Json::arrayValue;
|
2017-04-19 15:45:36 +00:00
|
|
|
|
2017-02-10 13:18:21 +00:00
|
|
|
for (auto const& sourceName: sources.getMemberNames())
|
2017-04-22 13:43:10 +00:00
|
|
|
{
|
|
|
|
string hash;
|
2017-04-24 21:38:03 +00:00
|
|
|
|
2018-12-12 14:49:10 +00:00
|
|
|
if (auto result = checkSourceKeys(sources[sourceName], sourceName))
|
2018-12-11 14:47:19 +00:00
|
|
|
return *result;
|
|
|
|
|
2017-04-22 13:43:10 +00:00
|
|
|
if (sources[sourceName]["keccak256"].isString())
|
|
|
|
hash = sources[sourceName]["keccak256"].asString();
|
|
|
|
|
2017-03-30 02:17:17 +00:00
|
|
|
if (sources[sourceName]["content"].isString())
|
2017-04-22 13:43:10 +00:00
|
|
|
{
|
|
|
|
string content = sources[sourceName]["content"].asString();
|
|
|
|
if (!hash.empty() && !hashMatchesContent(hash, content))
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.errors.append(formatError(
|
2017-04-22 13:43:10 +00:00
|
|
|
false,
|
|
|
|
"IOError",
|
|
|
|
"general",
|
|
|
|
"Mismatch between content and supplied hash for \"" + sourceName + "\""
|
|
|
|
));
|
|
|
|
else
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.sources[sourceName] = content;
|
2017-04-22 13:43:10 +00:00
|
|
|
}
|
2017-03-30 02:17:17 +00:00
|
|
|
else if (sources[sourceName]["urls"].isArray())
|
2017-04-19 15:45:36 +00:00
|
|
|
{
|
|
|
|
if (!m_readFile)
|
|
|
|
return formatFatalError("JSONError", "No import callback supplied, but URL is requested.");
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
vector<string> failures;
|
|
|
|
|
|
|
|
for (auto const& url: sources[sourceName]["urls"])
|
|
|
|
{
|
2018-09-21 13:51:45 +00:00
|
|
|
if (!url.isString())
|
|
|
|
return formatFatalError("JSONError", "URL must be a string.");
|
2019-09-17 14:06:43 +00:00
|
|
|
ReadCallback::Result result = m_readFile(ReadCallback::kindString(ReadCallback::Kind::ReadFile), url.asString());
|
2017-04-19 15:45:36 +00:00
|
|
|
if (result.success)
|
|
|
|
{
|
2017-07-13 19:06:04 +00:00
|
|
|
if (!hash.empty() && !hashMatchesContent(hash, result.responseOrErrorMessage))
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.errors.append(formatError(
|
2017-04-22 13:43:10 +00:00
|
|
|
false,
|
|
|
|
"IOError",
|
|
|
|
"general",
|
|
|
|
"Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\""
|
|
|
|
));
|
|
|
|
else
|
|
|
|
{
|
2020-04-01 03:04:29 +00:00
|
|
|
ret.sources[sourceName] = result.responseOrErrorMessage;
|
2017-04-22 13:43:10 +00:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2017-04-19 15:45:36 +00:00
|
|
|
}
|
|
|
|
else
|
2017-07-13 19:06:04 +00:00
|
|
|
failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.responseOrErrorMessage);
|
2017-04-19 15:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto const& failure: failures)
|
|
|
|
{
|
|
|
|
/// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors.
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.errors.append(formatError(
|
2017-04-19 15:45:36 +00:00
|
|
|
found ? true : false,
|
|
|
|
"IOError",
|
|
|
|
"general",
|
|
|
|
failure
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2017-03-30 02:17:17 +00:00
|
|
|
else
|
|
|
|
return formatFatalError("JSONError", "Invalid input source specified.");
|
2017-04-22 13:43:10 +00:00
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2017-10-13 13:19:53 +00:00
|
|
|
Json::Value const& auxInputs = _input["auxiliaryInput"];
|
2018-12-11 14:47:19 +00:00
|
|
|
|
|
|
|
if (auto result = checkAuxiliaryInputKeys(auxInputs))
|
|
|
|
return *result;
|
|
|
|
|
2017-10-13 13:19:53 +00:00
|
|
|
if (!!auxInputs)
|
|
|
|
{
|
2018-08-03 14:48:20 +00:00
|
|
|
Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"];
|
2017-10-13 13:19:53 +00:00
|
|
|
if (!!smtlib2Responses)
|
2018-12-12 14:49:10 +00:00
|
|
|
{
|
|
|
|
if (!smtlib2Responses.isObject())
|
|
|
|
return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.");
|
|
|
|
|
2017-10-13 13:19:53 +00:00
|
|
|
for (auto const& hashString: smtlib2Responses.getMemberNames())
|
|
|
|
{
|
2019-12-11 16:31:36 +00:00
|
|
|
util::h256 hash;
|
2017-10-13 13:19:53 +00:00
|
|
|
try
|
|
|
|
{
|
2019-12-11 16:31:36 +00:00
|
|
|
hash = util::h256(hashString);
|
2017-10-13 13:19:53 +00:00
|
|
|
}
|
2019-12-11 16:31:36 +00:00
|
|
|
catch (util::BadHexCharacter const&)
|
2017-10-13 13:19:53 +00:00
|
|
|
{
|
|
|
|
return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input.");
|
|
|
|
}
|
|
|
|
|
2018-12-12 14:49:10 +00:00
|
|
|
if (!smtlib2Responses[hashString].isString())
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"\"smtlib2Responses." + hashString + "\" must be a string."
|
|
|
|
);
|
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.smtLib2Responses[hash] = smtlib2Responses[hashString].asString();
|
2017-10-13 13:19:53 +00:00
|
|
|
}
|
2018-12-12 14:49:10 +00:00
|
|
|
}
|
2017-10-13 13:19:53 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 12:29:57 +00:00
|
|
|
Json::Value const& settings = _input.get("settings", Json::Value());
|
|
|
|
|
2018-12-11 14:47:19 +00:00
|
|
|
if (auto result = checkSettingsKeys(settings))
|
|
|
|
return *result;
|
|
|
|
|
2020-07-08 20:08:50 +00:00
|
|
|
if (settings.isMember("stopAfter"))
|
|
|
|
{
|
|
|
|
if (!settings["stopAfter"].isString())
|
|
|
|
return formatFatalError("JSONError", "\"settings.stopAfter\" must be a string.");
|
|
|
|
|
|
|
|
if (settings["stopAfter"].asString() != "parsing")
|
|
|
|
return formatFatalError("JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\".");
|
|
|
|
|
|
|
|
ret.stopAfter = CompilerStack::State::Parsed;
|
|
|
|
}
|
|
|
|
|
2019-05-30 14:35:42 +00:00
|
|
|
if (settings.isMember("parserErrorRecovery"))
|
|
|
|
{
|
|
|
|
if (!settings["parserErrorRecovery"].isBool())
|
|
|
|
return formatFatalError("JSONError", "\"settings.parserErrorRecovery\" must be a Boolean.");
|
|
|
|
ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool();
|
|
|
|
}
|
|
|
|
|
2018-02-21 22:43:40 +00:00
|
|
|
if (settings.isMember("evmVersion"))
|
|
|
|
{
|
2018-09-21 13:51:45 +00:00
|
|
|
if (!settings["evmVersion"].isString())
|
|
|
|
return formatFatalError("JSONError", "evmVersion must be a string.");
|
2019-10-28 10:39:30 +00:00
|
|
|
std::optional<langutil::EVMVersion> version = langutil::EVMVersion::fromString(settings["evmVersion"].asString());
|
2018-02-21 22:43:40 +00:00
|
|
|
if (!version)
|
|
|
|
return formatFatalError("JSONError", "Invalid EVM version requested.");
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.evmVersion = *version;
|
2018-02-21 22:43:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 14:44:36 +00:00
|
|
|
if (settings.isMember("debug"))
|
|
|
|
{
|
|
|
|
if (auto result = checkKeys(settings["debug"], {"revertStrings"}, "settings.debug"))
|
|
|
|
return *result;
|
|
|
|
|
|
|
|
if (settings["debug"].isMember("revertStrings"))
|
|
|
|
{
|
|
|
|
if (!settings["debug"]["revertStrings"].isString())
|
|
|
|
return formatFatalError("JSONError", "settings.debug.revertStrings must be a string.");
|
|
|
|
std::optional<RevertStrings> revertStrings = revertStringsFromString(settings["debug"]["revertStrings"].asString());
|
|
|
|
if (!revertStrings)
|
|
|
|
return formatFatalError("JSONError", "Invalid value for settings.debug.revertStrings.");
|
2020-01-22 14:48:56 +00:00
|
|
|
if (*revertStrings == RevertStrings::VerboseDebug)
|
2019-09-18 14:44:36 +00:00
|
|
|
return formatFatalError(
|
|
|
|
"UnimplementedFeatureError",
|
2020-01-22 14:48:56 +00:00
|
|
|
"Only \"default\", \"strip\" and \"debug\" are implemented for settings.debug.revertStrings for now."
|
2019-09-18 14:44:36 +00:00
|
|
|
);
|
|
|
|
ret.revertStrings = *revertStrings;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-12 14:49:10 +00:00
|
|
|
if (settings.isMember("remappings") && !settings["remappings"].isArray())
|
|
|
|
return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings.");
|
|
|
|
|
2017-03-17 12:30:50 +00:00
|
|
|
for (auto const& remapping: settings.get("remappings", Json::Value()))
|
2018-08-09 18:37:49 +00:00
|
|
|
{
|
2018-09-21 13:51:45 +00:00
|
|
|
if (!remapping.isString())
|
2018-12-12 14:49:10 +00:00
|
|
|
return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings");
|
2018-08-09 18:37:49 +00:00
|
|
|
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.remappings.emplace_back(std::move(*r));
|
2018-08-09 18:37:49 +00:00
|
|
|
else
|
|
|
|
return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\"");
|
|
|
|
}
|
2017-03-17 12:30:50 +00:00
|
|
|
|
2018-09-24 10:48:25 +00:00
|
|
|
if (settings.isMember("optimizer"))
|
2019-03-07 14:35:31 +00:00
|
|
|
{
|
|
|
|
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
|
2020-05-11 11:38:49 +00:00
|
|
|
if (std::holds_alternative<Json::Value>(optimiserSettings))
|
|
|
|
return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
2019-03-07 14:35:31 +00:00
|
|
|
else
|
2020-05-11 11:38:49 +00:00
|
|
|
ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
|
2019-03-07 14:35:31 +00:00
|
|
|
}
|
2018-12-11 14:47:19 +00:00
|
|
|
|
2018-02-28 09:36:07 +00:00
|
|
|
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
|
|
|
if (!jsonLibraries.isObject())
|
|
|
|
return formatFatalError("JSONError", "\"libraries\" is not a JSON object.");
|
2017-03-17 12:30:29 +00:00
|
|
|
for (auto const& sourceName: jsonLibraries.getMemberNames())
|
|
|
|
{
|
|
|
|
auto const& jsonSourceName = jsonLibraries[sourceName];
|
2018-02-28 09:36:07 +00:00
|
|
|
if (!jsonSourceName.isObject())
|
2018-09-21 13:51:45 +00:00
|
|
|
return formatFatalError("JSONError", "Library entry is not a JSON object.");
|
2017-03-17 12:30:29 +00:00
|
|
|
for (auto const& library: jsonSourceName.getMemberNames())
|
2018-02-28 09:44:48 +00:00
|
|
|
{
|
2018-09-21 13:51:45 +00:00
|
|
|
if (!jsonSourceName[library].isString())
|
|
|
|
return formatFatalError("JSONError", "Library address must be a string.");
|
2018-02-28 15:57:35 +00:00
|
|
|
string address = jsonSourceName[library].asString();
|
|
|
|
|
|
|
|
if (!boost::starts_with(address, "0x"))
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"Library address is not prefixed with \"0x\"."
|
|
|
|
);
|
|
|
|
|
|
|
|
if (address.length() != 42)
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"Library address is of invalid length."
|
|
|
|
);
|
|
|
|
|
2018-02-28 09:44:48 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// @TODO use libraries only for the given source
|
2019-12-11 16:31:36 +00:00
|
|
|
ret.libraries[library] = util::h160(address);
|
2018-02-28 09:44:48 +00:00
|
|
|
}
|
2019-12-11 16:31:36 +00:00
|
|
|
catch (util::BadHexCharacter const&)
|
2018-02-28 09:44:48 +00:00
|
|
|
{
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
2018-02-28 15:57:35 +00:00
|
|
|
"Invalid library address (\"" + address + "\") supplied."
|
2018-02-28 09:44:48 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2017-03-17 12:30:29 +00:00
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2017-03-30 01:41:38 +00:00
|
|
|
Json::Value metadataSettings = settings.get("metadata", Json::Value());
|
2018-12-11 14:47:19 +00:00
|
|
|
|
|
|
|
if (auto result = checkMetadataKeys(metadataSettings))
|
|
|
|
return *result;
|
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.metadataLiteralSources = metadataSettings.get("useLiteralContent", Json::Value(false)).asBool();
|
2019-09-06 17:11:07 +00:00
|
|
|
if (metadataSettings.isMember("bytecodeHash"))
|
|
|
|
{
|
|
|
|
auto metadataHash = metadataSettings["bytecodeHash"].asString();
|
|
|
|
ret.metadataHash =
|
|
|
|
metadataHash == "ipfs" ?
|
|
|
|
CompilerStack::MetadataHash::IPFS :
|
|
|
|
metadataHash == "bzzr1" ?
|
|
|
|
CompilerStack::MetadataHash::Bzzr1 :
|
|
|
|
CompilerStack::MetadataHash::None;
|
|
|
|
}
|
2017-03-30 01:41:38 +00:00
|
|
|
|
2017-09-29 18:05:39 +00:00
|
|
|
Json::Value outputSelection = settings.get("outputSelection", Json::Value());
|
2018-12-12 14:49:10 +00:00
|
|
|
|
|
|
|
if (auto jsonError = checkOutputSelection(outputSelection))
|
|
|
|
return *jsonError;
|
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
ret.outputSelection = std::move(outputSelection);
|
2017-09-29 18:05:39 +00:00
|
|
|
|
2020-07-08 20:08:50 +00:00
|
|
|
if (ret.stopAfter != CompilerStack::State::CompilationSuccessful && isBinaryRequested(ret.outputSelection))
|
|
|
|
return formatFatalError(
|
|
|
|
"JSONError",
|
|
|
|
"Requested output selection conflicts with \"settings.stopAfter\"."
|
|
|
|
);
|
|
|
|
|
2020-10-19 09:37:23 +00:00
|
|
|
Json::Value const& modelCheckerSettings = _input.get("modelCheckerSettings", Json::Value());
|
|
|
|
|
|
|
|
if (auto result = checkModelCheckerSettingsKeys(modelCheckerSettings))
|
|
|
|
return *result;
|
|
|
|
|
|
|
|
if (modelCheckerSettings.isMember("engine"))
|
2020-10-14 15:44:40 +00:00
|
|
|
{
|
2020-10-19 09:37:23 +00:00
|
|
|
if (!modelCheckerSettings["engine"].isString())
|
|
|
|
return formatFatalError("JSONError", "modelCheckerSettings.engine must be a string.");
|
|
|
|
std::optional<ModelCheckerEngine> engine = ModelCheckerEngine::fromString(modelCheckerSettings["engine"].asString());
|
2020-10-14 15:44:40 +00:00
|
|
|
if (!engine)
|
|
|
|
return formatFatalError("JSONError", "Invalid model checker engine requested.");
|
2020-11-02 20:20:20 +00:00
|
|
|
ret.modelCheckerSettings.engine = *engine;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modelCheckerSettings.isMember("timeout"))
|
|
|
|
{
|
|
|
|
if (!modelCheckerSettings["timeout"].isUInt())
|
|
|
|
return formatFatalError("JSONError", "modelCheckerSettings.timeout must be an unsigned integer.");
|
|
|
|
ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].asUInt();
|
2020-10-14 15:44:40 +00:00
|
|
|
}
|
|
|
|
|
2019-05-14 14:44:28 +00:00
|
|
|
return { std::move(ret) };
|
2019-03-07 14:35:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inputsAndSettings)
|
|
|
|
{
|
|
|
|
CompilerStack compilerStack(m_readFile);
|
|
|
|
|
|
|
|
StringMap sourceList = std::move(_inputsAndSettings.sources);
|
2019-03-20 17:57:46 +00:00
|
|
|
compilerStack.setSources(sourceList);
|
2019-03-07 14:35:31 +00:00
|
|
|
for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses)
|
|
|
|
compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second);
|
|
|
|
compilerStack.setEVMVersion(_inputsAndSettings.evmVersion);
|
2019-05-30 14:35:42 +00:00
|
|
|
compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery);
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.setRemappings(_inputsAndSettings.remappings);
|
|
|
|
compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
|
2019-09-18 14:44:36 +00:00
|
|
|
compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.setLibraries(_inputsAndSettings.libraries);
|
|
|
|
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
|
2019-09-06 17:11:07 +00:00
|
|
|
compilerStack.setMetadataHash(_inputsAndSettings.metadataHash);
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection));
|
2020-11-02 20:20:20 +00:00
|
|
|
compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings);
|
2019-03-07 14:35:31 +00:00
|
|
|
|
2020-08-06 09:43:36 +00:00
|
|
|
compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection));
|
2019-06-18 16:11:04 +00:00
|
|
|
compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection));
|
2019-12-09 16:36:12 +00:00
|
|
|
compilerStack.enableEwasmGeneration(isEwasmRequested(_inputsAndSettings.outputSelection));
|
2019-03-04 22:26:46 +00:00
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
Json::Value errors = std::move(_inputsAndSettings.errors);
|
|
|
|
|
|
|
|
bool const binariesRequested = isBinaryRequested(_inputsAndSettings.outputSelection);
|
2018-12-18 17:28:14 +00:00
|
|
|
|
2017-02-10 13:18:21 +00:00
|
|
|
try
|
|
|
|
{
|
2018-12-18 17:28:14 +00:00
|
|
|
if (binariesRequested)
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.compile();
|
2018-12-18 17:28:14 +00:00
|
|
|
else
|
2020-07-08 20:08:50 +00:00
|
|
|
compilerStack.parseAndAnalyze(_inputsAndSettings.stopAfter);
|
2017-03-29 20:50:53 +00:00
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
for (auto const& error: compilerStack.errors())
|
2017-03-29 20:50:53 +00:00
|
|
|
{
|
2017-07-05 15:02:02 +00:00
|
|
|
Error const& err = dynamic_cast<Error const&>(*error);
|
2017-03-29 20:50:53 +00:00
|
|
|
|
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
*error,
|
2017-07-05 15:02:02 +00:00
|
|
|
err.type() == Error::Type::Warning,
|
|
|
|
err.typeName(),
|
2017-03-29 20:50:53 +00:00
|
|
|
"general",
|
2020-05-29 23:42:36 +00:00
|
|
|
"",
|
|
|
|
err.errorId()
|
2017-03-29 20:50:53 +00:00
|
|
|
));
|
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2017-07-26 21:52:11 +00:00
|
|
|
/// This is only thrown in a very few locations.
|
2017-02-10 13:18:21 +00:00
|
|
|
catch (Error const& _error)
|
|
|
|
{
|
2017-07-26 21:53:54 +00:00
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_error,
|
|
|
|
false,
|
|
|
|
_error.typeName(),
|
|
|
|
"general",
|
2018-11-30 13:34:08 +00:00
|
|
|
"Uncaught error: "
|
2017-07-26 21:53:54 +00:00
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2017-07-26 21:52:11 +00:00
|
|
|
/// This should not be leaked from compile().
|
|
|
|
catch (FatalError const& _exception)
|
|
|
|
{
|
|
|
|
errors.append(formatError(
|
|
|
|
false,
|
|
|
|
"FatalError",
|
|
|
|
"general",
|
|
|
|
"Uncaught fatal error: " + boost::diagnostic_information(_exception)
|
|
|
|
));
|
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
catch (CompilerError const& _exception)
|
|
|
|
{
|
2017-03-29 20:50:53 +00:00
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_exception,
|
|
|
|
false,
|
|
|
|
"CompilerError",
|
|
|
|
"general",
|
2018-11-30 13:34:08 +00:00
|
|
|
"Compiler error (" + _exception.lineInfo() + ")"
|
2017-03-29 20:50:53 +00:00
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
|
|
|
catch (InternalCompilerError const& _exception)
|
|
|
|
{
|
2017-03-29 20:50:53 +00:00
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_exception,
|
|
|
|
false,
|
|
|
|
"InternalCompilerError",
|
|
|
|
"general",
|
2018-11-30 13:34:08 +00:00
|
|
|
"Internal compiler error (" + _exception.lineInfo() + ")"
|
2017-03-29 20:50:53 +00:00
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
|
|
|
catch (UnimplementedFeatureError const& _exception)
|
|
|
|
{
|
2017-03-29 20:50:53 +00:00
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_exception,
|
|
|
|
false,
|
|
|
|
"UnimplementedFeatureError",
|
|
|
|
"general",
|
2018-11-30 13:34:08 +00:00
|
|
|
"Unimplemented feature (" + _exception.lineInfo() + ")"
|
2017-07-26 21:52:11 +00:00
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2019-12-03 18:05:58 +00:00
|
|
|
catch (yul::YulException const& _exception)
|
|
|
|
{
|
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_exception,
|
|
|
|
false,
|
|
|
|
"YulException",
|
|
|
|
"general",
|
|
|
|
"Yul exception"
|
|
|
|
));
|
|
|
|
}
|
2020-05-20 10:55:12 +00:00
|
|
|
catch (smtutil::SMTLogicError const& _exception)
|
|
|
|
{
|
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
_exception,
|
|
|
|
false,
|
|
|
|
"SMTLogicException",
|
|
|
|
"general",
|
|
|
|
"SMT logic exception"
|
|
|
|
));
|
|
|
|
}
|
2019-12-11 16:31:36 +00:00
|
|
|
catch (util::Exception const& _exception)
|
2017-02-10 13:18:21 +00:00
|
|
|
{
|
2017-03-29 20:50:53 +00:00
|
|
|
errors.append(formatError(
|
|
|
|
false,
|
|
|
|
"Exception",
|
|
|
|
"general",
|
|
|
|
"Exception during compilation: " + boost::diagnostic_information(_exception)
|
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2019-05-29 20:26:18 +00:00
|
|
|
catch (std::exception const& _e)
|
|
|
|
{
|
|
|
|
errors.append(formatError(
|
|
|
|
false,
|
|
|
|
"Exception",
|
|
|
|
"general",
|
|
|
|
"Unknown exception during compilation" + (_e.what() ? ": " + string(_e.what()) : ".")
|
|
|
|
));
|
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
catch (...)
|
|
|
|
{
|
2017-03-29 20:50:53 +00:00
|
|
|
errors.append(formatError(
|
|
|
|
false,
|
|
|
|
"Exception",
|
|
|
|
"general",
|
|
|
|
"Unknown exception during compilation."
|
|
|
|
));
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 15:24:54 +00:00
|
|
|
bool analysisPerformed = compilerStack.state() >= CompilerStack::State::AnalysisPerformed;
|
2019-03-07 14:35:31 +00:00
|
|
|
bool const compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful;
|
2017-07-03 11:02:06 +00:00
|
|
|
|
2019-05-28 15:24:54 +00:00
|
|
|
if (compilerStack.hasError() && !_inputsAndSettings.parserErrorRecovery)
|
|
|
|
analysisPerformed = false;
|
|
|
|
|
2017-03-29 20:50:53 +00:00
|
|
|
/// Inconsistent state - stop here to receive error reports from users
|
2020-07-08 20:08:50 +00:00
|
|
|
if (
|
|
|
|
((binariesRequested && !compilationSuccess) || !analysisPerformed) &&
|
|
|
|
(errors.empty() && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed)
|
|
|
|
)
|
2017-03-29 20:50:53 +00:00
|
|
|
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
|
|
|
|
|
2017-07-18 13:43:10 +00:00
|
|
|
Json::Value output = Json::objectValue;
|
|
|
|
|
|
|
|
if (errors.size() > 0)
|
2019-03-07 14:35:31 +00:00
|
|
|
output["errors"] = std::move(errors);
|
2017-07-18 13:43:10 +00:00
|
|
|
|
2019-03-07 14:35:31 +00:00
|
|
|
if (!compilerStack.unhandledSMTLib2Queries().empty())
|
|
|
|
for (string const& query: compilerStack.unhandledSMTLib2Queries())
|
2019-12-11 16:31:36 +00:00
|
|
|
output["auxiliaryInputRequested"]["smtlib2queries"]["0x" + util::keccak256(query).hex()] = query;
|
2017-10-13 13:19:53 +00:00
|
|
|
|
2019-06-18 16:11:04 +00:00
|
|
|
bool const wildcardMatchesExperimental = false;
|
2019-03-04 22:26:46 +00:00
|
|
|
|
2017-03-29 13:54:34 +00:00
|
|
|
output["sources"] = Json::objectValue;
|
|
|
|
unsigned sourceIndex = 0;
|
2020-07-08 20:08:50 +00:00
|
|
|
if (compilerStack.state() >= CompilerStack::State::Parsed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery))
|
|
|
|
for (string const& sourceName: compilerStack.sourceNames())
|
|
|
|
{
|
|
|
|
Json::Value sourceResult = Json::objectValue;
|
|
|
|
sourceResult["id"] = sourceIndex++;
|
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental))
|
|
|
|
sourceResult["ast"] = ASTJsonConverter(false, compilerStack.state(), compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST", wildcardMatchesExperimental))
|
|
|
|
sourceResult["legacyAST"] = ASTJsonConverter(true, compilerStack.state(), compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName));
|
|
|
|
output["sources"][sourceName] = sourceResult;
|
|
|
|
}
|
2017-03-29 13:54:34 +00:00
|
|
|
|
2017-02-10 13:18:21 +00:00
|
|
|
Json::Value contractsOutput = Json::objectValue;
|
2019-05-28 15:24:54 +00:00
|
|
|
for (string const& contractName: analysisPerformed ? compilerStack.contractNames() : vector<string>())
|
2017-02-10 13:18:21 +00:00
|
|
|
{
|
2018-01-03 11:34:48 +00:00
|
|
|
size_t colon = contractName.rfind(':');
|
2017-03-30 01:32:43 +00:00
|
|
|
solAssert(colon != string::npos, "");
|
|
|
|
string file = contractName.substr(0, colon);
|
|
|
|
string name = contractName.substr(colon + 1);
|
|
|
|
|
2019-10-30 17:11:33 +00:00
|
|
|
// ABI, storage layout, documentation and metadata
|
2017-02-10 13:18:21 +00:00
|
|
|
Json::Value contractData(Json::objectValue);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
contractData["abi"] = compilerStack.contractABI(contractName);
|
2019-10-30 17:11:33 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
|
|
|
|
contractData["storageLayout"] = compilerStack.storageLayout(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
contractData["metadata"] = compilerStack.metadata(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
contractData["userdoc"] = compilerStack.natspecUser(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "devdoc", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
contractData["devdoc"] = compilerStack.natspecDev(contractName);
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2019-03-04 22:26:46 +00:00
|
|
|
// IR
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesExperimental))
|
2019-03-04 22:26:46 +00:00
|
|
|
contractData["ir"] = compilerStack.yulIR(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental))
|
2019-03-04 22:26:46 +00:00
|
|
|
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
|
|
|
|
|
2019-12-09 16:36:12 +00:00
|
|
|
// Ewasm
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wast", wildcardMatchesExperimental))
|
2019-12-09 16:36:12 +00:00
|
|
|
contractData["ewasm"]["wast"] = compilerStack.ewasm(contractName);
|
2019-10-31 17:43:54 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ewasm.wasm", wildcardMatchesExperimental))
|
2019-12-09 16:36:12 +00:00
|
|
|
contractData["ewasm"]["wasm"] = compilerStack.ewasmObject(contractName).toHex();
|
2019-06-18 16:11:04 +00:00
|
|
|
|
2017-02-10 13:18:21 +00:00
|
|
|
// EVM
|
|
|
|
Json::Value evmData(Json::objectValue);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental))
|
2020-02-18 09:22:34 +00:00
|
|
|
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
|
2019-06-18 16:11:04 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental))
|
2019-03-07 14:35:31 +00:00
|
|
|
evmData["gasEstimates"] = compilerStack.gasEstimates(contractName);
|
2017-04-12 09:37:04 +00:00
|
|
|
|
2018-12-18 17:28:14 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(
|
2019-03-07 14:35:31 +00:00
|
|
|
_inputsAndSettings.outputSelection,
|
2017-10-19 13:09:40 +00:00
|
|
|
file,
|
|
|
|
name,
|
2020-09-15 09:21:13 +00:00
|
|
|
evmObjectComponents("bytecode"),
|
2019-06-18 16:11:04 +00:00
|
|
|
wildcardMatchesExperimental
|
2017-10-19 13:09:40 +00:00
|
|
|
))
|
2017-04-12 09:37:04 +00:00
|
|
|
evmData["bytecode"] = collectEVMObject(
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.object(contractName),
|
2020-04-02 15:27:35 +00:00
|
|
|
compilerStack.sourceMapping(contractName),
|
2020-05-28 11:17:16 +00:00
|
|
|
compilerStack.generatedSources(contractName),
|
2020-04-02 15:27:35 +00:00
|
|
|
false
|
2017-04-12 09:37:04 +00:00
|
|
|
);
|
|
|
|
|
2018-12-18 17:28:14 +00:00
|
|
|
if (compilationSuccess && isArtifactRequested(
|
2019-03-07 14:35:31 +00:00
|
|
|
_inputsAndSettings.outputSelection,
|
2017-10-19 13:09:40 +00:00
|
|
|
file,
|
|
|
|
name,
|
2020-09-15 09:21:13 +00:00
|
|
|
evmObjectComponents("deployedBytecode"),
|
2019-06-18 16:11:04 +00:00
|
|
|
wildcardMatchesExperimental
|
2017-10-19 13:09:40 +00:00
|
|
|
))
|
2017-04-12 09:37:04 +00:00
|
|
|
evmData["deployedBytecode"] = collectEVMObject(
|
2019-03-07 14:35:31 +00:00
|
|
|
compilerStack.runtimeObject(contractName),
|
2020-04-02 15:27:35 +00:00
|
|
|
compilerStack.runtimeSourceMapping(contractName),
|
2020-05-28 11:17:16 +00:00
|
|
|
compilerStack.generatedSources(contractName, true),
|
2020-04-02 15:27:35 +00:00
|
|
|
true
|
2017-04-12 09:37:04 +00:00
|
|
|
);
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2018-12-19 11:22:19 +00:00
|
|
|
if (!evmData.empty())
|
|
|
|
contractData["evm"] = evmData;
|
2017-02-10 13:18:21 +00:00
|
|
|
|
2018-12-19 11:22:19 +00:00
|
|
|
if (!contractData.empty())
|
|
|
|
{
|
|
|
|
if (!contractsOutput.isMember(file))
|
|
|
|
contractsOutput[file] = Json::objectValue;
|
|
|
|
contractsOutput[file][name] = contractData;
|
|
|
|
}
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2018-12-19 11:22:19 +00:00
|
|
|
if (!contractsOutput.empty())
|
|
|
|
output["contracts"] = contractsOutput;
|
2017-02-10 13:18:21 +00:00
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2017-04-21 14:34:40 +00:00
|
|
|
|
|
|
|
Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
|
|
|
{
|
|
|
|
if (_inputsAndSettings.sources.size() != 1)
|
|
|
|
return formatFatalError("JSONError", "Yul mode only supports exactly one input file.");
|
|
|
|
if (!_inputsAndSettings.smtLib2Responses.empty())
|
|
|
|
return formatFatalError("JSONError", "Yul mode does not support smtlib2responses.");
|
|
|
|
if (!_inputsAndSettings.remappings.empty())
|
|
|
|
return formatFatalError("JSONError", "Field \"settings.remappings\" cannot be used for Yul.");
|
2019-09-18 14:44:36 +00:00
|
|
|
if (_inputsAndSettings.revertStrings != RevertStrings::Default)
|
|
|
|
return formatFatalError("JSONError", "Field \"settings.debug.revertStrings\" cannot be used for Yul.");
|
2017-04-21 14:34:40 +00:00
|
|
|
|
|
|
|
Json::Value output = Json::objectValue;
|
|
|
|
|
2019-03-27 11:49:50 +00:00
|
|
|
AssemblyStack stack(
|
|
|
|
_inputsAndSettings.evmVersion,
|
|
|
|
AssemblyStack::Language::StrictAssembly,
|
|
|
|
_inputsAndSettings.optimiserSettings
|
|
|
|
);
|
2017-04-21 14:34:40 +00:00
|
|
|
string const& sourceName = _inputsAndSettings.sources.begin()->first;
|
|
|
|
string const& sourceContents = _inputsAndSettings.sources.begin()->second;
|
2019-03-25 13:09:18 +00:00
|
|
|
|
|
|
|
// Inconsistent state - stop here to receive error reports from users
|
|
|
|
if (!stack.parseAndAnalyze(sourceName, sourceContents) && stack.errors().empty())
|
|
|
|
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
|
|
|
|
|
|
|
|
if (!stack.errors().empty())
|
2017-04-21 14:34:40 +00:00
|
|
|
{
|
|
|
|
Json::Value errors = Json::arrayValue;
|
|
|
|
for (auto const& error: stack.errors())
|
|
|
|
{
|
|
|
|
auto err = dynamic_pointer_cast<Error const>(error);
|
|
|
|
|
|
|
|
errors.append(formatErrorWithException(
|
|
|
|
*error,
|
|
|
|
err->type() == Error::Type::Warning,
|
|
|
|
err->typeName(),
|
|
|
|
"general",
|
|
|
|
""
|
|
|
|
));
|
|
|
|
}
|
|
|
|
output["errors"] = errors;
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2019-03-25 13:41:37 +00:00
|
|
|
// TODO: move this warning to AssemblyStack
|
|
|
|
output["errors"] = Json::arrayValue;
|
|
|
|
output["errors"].append(formatError(true, "Warning", "general", "Yul is still experimental. Please use the output with care."));
|
|
|
|
|
2017-04-21 14:34:40 +00:00
|
|
|
string contractName = stack.parserResult()->name.str();
|
|
|
|
|
2019-06-18 16:11:04 +00:00
|
|
|
bool const wildcardMatchesExperimental = true;
|
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
|
2017-04-21 14:34:40 +00:00
|
|
|
output["contracts"][sourceName][contractName]["ir"] = stack.print();
|
|
|
|
|
2019-03-27 11:49:50 +00:00
|
|
|
stack.optimize();
|
2017-04-21 14:34:40 +00:00
|
|
|
|
2020-05-12 14:33:05 +00:00
|
|
|
MachineAssemblyObject object;
|
|
|
|
MachineAssemblyObject runtimeObject;
|
|
|
|
tie(object, runtimeObject) = stack.assembleAndGuessRuntime();
|
2017-04-21 14:34:40 +00:00
|
|
|
|
2020-11-05 13:40:51 +00:00
|
|
|
if (object.bytecode)
|
|
|
|
object.bytecode->link(_inputsAndSettings.libraries);
|
|
|
|
if (runtimeObject.bytecode)
|
|
|
|
runtimeObject.bytecode->link(_inputsAndSettings.libraries);
|
|
|
|
|
2020-05-12 14:33:05 +00:00
|
|
|
for (string const& objectKind: vector<string>{"bytecode", "deployedBytecode"})
|
|
|
|
if (isArtifactRequested(
|
|
|
|
_inputsAndSettings.outputSelection,
|
|
|
|
sourceName,
|
|
|
|
contractName,
|
2020-09-15 09:21:13 +00:00
|
|
|
evmObjectComponents(objectKind),
|
2020-05-12 14:33:05 +00:00
|
|
|
wildcardMatchesExperimental
|
|
|
|
))
|
|
|
|
{
|
|
|
|
MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject;
|
|
|
|
if (o.bytecode)
|
2020-05-28 11:17:16 +00:00
|
|
|
output["contracts"][sourceName][contractName]["evm"][objectKind] =
|
|
|
|
collectEVMObject(*o.bytecode, o.sourceMappings.get(), Json::arrayValue, false);
|
2020-05-12 14:33:05 +00:00
|
|
|
}
|
2017-04-21 14:34:40 +00:00
|
|
|
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))
|
2017-04-21 14:34:40 +00:00
|
|
|
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
|
2019-06-18 16:11:04 +00:00
|
|
|
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental))
|
2017-04-21 14:34:40 +00:00
|
|
|
output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly;
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-08 12:22:33 +00:00
|
|
|
Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
|
2017-03-17 12:08:12 +00:00
|
|
|
{
|
2019-04-24 12:03:09 +00:00
|
|
|
YulStringRepository::reset();
|
|
|
|
|
2017-03-17 12:08:12 +00:00
|
|
|
try
|
|
|
|
{
|
2019-03-07 14:35:31 +00:00
|
|
|
auto parsed = parseInput(_input);
|
2020-05-11 11:38:49 +00:00
|
|
|
if (std::holds_alternative<Json::Value>(parsed))
|
|
|
|
return std::get<Json::Value>(std::move(parsed));
|
|
|
|
InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
|
2017-04-21 14:34:40 +00:00
|
|
|
if (settings.language == "Solidity")
|
|
|
|
return compileSolidity(std::move(settings));
|
|
|
|
else if (settings.language == "Yul")
|
|
|
|
return compileYul(std::move(settings));
|
|
|
|
else
|
|
|
|
return formatFatalError("JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language.");
|
2017-03-17 12:08:12 +00:00
|
|
|
}
|
2017-04-24 21:49:50 +00:00
|
|
|
catch (Json::LogicError const& _exception)
|
|
|
|
{
|
|
|
|
return formatFatalError("InternalCompilerError", string("JSON logic exception: ") + _exception.what());
|
|
|
|
}
|
|
|
|
catch (Json::RuntimeError const& _exception)
|
|
|
|
{
|
|
|
|
return formatFatalError("InternalCompilerError", string("JSON runtime exception: ") + _exception.what());
|
|
|
|
}
|
2019-12-11 16:31:36 +00:00
|
|
|
catch (util::Exception const& _exception)
|
2017-04-23 18:43:06 +00:00
|
|
|
{
|
2019-03-07 14:35:31 +00:00
|
|
|
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile: " + boost::diagnostic_information(_exception));
|
2017-04-23 18:43:06 +00:00
|
|
|
}
|
2017-03-17 12:08:12 +00:00
|
|
|
catch (...)
|
|
|
|
{
|
2019-03-07 14:35:31 +00:00
|
|
|
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile");
|
2017-03-17 12:08:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-08 12:22:33 +00:00
|
|
|
string StandardCompiler::compile(string const& _input) noexcept
|
2017-02-09 21:56:49 +00:00
|
|
|
{
|
2017-02-10 13:18:21 +00:00
|
|
|
Json::Value input;
|
2018-02-07 01:05:20 +00:00
|
|
|
string errors;
|
2017-03-29 12:34:16 +00:00
|
|
|
try
|
|
|
|
{
|
2019-12-11 16:31:36 +00:00
|
|
|
if (!util::jsonParseStrict(_input, input, &errors))
|
|
|
|
return util::jsonCompactPrint(formatFatalError("JSONError", errors));
|
2017-03-29 12:34:16 +00:00
|
|
|
}
|
2018-05-17 12:57:21 +00:00
|
|
|
catch (...)
|
2017-02-10 13:18:21 +00:00
|
|
|
{
|
2018-09-21 21:43:57 +00:00
|
|
|
return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// cout << "Input: " << input.toStyledString() << endl;
|
|
|
|
Json::Value output = compile(input);
|
|
|
|
// cout << "Output: " << output.toStyledString() << endl;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2019-12-11 16:31:36 +00:00
|
|
|
return util::jsonCompactPrint(output);
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2018-05-17 12:57:21 +00:00
|
|
|
catch (...)
|
2017-02-10 13:18:21 +00:00
|
|
|
{
|
2018-09-21 21:43:57 +00:00
|
|
|
return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
|
2017-02-10 13:18:21 +00:00
|
|
|
}
|
2017-02-09 21:56:49 +00:00
|
|
|
}
|