mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5681 from ethereum/limitOutput
Do not compile unless requested.
This commit is contained in:
commit
ae08d7c375
@ -11,6 +11,7 @@ Compiler Features:
|
||||
* Code Generator: Use binary search for dispatch function if more efficient. The size/speed tradeoff can be tuned using ``--optimize-runs``.
|
||||
* Compiler Interface: Disallow unknown keys in standard JSON input.
|
||||
* SMTChecker: Support mathematical and cryptographic functions in an uninterpreted way.
|
||||
* Standard JSON interface: Only run code generation if it has been requested. This could lead to unsupported feature errors only being reported at the point where you request bytecode.
|
||||
* Static Analyzer: Do not warn about unused variables or state mutability for functions with an empty body.
|
||||
* Type Checker: Add an additional reason to be displayed when type conversion fails.
|
||||
* Yul: Support object access via ``datasize``, ``dataoffset`` and ``datacopy`` in standalone assembly mode.
|
||||
|
@ -389,18 +389,27 @@ string const CompilerStack::lastContractName() const
|
||||
|
||||
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr;
|
||||
}
|
||||
|
||||
eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
|
||||
}
|
||||
|
||||
string const* CompilerStack::sourceMapping(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& c = contract(_contractName);
|
||||
if (!c.sourceMapping)
|
||||
{
|
||||
@ -412,6 +421,9 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
|
||||
|
||||
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& c = contract(_contractName);
|
||||
if (!c.runtimeSourceMapping)
|
||||
{
|
||||
@ -447,17 +459,26 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
|
||||
|
||||
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
return contract(_contractName).object;
|
||||
}
|
||||
|
||||
eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
return contract(_contractName).runtimeObject;
|
||||
}
|
||||
|
||||
/// FIXME: cache this string
|
||||
string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
if (currentContract.compiler)
|
||||
return currentContract.compiler->assemblyString(_sourceCodes);
|
||||
@ -468,6 +489,9 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou
|
||||
/// FIXME: cache the JSON
|
||||
Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap _sourceCodes) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
Contract const& currentContract = contract(_contractName);
|
||||
if (currentContract.compiler)
|
||||
return currentContract.compiler->assemblyJSON(_sourceCodes);
|
||||
@ -494,13 +518,16 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
|
||||
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return contractABI(contract(_contractName));
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
|
||||
@ -513,13 +540,16 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return natspecUser(contract(_contractName));
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
|
||||
@ -532,13 +562,16 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return natspecDev(contract(_contractName));
|
||||
}
|
||||
|
||||
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
|
||||
@ -551,6 +584,9 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
||||
|
||||
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
Json::Value methodIdentifiers(Json::objectValue);
|
||||
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
|
||||
methodIdentifiers[it.second->externalSignature()] = it.first.hex();
|
||||
@ -583,8 +619,8 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
||||
|
||||
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return *contract(_contractName).contract;
|
||||
}
|
||||
@ -594,6 +630,9 @@ size_t CompilerStack::functionEntryPoint(
|
||||
FunctionDefinition const& _function
|
||||
) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
shared_ptr<Compiler> const& compiler = contract(_contractName).compiler;
|
||||
if (!compiler)
|
||||
return 0;
|
||||
@ -936,6 +975,9 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen
|
||||
|
||||
string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
string ret;
|
||||
map<string, unsigned> sourceIndicesMap = sourceIndices();
|
||||
int prevStart = -1;
|
||||
@ -1022,6 +1064,9 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
|
||||
|
||||
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState != CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
||||
|
||||
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
|
||||
return Json::Value();
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <libdevcore/JSON.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
@ -191,6 +192,31 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @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;
|
||||
|
||||
// This does not inculde "evm.methodIdentifiers" on purpose!
|
||||
static vector<string> const outputsThatRequireBinaries{
|
||||
"*",
|
||||
"metadata", // This is only generated at the end of compilation, but could be generated earlier.
|
||||
"evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes",
|
||||
"evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences",
|
||||
"evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap",
|
||||
"evm.bytecode.linkReferences",
|
||||
"evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
|
||||
};
|
||||
|
||||
for (auto const& fileRequests: _outputSelection)
|
||||
for (auto const& requests: fileRequests)
|
||||
for (auto const& output: outputsThatRequireBinaries)
|
||||
if (isArtifactRequested(requests, output))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
|
||||
{
|
||||
Json::Value ret(Json::objectValue);
|
||||
@ -564,9 +590,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
|
||||
m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection));
|
||||
|
||||
bool const binariesRequested = isBinaryRequested(outputSelection);
|
||||
|
||||
try
|
||||
{
|
||||
m_compilerStack.compile();
|
||||
if (binariesRequested)
|
||||
m_compilerStack.compile();
|
||||
else
|
||||
m_compilerStack.parseAndAnalyze();
|
||||
|
||||
for (auto const& error: m_compilerStack.errors())
|
||||
{
|
||||
@ -655,7 +686,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
|
||||
|
||||
/// Inconsistent state - stop here to receive error reports from users
|
||||
if (!compilationSuccess && errors.empty())
|
||||
if (((binariesRequested && !compilationSuccess) || !analysisSuccess) && errors.empty())
|
||||
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
|
||||
|
||||
Json::Value output = Json::objectValue;
|
||||
@ -681,7 +712,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
}
|
||||
|
||||
Json::Value contractsOutput = Json::objectValue;
|
||||
for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>())
|
||||
for (string const& contractName: analysisSuccess ? m_compilerStack.contractNames() : vector<string>())
|
||||
{
|
||||
size_t colon = contractName.rfind(':');
|
||||
solAssert(colon != string::npos, "");
|
||||
@ -692,7 +723,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (isArtifactRequested(outputSelection, file, name, "abi"))
|
||||
contractData["abi"] = m_compilerStack.contractABI(contractName);
|
||||
if (isArtifactRequested(outputSelection, file, name, "metadata"))
|
||||
if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "metadata"))
|
||||
contractData["metadata"] = m_compilerStack.metadata(contractName);
|
||||
if (isArtifactRequested(outputSelection, file, name, "userdoc"))
|
||||
contractData["userdoc"] = m_compilerStack.natspecUser(contractName);
|
||||
@ -702,16 +733,16 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
// EVM
|
||||
Json::Value evmData(Json::objectValue);
|
||||
// @TODO: add ir
|
||||
if (isArtifactRequested(outputSelection, file, name, "evm.assembly"))
|
||||
if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.assembly"))
|
||||
evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input));
|
||||
if (isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
|
||||
if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
|
||||
evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input));
|
||||
if (isArtifactRequested(outputSelection, file, name, "evm.methodIdentifiers"))
|
||||
evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName);
|
||||
if (isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
|
||||
if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
|
||||
evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
|
||||
|
||||
if (isArtifactRequested(
|
||||
if (compilationSuccess && isArtifactRequested(
|
||||
outputSelection,
|
||||
file,
|
||||
name,
|
||||
@ -722,7 +753,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
m_compilerStack.sourceMapping(contractName)
|
||||
);
|
||||
|
||||
if (isArtifactRequested(
|
||||
if (compilationSuccess && isArtifactRequested(
|
||||
outputSelection,
|
||||
file,
|
||||
name,
|
||||
@ -733,14 +764,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
m_compilerStack.runtimeSourceMapping(contractName)
|
||||
);
|
||||
|
||||
contractData["evm"] = evmData;
|
||||
if (!evmData.empty())
|
||||
contractData["evm"] = evmData;
|
||||
|
||||
if (!contractsOutput.isMember(file))
|
||||
contractsOutput[file] = Json::objectValue;
|
||||
|
||||
contractsOutput[file][name] = contractData;
|
||||
if (!contractData.empty())
|
||||
{
|
||||
if (!contractsOutput.isMember(file))
|
||||
contractsOutput[file] = Json::objectValue;
|
||||
contractsOutput[file][name] = contractData;
|
||||
}
|
||||
}
|
||||
output["contracts"] = contractsOutput;
|
||||
if (!contractsOutput.empty())
|
||||
output["contracts"] = contractsOutput;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ test_solc_behaviour() {
|
||||
|
||||
if [[ "$solc_args" == *"--standard-json"* ]]; then
|
||||
sed -i -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path"
|
||||
sed -i -e 's/,"errors":\[\]//' "$stdout_path"
|
||||
sed -i -e 's/"errors":\[\],\{0,1\}//' "$stdout_path"
|
||||
else
|
||||
sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
|
||||
sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path"
|
||||
|
@ -1 +1 @@
|
||||
{"contracts":{"A":{"C":{"evm":{}}}},"sources":{"A":{"id":0}}}
|
||||
{"sources":{"A":{"id":0}}}
|
||||
|
17
test/cmdlineTests/standard_binaries_requested.json
Normal file
17
test/cmdlineTests/standard_binaries_requested.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"*": { "*": ["evm.gasEstimates"] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"25600","executionCost":"75","totalCost":"25675"},"external":{"f()":"127"}}}}}},"sources":{"A":{"id":0}}}
|
17
test/cmdlineTests/standard_methodIdentifiersRequested.json
Normal file
17
test/cmdlineTests/standard_methodIdentifiersRequested.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"*": { "*": ["evm.methodIdentifiers"] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{"f()":"26121ff0"}}}}},"sources":{"A":{"id":0}}}
|
17
test/cmdlineTests/standard_only_ast_requested.json
Normal file
17
test/cmdlineTests/standard_only_ast_requested.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"*": { "": ["ast"] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"0:22:0"},{"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"61:2:0","statements":[]},"documentation":null,"id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"46:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"61:0:0"},"scope":6,"src":"36:27:0","stateMutability":"pure","superFunction":null,"visibility":"public"}],"scope":7,"src":"23:42:0"}],"src":"0:65:0"},"id":0}}}
|
@ -81,7 +81,9 @@ BOOST_AUTO_TEST_CASE(standard_compilation)
|
||||
|
||||
// Only tests some assumptions. The StandardCompiler is tested properly in another suite.
|
||||
BOOST_CHECK(result.isMember("sources"));
|
||||
BOOST_CHECK(result.isMember("contracts"));
|
||||
// This used to test that it is a member, but we did not actually request any output,
|
||||
// so there should not be a contract member.
|
||||
BOOST_CHECK(!result.isMember("contracts"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user