From 13571f4a6797f838ea4ca5ffb2b4fe737ac529bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 14 Sep 2021 14:09:18 +0200 Subject: [PATCH 1/3] CommandLineInterface: Add asserts documenting which function is expected to work in which input modes --- solc/CommandLineInterface.cpp | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index de361f0c6..3399a86fb 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -154,6 +154,8 @@ static bool coloredOutput(CommandLineOptions const& _options) void CommandLineInterface::handleBinary(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (m_options.compiler.outputs.binary) { if (!m_options.output.dir.empty()) @@ -178,6 +180,8 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); else @@ -190,6 +194,8 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleIR(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.ir) return; @@ -204,6 +210,8 @@ void CommandLineInterface::handleIR(string const& _contractName) void CommandLineInterface::handleIROptimized(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.irOptimized) return; @@ -218,6 +226,8 @@ void CommandLineInterface::handleIROptimized(string const& _contractName) void CommandLineInterface::handleEwasm(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.ewasm) return; @@ -239,6 +249,8 @@ void CommandLineInterface::handleEwasm(string const& _contractName) void CommandLineInterface::handleBytecode(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (m_options.compiler.outputs.opcodes) handleOpcode(_contract); if (m_options.compiler.outputs.binary || m_options.compiler.outputs.binaryRuntime) @@ -247,6 +259,8 @@ void CommandLineInterface::handleBytecode(string const& _contract) void CommandLineInterface::handleSignatureHashes(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.signatureHashes) return; @@ -263,6 +277,8 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) void CommandLineInterface::handleMetadata(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.metadata) return; @@ -275,6 +291,8 @@ void CommandLineInterface::handleMetadata(string const& _contract) void CommandLineInterface::handleABI(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.abi) return; @@ -287,6 +305,8 @@ void CommandLineInterface::handleABI(string const& _contract) void CommandLineInterface::handleStorageLayout(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.storageLayout) return; @@ -299,6 +319,8 @@ void CommandLineInterface::handleStorageLayout(string const& _contract) void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + bool enabled = false; std::string suffix; std::string title; @@ -339,6 +361,8 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra void CommandLineInterface::handleGasEstimation(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + Json::Value estimates = m_compiler->gasEstimates(_contract); sout() << "Gas estimation:" << endl; @@ -465,6 +489,8 @@ bool CommandLineInterface::readInputFiles() map CommandLineInterface::parseAstFromInput() { + solAssert(m_options.input.mode == InputMode::CompilerWithASTImport, ""); + map sourceJsons; map tmpSources; @@ -711,6 +737,8 @@ bool CommandLineInterface::compile() void CommandLineInterface::handleCombinedJSON() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.combinedJsonRequests.has_value()) return; @@ -801,6 +829,8 @@ void CommandLineInterface::handleCombinedJSON() void CommandLineInterface::handleAst() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.astCompactJson) return; @@ -848,6 +878,8 @@ bool CommandLineInterface::actOnInput() bool CommandLineInterface::link() { + solAssert(m_options.input.mode == InputMode::Linker, ""); + // Map from how the libraries will be named inside the bytecode to their addresses. map librariesReplacements; int const placeholderSize = 40; // 20 bytes or 40 hex characters @@ -911,6 +943,8 @@ bool CommandLineInterface::link() void CommandLineInterface::writeLinkedFiles() { + solAssert(m_options.input.mode == InputMode::Linker, ""); + for (auto const& src: m_fileReader.sourceCodes()) if (src.first == g_stdinFileName) sout() << src.second << endl; @@ -946,6 +980,8 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine) { + solAssert(m_options.input.mode == InputMode::Assembler, ""); + bool successful = true; map assemblyStacks; for (auto const& src: m_fileReader.sourceCodes()) @@ -1088,6 +1124,8 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul: void CommandLineInterface::outputCompilationResults() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + handleCombinedJSON(); // do we need AST output? From 30796b8957da1fae59150fa8a120d339f7e7f116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Sep 2021 14:15:17 +0200 Subject: [PATCH 2/3] Split CommandLineParser::parse() into smaller functions --- solc/CommandLineParser.cpp | 61 ++++++++++++++++++++++++++------------ solc/CommandLineParser.h | 20 ++++++++++++- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 220f0e393..dbfb46756 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -323,6 +323,16 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const return settings; } +bool CommandLineParser::parse(int _argc, char const* const* _argv, bool _interactiveTerminal) +{ + m_hasOutput = false; + + if (!parseArgs(_argc, _argv, _interactiveTerminal)) + return false; + + return processArgs(); +} + bool CommandLineParser::parseInputPathsAndRemappings() { m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0); @@ -478,10 +488,8 @@ bool CommandLineParser::parseLibraryOption(string const& _input) return true; } -bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) +po::options_description CommandLineParser::optionsDescription() { - m_hasOutput = false; - // Declare the supported options. po::options_description desc((R"(solc, the Solidity commandline compiler. @@ -780,12 +788,22 @@ General Information)").c_str(), ; desc.add(smtCheckerOptions); - po::options_description allOptions = desc; - allOptions.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); + desc.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); + return desc; +} +po::positional_options_description CommandLineParser::positionalOptionsDescription() +{ // All positional options should be interpreted as input files po::positional_options_description filesPositions; filesPositions.add(g_strInputFile.c_str(), -1); + return filesPositions; +} + +bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal) +{ + po::options_description allOptions = optionsDescription(); + po::positional_options_description filesPositions = positionalOptionsDescription(); // parse the compiler arguments try @@ -801,6 +819,25 @@ General Information)").c_str(), return false; } + if (m_args.count(g_strHelp) || (_interactiveTerminal && _argc == 1)) + { + sout() << allOptions; + return false; + } + + if (m_args.count(g_strVersion)) + printVersionAndExit(); + + if (m_args.count(g_strLicense)) + printLicenseAndExit(); + + po::notify(m_args); + + return true; +} + +bool CommandLineParser::processArgs() +{ if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) return false; @@ -826,18 +863,6 @@ General Information)").c_str(), m_options.formatting.withErrorIds = m_args.count(g_strErrorIds); - if (m_args.count(g_strHelp) || (interactiveTerminal && _argc == 1)) - { - sout() << desc; - return false; - } - - if (m_args.count(g_strVersion)) - printVersionAndExit(); - - if (m_args.count(g_strLicense)) - printLicenseAndExit(); - if (m_args.count(g_strRevertStrings)) { string revertStringsString = m_args[g_strRevertStrings].as(); @@ -895,8 +920,6 @@ General Information)").c_str(), m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0); - po::notify(m_args); - if (m_args.count(g_strBasePath)) m_options.input.basePath = m_args[g_strBasePath].as(); diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index ac80f6f0d..41aaab71b 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -195,7 +195,7 @@ public: /// CommandLineOptions structure has been fully initialized. false if there were errors - in /// this case CommandLineOptions may be only partially filled out. May also return false if /// there is not further processing necessary and the program should just exit. - bool parse(int _argc, char const* const* _argv, bool interactiveTerminal); + bool parse(int _argc, char const* const* _argv, bool _interactiveTerminal); CommandLineOptions const& options() const { return m_options; } @@ -203,6 +203,24 @@ public: bool hasOutput() const { return m_hasOutput; } private: + /// @returns a specification of all named command-line options accepted by the compiler. + /// The object can be used to parse command-line arguments or to generate the help screen. + static boost::program_options::options_description optionsDescription(); + + /// @returns a specification of all positional command-line arguments accepted by the compiler. + /// The object can be used to parse command-line arguments or to generate the help screen. + static boost::program_options::positional_options_description positionalOptionsDescription(); + + /// Uses boost::program_options to parse the command-line arguments and leaves the result in @a m_args. + /// Also handles the arguments that result in information being printed followed by immediate exit. + /// @returns false if parsing fails due to syntactical errors or the arguments not matching the description. + bool parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal); + + /// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions + /// structure. + /// @return false if there are any validation errors, true otherwise. + bool processArgs(); + /// Parses the value supplied to --combined-json. /// @return false if there are any validation errors, true otherwise. bool parseCombinedJsonOption(); From 4a8a003b3d937bd65c8610258e865a3bc51ac9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Sep 2021 14:21:56 +0200 Subject: [PATCH 3/3] CommandLineParser: Initialize inputMode and perform generic validations earlier --- solc/CommandLineParser.cpp | 102 ++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index dbfb46756..a72a010ab 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -838,6 +838,37 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int bool CommandLineParser::processArgs() { + if (!checkMutuallyExclusive({ + g_strStandardJSON, + g_strLink, + g_strAssemble, + g_strStrictAssembly, + g_strYul, + g_strImportAst, + })) + return false; + + if (m_args.count(g_strStandardJSON) > 0) + m_options.input.mode = InputMode::StandardJson; + else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) + m_options.input.mode = InputMode::Assembler; + else if (m_args.count(g_strLink) > 0) + m_options.input.mode = InputMode::Linker; + else if (m_args.count(g_strImportAst) > 0) + m_options.input.mode = InputMode::CompilerWithASTImport; + else + m_options.input.mode = InputMode::Compiler; + + if ( + m_args.count(g_strExperimentalViaIR) > 0 && + m_options.input.mode != InputMode::Compiler && + m_options.input.mode != InputMode::CompilerWithASTImport + ) + { + serr() << "The option --" << g_strExperimentalViaIR << " is only supported in the compiler mode." << endl; + return false; + } + if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) return false; @@ -856,6 +887,26 @@ bool CommandLineParser::processArgs() if (!checkMutuallyExclusive({g_strStopAfter, option})) return false; + if ( + m_options.input.mode != InputMode::Compiler && + m_options.input.mode != InputMode::CompilerWithASTImport && + m_options.input.mode != InputMode::Assembler + ) + { + if (!m_args[g_strOptimizeRuns].defaulted()) + { + serr() << "Option --" << g_strOptimizeRuns << " is only valid in compiler and assembler modes." << endl; + return false; + } + + for (string const& option: {g_strOptimize, g_strNoOptimizeYul, g_strOptimizeYul, g_strYulOptimizations}) + if (m_args.count(option) > 0) + { + serr() << "Option --" << option << " is only valid in compiler and assembler modes." << endl; + return false; + } + } + if (m_args.count(g_strColor) > 0) m_options.formatting.coloredOutput = true; else if (m_args.count(g_strNoColor) > 0) @@ -950,60 +1001,9 @@ bool CommandLineParser::processArgs() m_options.output.stopAfter = CompilerStack::State::Parsed; } - if (!checkMutuallyExclusive({ - g_strStandardJSON, - g_strLink, - g_strAssemble, - g_strStrictAssembly, - g_strYul, - g_strImportAst, - })) - return false; - - if (m_args.count(g_strStandardJSON) > 0) - m_options.input.mode = InputMode::StandardJson; - else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) - m_options.input.mode = InputMode::Assembler; - else if (m_args.count(g_strLink) > 0) - m_options.input.mode = InputMode::Linker; - else if (m_args.count(g_strImportAst) > 0) - m_options.input.mode = InputMode::CompilerWithASTImport; - else - m_options.input.mode = InputMode::Compiler; - - if ( - m_args.count(g_strExperimentalViaIR) > 0 && - m_options.input.mode != InputMode::Compiler && - m_options.input.mode != InputMode::CompilerWithASTImport - ) - { - serr() << "The option --" << g_strExperimentalViaIR << " is only supported in the compiler mode." << endl; - return false; - } - if (!parseInputPathsAndRemappings()) return false; - if ( - m_options.input.mode != InputMode::Compiler && - m_options.input.mode != InputMode::CompilerWithASTImport && - m_options.input.mode != InputMode::Assembler - ) - { - if (!m_args[g_strOptimizeRuns].defaulted()) - { - serr() << "Option --" << g_strOptimizeRuns << " is only valid in compiler and assembler modes." << endl; - return false; - } - - for (string const& option: {g_strOptimize, g_strNoOptimizeYul, g_strOptimizeYul, g_strYulOptimizations}) - if (m_args.count(option) > 0) - { - serr() << "Option --" << option << " is only valid in compiler and assembler modes." << endl; - return false; - } - } - if (m_options.input.mode == InputMode::StandardJson) return true;