diff options
author | chriseth <chris@ethereum.org> | 2018-12-20 00:02:28 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-20 00:02:28 +0800 |
commit | ae08d7c375482cef29567e15468e8867380c9cc6 (patch) | |
tree | fd162c6117bece7ea548d8d9474f9e217fbb96a6 | |
parent | ef59f35a14c6eea65b15ac78db06befb182907c1 (diff) | |
parent | 900d100700d6b7ba347bae9779aa7f540387fee8 (diff) | |
download | dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar.gz dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar.bz2 dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar.lz dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar.xz dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.tar.zst dexon-solidity-ae08d7c375482cef29567e15468e8867380c9cc6.zip |
Merge pull request #5681 from ethereum/limitOutput
Do not compile unless requested.
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 55 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 65 | ||||
-rwxr-xr-x | test/cmdlineTests.sh | 2 | ||||
-rw-r--r-- | test/cmdlineTests/standard.json.stdout | 2 | ||||
-rw-r--r-- | test/cmdlineTests/standard_binaries_requested.json | 17 | ||||
-rw-r--r-- | test/cmdlineTests/standard_binaries_requested.json.stdout | 1 | ||||
-rw-r--r-- | test/cmdlineTests/standard_methodIdentifiersRequested.json | 17 | ||||
-rw-r--r-- | test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout | 1 | ||||
-rw-r--r-- | test/cmdlineTests/standard_only_ast_requested.json | 17 | ||||
-rw-r--r-- | test/cmdlineTests/standard_only_ast_requested.json.stdout | 1 | ||||
-rw-r--r-- | test/libsolidity/LibSolc.cpp | 4 |
12 files changed, 160 insertions, 23 deletions
diff --git a/Changelog.md b/Changelog.md index 60e3904c..428a0be0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index adfb94bd..f9d889e7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -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(); diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index e99b1324..137a4439 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -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; } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index bb71e012..9d2ffa5f 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -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" diff --git a/test/cmdlineTests/standard.json.stdout b/test/cmdlineTests/standard.json.stdout index 490e1f80..59b90c8c 100644 --- a/test/cmdlineTests/standard.json.stdout +++ b/test/cmdlineTests/standard.json.stdout @@ -1 +1 @@ -{"contracts":{"A":{"C":{"evm":{}}}},"sources":{"A":{"id":0}}} +{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_binaries_requested.json b/test/cmdlineTests/standard_binaries_requested.json new file mode 100644 index 00000000..65f19543 --- /dev/null +++ b/test/cmdlineTests/standard_binaries_requested.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["evm.gasEstimates"] } + } + } +} diff --git a/test/cmdlineTests/standard_binaries_requested.json.stdout b/test/cmdlineTests/standard_binaries_requested.json.stdout new file mode 100644 index 00000000..2baef22a --- /dev/null +++ b/test/cmdlineTests/standard_binaries_requested.json.stdout @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"25600","executionCost":"75","totalCost":"25675"},"external":{"f()":"127"}}}}}},"sources":{"A":{"id":0}}}
\ No newline at end of file diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json b/test/cmdlineTests/standard_methodIdentifiersRequested.json new file mode 100644 index 00000000..79a3c75d --- /dev/null +++ b/test/cmdlineTests/standard_methodIdentifiersRequested.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["evm.methodIdentifiers"] } + } + } +} diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout b/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout new file mode 100644 index 00000000..7e3f139f --- /dev/null +++ b/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{"f()":"26121ff0"}}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_only_ast_requested.json b/test/cmdlineTests/standard_only_ast_requested.json new file mode 100644 index 00000000..7abd6da5 --- /dev/null +++ b/test/cmdlineTests/standard_only_ast_requested.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "outputSelection": + { + "*": { "": ["ast"] } + } + } +} diff --git a/test/cmdlineTests/standard_only_ast_requested.json.stdout b/test/cmdlineTests/standard_only_ast_requested.json.stdout new file mode 100644 index 00000000..b884ab7d --- /dev/null +++ b/test/cmdlineTests/standard_only_ast_requested.json.stdout @@ -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}}} diff --git a/test/libsolidity/LibSolc.cpp b/test/libsolidity/LibSolc.cpp index 09c08700..ec97f22f 100644 --- a/test/libsolidity/LibSolc.cpp +++ b/test/libsolidity/LibSolc.cpp @@ -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() |