diff options
Diffstat (limited to 'libsolidity/interface')
-rw-r--r-- | libsolidity/interface/ABI.cpp | 1 | ||||
-rw-r--r-- | libsolidity/interface/ABI.h | 4 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 82 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.h | 14 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 99 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 15 | ||||
-rw-r--r-- | libsolidity/interface/GasEstimator.cpp | 19 | ||||
-rw-r--r-- | libsolidity/interface/GasEstimator.h | 6 | ||||
-rw-r--r-- | libsolidity/interface/Natspec.cpp | 3 | ||||
-rw-r--r-- | libsolidity/interface/Natspec.h | 4 | ||||
-rw-r--r-- | libsolidity/interface/ReadFile.h | 4 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 225 | ||||
-rw-r--r-- | libsolidity/interface/Version.cpp | 5 | ||||
-rw-r--r-- | libsolidity/interface/Version.h | 2 |
14 files changed, 380 insertions, 103 deletions
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index aefb34af..0d27109e 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -19,6 +19,7 @@ */ #include <libsolidity/interface/ABI.h> + #include <libsolidity/ast/AST.h> using namespace std; diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index db70729d..082f3900 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -20,9 +20,9 @@ #pragma once -#include <string> -#include <memory> #include <json/json.h> +#include <memory> +#include <string> namespace dev { diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index f5eb7e41..69bceefc 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -22,18 +22,19 @@ #include <libsolidity/interface/AssemblyStack.h> +#include <libsolidity/codegen/AsmCodeGen.h> +#include <libevmasm/Assembly.h> #include <liblangutil/Scanner.h> -#include <libyul/AsmPrinter.h> -#include <libyul/AsmParser.h> + #include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysisInfo.h> -#include <libyul/AsmCodeGen.h> -#include <libyul/backends/evm/EVMCodeTransform.h> +#include <libyul/AsmParser.h> +#include <libyul/AsmPrinter.h> #include <libyul/backends/evm/EVMAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/EVMDialect.h> +#include <libyul/backends/evm/EVMObjectCompiler.h> #include <libyul/ObjectParser.h> - -#include <libevmasm/Assembly.h> - #include <libyul/optimiser/Suite.h> using namespace std; @@ -43,19 +44,19 @@ using namespace dev::solidity; namespace { -yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language) +shared_ptr<yul::Dialect> languageToDialect(AssemblyStack::Language _language) { switch (_language) { case AssemblyStack::Language::Assembly: - return yul::AsmFlavour::Loose; + return yul::EVMDialect::looseAssemblyForEVM(); case AssemblyStack::Language::StrictAssembly: - return yul::AsmFlavour::Strict; + return yul::EVMDialect::strictAssemblyForEVMObjects(); case AssemblyStack::Language::Yul: - return yul::AsmFlavour::Yul; + return yul::Dialect::yul(); } solAssert(false, ""); - return yul::AsmFlavour::Yul; + return yul::Dialect::yul(); } } @@ -72,7 +73,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName)); - m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); + m_parserResult = yul::ObjectParser(m_errorReporter, languageToDialect(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); @@ -84,21 +85,59 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string void AssemblyStack::optimize() { solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly."); - yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo); + solAssert(m_analysisSuccessful, "Analysis was not successful."); + m_analysisSuccessful = false; + optimize(*m_parserResult); solAssert(analyzeParsed(), "Invalid source code after optimization."); } bool AssemblyStack::analyzeParsed() { solAssert(m_parserResult, ""); - solAssert(m_parserResult->code, ""); - m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>(); - yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); - m_analysisSuccessful = analyzer.analyze(*m_parserResult->code); + m_analysisSuccessful = analyzeParsed(*m_parserResult); return m_analysisSuccessful; } -MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const +bool AssemblyStack::analyzeParsed(yul::Object& _object) +{ + solAssert(_object.code, ""); + _object.analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language)); + bool success = analyzer.analyze(*_object.code); + for (auto& subNode: _object.subObjects) + if (auto subObject = dynamic_cast<yul::Object*>(subNode.get())) + if (!analyzeParsed(*subObject)) + success = false; + return success; +} + +void AssemblyStack::compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const +{ + shared_ptr<yul::EVMDialect> dialect; + + if (m_language == Language::Assembly) + dialect = yul::EVMDialect::looseAssemblyForEVM(); + else if (m_language == AssemblyStack::Language::StrictAssembly) + dialect = yul::EVMDialect::strictAssemblyForEVMObjects(); + else if (m_language == AssemblyStack::Language::Yul) + dialect = yul::EVMDialect::yulForEVM(); + else + solAssert(false, "Invalid language."); + + yul::EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize); +} + +void AssemblyStack::optimize(yul::Object& _object) +{ + solAssert(_object.code, ""); + solAssert(_object.analysisInfo, ""); + for (auto& subNode: _object.subObjects) + if (auto subObject = dynamic_cast<yul::Object*>(subNode.get())) + optimize(*subObject); + yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo); +} + +MachineAssemblyObject AssemblyStack::assemble(Machine _machine, bool _optimize) const { solAssert(m_analysisSuccessful, ""); solAssert(m_parserResult, ""); @@ -111,7 +150,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; eth::Assembly assembly; - yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly); + EthAssemblyAdapter adapter(assembly); + compileEVM(adapter, false, _optimize); object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); object.assembly = assembly.assemblyString(); return object; @@ -120,7 +160,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; yul::EVMAssembly assembly(true); - yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code); + compileEVM(assembly, true, _optimize); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); /// TODO: fill out text representation return object; diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 0d04ffec..01db6b61 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -29,13 +29,17 @@ #include <libevmasm/LinkerObject.h> -#include <string> #include <memory> +#include <string> namespace langutil { class Scanner; } +namespace yul +{ +class AbstractAssembly; +} namespace dev { @@ -73,7 +77,8 @@ public: void optimize(); /// Run the assembly step (should only be called after parseAndAnalyze). - MachineAssemblyObject assemble(Machine _machine) const; + /// @param _optimize does not run the optimizer but performs optimized code generation. + MachineAssemblyObject assemble(Machine _machine, bool _optimize = false) const; /// @returns the errors generated during parsing, analysis (and potentially assembly). langutil::ErrorList const& errors() const { return m_errors; } @@ -83,6 +88,11 @@ public: private: bool analyzeParsed(); + bool analyzeParsed(yul::Object& _object); + + void compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const; + + void optimize(yul::Object& _object); Language m_language = Language::Assembly; EVMVersion m_evmVersion; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 610caea1..f9d889e7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -24,26 +24,27 @@ #include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/interface/Version.h> -#include <libsolidity/analysis/SemVerHandler.h> -#include <libsolidity/ast/AST.h> -#include <libsolidity/parsing/Parser.h> -#include <libsolidity/analysis/ContractLevelChecker.h> #include <libsolidity/analysis/ControlFlowAnalyzer.h> #include <libsolidity/analysis/ControlFlowGraph.h> +#include <libsolidity/analysis/ContractLevelChecker.h> +#include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/GlobalContext.h> #include <libsolidity/analysis/NameAndTypeResolver.h> -#include <libsolidity/analysis/TypeChecker.h> -#include <libsolidity/analysis/DocStringAnalyser.h> -#include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/PostTypeChecker.h> +#include <libsolidity/analysis/SemVerHandler.h> +#include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/SyntaxChecker.h> +#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/ViewPureChecker.h> + +#include <libsolidity/ast/AST.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/formal/SMTChecker.h> #include <libsolidity/interface/ABI.h> #include <libsolidity/interface/Natspec.h> #include <libsolidity/interface/GasEstimator.h> +#include <libsolidity/interface/Version.h> +#include <libsolidity/parsing/Parser.h> #include <libyul/YulString.h> @@ -388,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) { @@ -411,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) { @@ -446,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); @@ -467,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); @@ -493,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, ""); @@ -512,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, ""); @@ -531,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, ""); @@ -550,9 +584,12 @@ 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()] = toHex(it.first.ref()); + methodIdentifiers[it.second->externalSignature()] = it.first.hex(); return methodIdentifiers; } @@ -582,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; } @@ -593,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; @@ -618,6 +658,22 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); } + +h256 const& CompilerStack::Source::keccak256() const +{ + if (keccak256HashCached == h256{}) + keccak256HashCached = dev::keccak256(scanner->source()); + return keccak256HashCached; +} + +h256 const& CompilerStack::Source::swarmHash() const +{ + if (swarmHashCached == h256{}) + swarmHashCached = dev::swarmHash(scanner->source()); + return swarmHashCached; +} + + StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath) { solAssert(m_stackState < ParsingSuccessful, ""); @@ -859,16 +915,13 @@ string CompilerStack::createMetadata(Contract const& _contract) const continue; solAssert(s.second.scanner, "Scanner not available"); - meta["sources"][s.first]["keccak256"] = - "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes()); + meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes()); if (m_metadataLiteralSources) meta["sources"][s.first]["content"] = s.second.scanner->source(); else { meta["sources"][s.first]["urls"] = Json::arrayValue; - meta["sources"][s.first]["urls"].append( - "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()) - ); + meta["sources"][s.first]["urls"].append("bzzr://" + toHex(s.second.swarmHash().asBytes())); } } meta["settings"]["optimizer"]["enabled"] = m_optimize; @@ -895,7 +948,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const return jsonCompactPrint(meta); } -bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode) +bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode) { bytes cborEncodedHash = // CBOR-encoding of the key "bzzr0" @@ -922,6 +975,9 @@ bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode 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; @@ -1008,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/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2c7add3b..81d5009f 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -34,15 +34,14 @@ #include <libdevcore/Common.h> #include <libdevcore/FixedHash.h> -#include <json/json.h> - #include <boost/noncopyable.hpp> +#include <json/json.h> +#include <functional> +#include <memory> #include <ostream> #include <string> -#include <memory> #include <vector> -#include <functional> namespace langutil { @@ -262,7 +261,11 @@ private: std::shared_ptr<langutil::Scanner> scanner; std::shared_ptr<SourceUnit> ast; bool isLibrary = false; - void reset() { scanner.reset(); ast.reset(); } + h256 mutable keccak256HashCached; + h256 mutable swarmHashCached; + void reset() { *this = Source(); } + h256 const& keccak256() const; + h256 const& swarmHash() const; }; /// The state per contract. Filled gradually during compilation. @@ -316,7 +319,7 @@ private: std::string createMetadata(Contract const& _contract) const; /// @returns the metadata CBOR for the given serialised metadata JSON. - static bytes createCBORMetadata(std::string _metadata, bool _experimentalMode); + static bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode); /// @returns the computer source mapping string. std::string computeSourceMapping(eth::AssemblyItems const& _items) const; diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index de6b2ce5..8ffcf951 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -20,18 +20,21 @@ * Gas consumption estimator working alongside the AST. */ -#include "GasEstimator.h" -#include <map> -#include <functional> -#include <memory> -#include <libdevcore/Keccak256.h> -#include <libevmasm/ControlFlowGraph.h> -#include <libevmasm/KnownState.h> -#include <libevmasm/PathGasMeter.h> +#include <libsolidity/interface/GasEstimator.h> + #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/codegen/CompilerUtils.h> +#include <libevmasm/ControlFlowGraph.h> +#include <libevmasm/KnownState.h> +#include <libevmasm/PathGasMeter.h> +#include <libdevcore/Keccak256.h> + +#include <functional> +#include <map> +#include <memory> + using namespace std; using namespace dev; using namespace dev::eth; diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index 214a3e58..f40cffeb 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -24,12 +24,12 @@ #include <liblangutil/EVMVersion.h> -#include <libevmasm/GasMeter.h> #include <libevmasm/Assembly.h> +#include <libevmasm/GasMeter.h> -#include <vector> -#include <map> #include <array> +#include <map> +#include <vector> namespace dev { diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index 11dde349..7a89abae 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -24,8 +24,9 @@ */ #include <libsolidity/interface/Natspec.h> -#include <boost/range/irange.hpp> + #include <libsolidity/ast/AST.h> +#include <boost/range/irange.hpp> using namespace std; using namespace dev; diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index 0be4dda2..fbaa6d4d 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -25,9 +25,9 @@ #pragma once -#include <string> -#include <memory> #include <json/json.h> +#include <memory> +#include <string> namespace dev { diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h index 7068629d..3b3d747e 100644 --- a/libsolidity/interface/ReadFile.h +++ b/libsolidity/interface/ReadFile.h @@ -17,9 +17,9 @@ #pragma once -#include <string> -#include <functional> #include <boost/noncopyable.hpp> +#include <functional> +#include <string> namespace dev { diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 0eef50d2..137a4439 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -21,13 +21,17 @@ */ #include <libsolidity/interface/StandardCompiler.h> -#include <liblangutil/SourceReferenceFormatter.h> + #include <libsolidity/ast/ASTJsonConverter.h> +#include <liblangutil/SourceReferenceFormatter.h> #include <libevmasm/Instruction.h> #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> using namespace std; using namespace dev; @@ -69,12 +73,11 @@ Json::Value formatErrorWithException( bool const& _warning, string const& _type, string const& _component, - string const& _message, - function<Scanner const&(string const&)> const& _scannerFromSourceName + string const& _message ) { string message; - string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type, _scannerFromSourceName); + string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); // NOTE: the below is partially a copy from SourceReferenceFormatter SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); @@ -189,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); @@ -226,6 +254,99 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so return output; } +boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name) +{ + if (!!_input && !_input.isObject()) + return formatFatalError("JSONError", "\"" + _name + "\" must be an object"); + + for (auto const& member: _input.getMemberNames()) + if (!_keys.count(member)) + return formatFatalError("JSONError", "Unknown key \"" + member + "\""); + + return boost::none; +} + +boost::optional<Json::Value> checkRootKeys(Json::Value const& _input) +{ + static set<string> keys{"auxiliaryInput", "language", "settings", "sources"}; + return checkKeys(_input, keys, "root"); +} + +boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name) +{ + static set<string> keys{"content", "keccak256", "urls"}; + return checkKeys(_input, keys, "sources." + _name); +} + +boost::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input) +{ + static set<string> keys{"smtlib2responses"}; + return checkKeys(_input, keys, "auxiliaryInput"); +} + +boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input) +{ + static set<string> keys{"evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"}; + return checkKeys(_input, keys, "settings"); +} + +boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input) +{ + static set<string> keys{"enabled", "runs"}; + return checkKeys(_input, keys, "settings.optimizer"); +} + +boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input) +{ + static set<string> keys{"useLiteralContent"}; + return checkKeys(_input, keys, "settings.metadata"); +} + +boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection) +{ + 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" + ); + } + } + + return boost::none; +} + } Json::Value StandardCompiler::compileInternal(Json::Value const& _input) @@ -235,6 +356,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (!_input.isObject()) return formatFatalError("JSONError", "Input is not a JSON object."); + if (auto result = checkRootKeys(_input)) + return *result; + if (_input["language"] != "Solidity") return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language."); @@ -252,8 +376,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { string hash; - if (!sources[sourceName].isObject()) - return formatFatalError("JSONError", "Source input is not a JSON object."); + if (auto result = checkSourceKeys(sources[sourceName], sourceName)) + return *result; if (sources[sourceName]["keccak256"].isString()) hash = sources[sourceName]["keccak256"].asString(); @@ -320,10 +444,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) } Json::Value const& auxInputs = _input["auxiliaryInput"]; + + if (auto result = checkAuxiliaryInputKeys(auxInputs)) + return *result; + if (!!auxInputs) { Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"]; if (!!smtlib2Responses) + { + if (!smtlib2Responses.isObject()) + return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object."); + for (auto const& hashString: smtlib2Responses.getMemberNames()) { h256 hash; @@ -336,12 +468,22 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input."); } + if (!smtlib2Responses[hashString].isString()) + return formatFatalError( + "JSONError", + "\"smtlib2Responses." + hashString + "\" must be a string." + ); + m_compilerStack.addSMTLib2Response(hash, smtlib2Responses[hashString].asString()); } + } } Json::Value const& settings = _input.get("settings", Json::Value()); + if (auto result = checkSettingsKeys(settings)) + return *result; + if (settings.isMember("evmVersion")) { if (!settings["evmVersion"].isString()) @@ -352,11 +494,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setEVMVersion(*version); } + if (settings.isMember("remappings") && !settings["remappings"].isArray()) + return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings."); + vector<CompilerStack::Remapping> remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) { if (!remapping.isString()) - return formatFatalError("JSONError", "Remapping entry must be a string."); + return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings"); if (auto r = CompilerStack::parseRemapping(remapping.asString())) remappings.emplace_back(std::move(*r)); else @@ -367,6 +512,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (settings.isMember("optimizer")) { Json::Value optimizerSettings = settings["optimizer"]; + + if (auto result = checkOptimizerKeys(optimizerSettings)) + return *result; + if (optimizerSettings.isMember("enabled")) { if (!optimizerSettings["enabled"].isBool()) @@ -428,16 +577,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setLibraries(libraries); Json::Value metadataSettings = settings.get("metadata", Json::Value()); + + if (auto result = checkMetadataKeys(metadataSettings)) + return *result; + m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool()); Json::Value outputSelection = settings.get("outputSelection", Json::Value()); + + if (auto jsonError = checkOutputSelection(outputSelection)) + return *jsonError; + m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection)); - auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compilerStack.scanner(_sourceName); }; + 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()) { @@ -448,8 +608,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) err.type() == Error::Type::Warning, err.typeName(), "general", - "", - scannerFromSourceName + "" )); } } @@ -461,8 +620,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, _error.typeName(), "general", - "Uncaught error: ", - scannerFromSourceName + "Uncaught error: " )); } /// This should not be leaked from compile(). @@ -482,8 +640,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "CompilerError", "general", - "Compiler error (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Compiler error (" + _exception.lineInfo() + ")" )); } catch (InternalCompilerError const& _exception) @@ -493,8 +650,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "InternalCompilerError", "general", - "Internal compiler error (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Internal compiler error (" + _exception.lineInfo() + ")" )); } catch (UnimplementedFeatureError const& _exception) @@ -504,8 +660,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) false, "UnimplementedFeatureError", "general", - "Unimplemented feature (" + _exception.lineInfo() + ")", - scannerFromSourceName + "Unimplemented feature (" + _exception.lineInfo() + ")" )); } catch (Exception const& _exception) @@ -531,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; @@ -557,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, ""); @@ -568,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); @@ -578,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, @@ -598,7 +753,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.sourceMapping(contractName) ); - if (isArtifactRequested( + if (compilationSuccess && isArtifactRequested( outputSelection, file, name, @@ -609,14 +764,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.runtimeSourceMapping(contractName) ); - contractData["evm"] = evmData; - - if (!contractsOutput.isMember(file)) - contractsOutput[file] = Json::objectValue; + if (!evmData.empty()) + contractData["evm"] = evmData; - 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/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index b785d557..efd46d40 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -21,11 +21,12 @@ */ #include <libsolidity/interface/Version.h> -#include <string> + +#include <liblangutil/Exceptions.h> #include <libdevcore/CommonData.h> #include <libdevcore/Common.h> -#include <liblangutil/Exceptions.h> #include <solidity/BuildInfo.h> +#include <string> using namespace dev; using namespace dev::solidity; diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h index 24c3555d..38d63ec6 100644 --- a/libsolidity/interface/Version.h +++ b/libsolidity/interface/Version.h @@ -22,8 +22,8 @@ #pragma once -#include <string> #include <libdevcore/Common.h> +#include <string> namespace dev { |