From 5ab4a1ae7819004415293bf72a86824beb43cd51 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 23:43:40 +0100 Subject: [PATCH] Add ability to set the target EVM version. --- libsolidity/interface/CompilerStack.cpp | 7 ++ libsolidity/interface/CompilerStack.h | 27 +++++--- libsolidity/interface/EVMVersion.h | 81 ++++++++++++++++++++++ libsolidity/interface/StandardCompiler.cpp | 8 +++ solc/CommandLineInterface.cpp | 24 ++++++- 5 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 libsolidity/interface/EVMVersion.h diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3b5e65e8c..7bc31c17b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector const& _remappings) swap(m_remappings, remappings); } +void CompilerStack::setEVMVersion(EVMVersion _version) +{ + solAssert(m_stackState < State::ParsingSuccessful, "Set EVM version after parsing."); + m_evmVersion = _version; +} + void CompilerStack::reset(bool _keepSources) { if (_keepSources) @@ -88,6 +94,7 @@ void CompilerStack::reset(bool _keepSources) m_sources.clear(); } m_libraries.clear(); + m_evmVersion = EVMVersion(); m_optimize = false; m_optimizeRuns = 200; m_globalContext.reset(); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index b377b3aa8..13c9cc7a1 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -23,20 +23,26 @@ #pragma once +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace dev { @@ -116,6 +122,8 @@ public: m_optimizeRuns = _runs; } + void setEVMVersion(EVMVersion _version = EVMVersion{}); + /// Sets the list of requested contract names. If empty, no filtering is performed and every contract /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. void setRequestedContractNames(std::set const& _contractNames = std::set{}) @@ -310,6 +318,7 @@ private: ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; + EVMVersion m_evmVersion; std::set m_requestedContractNames; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h new file mode 100644 index 000000000..1ddcd218b --- /dev/null +++ b/libsolidity/interface/EVMVersion.h @@ -0,0 +1,81 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * EVM versioning. + */ + +#pragma once + +#include + +#include + +namespace dev +{ +namespace solidity +{ + +/** + * A version specifier of the EVM we want to compile to. + * Defaults to the latest version. + */ +class EVMVersion +{ +public: + EVMVersion() {} + + static EVMVersion homestead() { return {Version::Homestead}; } + static EVMVersion byzantium() { return {Version::Byzantium}; } + + static boost::optional fromString(std::string const& _version) + { + if (_version == "homestead") + return homestead(); + else if (_version == "byzantium") + return byzantium(); + else + return {}; + } + + bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; } + bool operator!=(EVMVersion const& _other) const { return !this->operator==(_other); } + + std::string name() const { return m_version == Version::Byzantium ? "byzantium" : "homestead"; } + + /// Has the RETURNDATACOPY and RETURNDATASIZE opcodes. + bool supportsReturndata() const { return *this >= byzantium(); } + bool hasStaticCall() const { return *this >= byzantium(); } + + /// Whether we have to retain the costs for the call opcode itself (false), + /// or whether we can just forward easily all remaining gas (true). + bool canOverchargeGasForCall() const + { + // @TODO when exactly was this introduced? Was together with the call stack fix. + return m_version == Version::Byzantium; + } + +private: + enum class Version { Homestead, Byzantium }; + + EVMVersion(Version _version): m_version(_version) {} + + Version m_version = Version::Byzantium; +}; + + +} +} diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 91fe72ae2..ee9b14406 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -318,6 +318,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value const& settings = _input.get("settings", Json::Value()); + if (settings.isMember("evmVersion")) + { + boost::optional version = EVMVersion::fromString(settings.get("evmVersion", {}).asString()); + if (!version) + return formatFatalError("JSONError", "Invalid EVM version requested."); + m_compilerStack.setEVMVersion(*version); + } + vector remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) remappings.push_back(remapping.asString()); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 62b249755..04d6d1a8d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -71,7 +71,6 @@ namespace solidity static string const g_stdinFileNameStr = ""; static string const g_strAbi = "abi"; -static string const g_strAddStandard = "add-std"; static string const g_strAllowPaths = "allow-paths"; static string const g_strAsm = "asm"; static string const g_strAsmJson = "asm-json"; @@ -87,6 +86,7 @@ static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; +static string const g_strEVMVersion = "evm-version"; static string const g_streWasm = "ewasm"; static string const g_strFormal = "formal"; static string const g_strGas = "gas"; @@ -118,7 +118,6 @@ static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; static string const g_argAbi = g_strAbi; -static string const g_argAddStandard = g_strAddStandard; static string const g_argPrettyJson = g_strPrettyJson; static string const g_argAllowPaths = g_strAllowPaths; static string const g_argAsm = g_strAsm; @@ -537,13 +536,17 @@ Allowed options)", (g_argHelp.c_str(), "Show help message and exit.") (g_argVersion.c_str(), "Show version and exit.") (g_strLicense.c_str(), "Show licensing information and exit.") + ( + g_strEVMVersion.c_str(), + po::value()->value_name("version"), + "Select desired EVM version. Either homestead or byzantium (default)." + ) (g_argOptimize.c_str(), "Enable bytecode optimizer.") ( g_argOptimizeRuns.c_str(), po::value()->value_name("n")->default_value(200), "Estimated number of contract runs for optimizer tuning." ) - (g_argAddStandard.c_str(), "Add standard contracts.") (g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.") ( g_argLibraries.c_str(), @@ -779,6 +782,19 @@ bool CommandLineInterface::processInput() m_compiler.reset(new CompilerStack(fileReader)); + EVMVersion evmVersion; + if (m_args.count(g_strEVMVersion)) + { + string versionOptionStr = m_args[g_strEVMVersion].as(); + boost::optional versionOption = EVMVersion::fromString(versionOptionStr); + if (!versionOption) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return false; + } + evmVersion = *versionOption; + } + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; SourceReferenceFormatter formatter(cerr, scannerFromSourceName); @@ -792,6 +808,8 @@ bool CommandLineInterface::processInput() m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); + if (m_args.count(g_strEVMVersion)) + m_compiler->setEVMVersion(evmVersion); // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count(g_argOptimize) > 0; unsigned runs = m_args[g_argOptimizeRuns].as();