mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
@@ -278,8 +278,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
};
|
||||
|
||||
// Will be re-generated later with correct information
|
||||
// We use the latest EVM version because we will re-run it anyway.
|
||||
assembly::AsmAnalysisInfo analysisInfo;
|
||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -875,6 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
assembly::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_errorReporter,
|
||||
m_evmVersion,
|
||||
assembly::AsmFlavour::Loose,
|
||||
identifierAccess
|
||||
);
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
@@ -43,7 +45,10 @@ class TypeChecker: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// @param _errorReporter provides the error logging functionality.
|
||||
TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
||||
TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter):
|
||||
m_evmVersion(_evmVersion),
|
||||
m_errorReporter(_errorReporter)
|
||||
{}
|
||||
|
||||
/// Performs type checking on the given contract and all of its sub-nodes.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
@@ -132,6 +137,8 @@ private:
|
||||
|
||||
ContractDefinition const* m_scope = nullptr;
|
||||
|
||||
EVMVersion m_evmVersion;
|
||||
|
||||
/// Flag indicating whether we are currently inside an EmitStatement.
|
||||
bool m_insideEmitStatement = false;
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/CompilerContext.h>
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <libsolidity/codegen/CompilerContext.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
namespace dev {
|
||||
namespace solidity {
|
||||
@@ -33,11 +36,11 @@ namespace solidity {
|
||||
class Compiler
|
||||
{
|
||||
public:
|
||||
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
|
||||
explicit Compiler(EVMVersion _evmVersion = EVMVersion{}, bool _optimize = false, unsigned _runs = 200):
|
||||
m_optimize(_optimize),
|
||||
m_optimizeRuns(_runs),
|
||||
m_runtimeContext(),
|
||||
m_context(&m_runtimeContext)
|
||||
m_runtimeContext(_evmVersion),
|
||||
m_context(_evmVersion, &m_runtimeContext)
|
||||
{ }
|
||||
|
||||
/// Compiles a contract.
|
||||
|
||||
@@ -329,6 +329,7 @@ void CompilerContext::appendInlineAssembly(
|
||||
analyzerResult = assembly::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
m_evmVersion,
|
||||
assembly::AsmFlavour::Strict,
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult);
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/ast/ASTAnnotations.h>
|
||||
@@ -50,14 +52,17 @@ namespace solidity {
|
||||
class CompilerContext
|
||||
{
|
||||
public:
|
||||
explicit CompilerContext(CompilerContext* _runtimeContext = nullptr):
|
||||
explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
|
||||
m_asm(std::make_shared<eth::Assembly>()),
|
||||
m_evmVersion(_evmVersion),
|
||||
m_runtimeContext(_runtimeContext)
|
||||
{
|
||||
if (m_runtimeContext)
|
||||
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
|
||||
}
|
||||
|
||||
EVMVersion const& evmVersion() const { return m_evmVersion; }
|
||||
|
||||
/// Update currently enabled set of experimental features.
|
||||
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
|
||||
/// @returns true if the given feature is enabled.
|
||||
@@ -204,7 +209,7 @@ public:
|
||||
void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
|
||||
|
||||
/// Run optimisation step.
|
||||
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
|
||||
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
|
||||
|
||||
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
||||
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
||||
@@ -287,6 +292,8 @@ private:
|
||||
} m_functionCompilationQueue;
|
||||
|
||||
eth::AssemblyPointer m_asm;
|
||||
/// Version of the EVM to compile against.
|
||||
EVMVersion m_evmVersion;
|
||||
/// Activated experimental features.
|
||||
std::set<ExperimentalFeature> m_experimentalFeatures;
|
||||
/// Other already compiled contracts to be used in contract creation calls.
|
||||
|
||||
@@ -1059,7 +1059,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin
|
||||
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
||||
}
|
||||
|
||||
eth::AssemblyPointer ContractCompiler::cloneRuntime()
|
||||
eth::AssemblyPointer ContractCompiler::cloneRuntime() const
|
||||
{
|
||||
eth::Assembly a;
|
||||
a << Instruction::CALLDATASIZE;
|
||||
@@ -1070,7 +1070,7 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime()
|
||||
// this is the address which has to be substituted by the linker.
|
||||
//@todo implement as special "marker" AssemblyItem.
|
||||
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
||||
a << u256(eth::GasCosts::callGas + 10) << Instruction::GAS << Instruction::SUB;
|
||||
a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB;
|
||||
a << Instruction::DELEGATECALL;
|
||||
//Propagate error condition (if DELEGATECALL pushes 0 on stack).
|
||||
a << Instruction::ISZERO;
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
m_runtimeCompiler(_runtimeCompiler),
|
||||
m_context(_context)
|
||||
{
|
||||
m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
|
||||
m_context = CompilerContext(_context.evmVersion(), _runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
|
||||
}
|
||||
|
||||
void compileContract(
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
||||
|
||||
/// @returns the runtime assembly for clone contracts.
|
||||
static eth::AssemblyPointer cloneRuntime();
|
||||
eth::AssemblyPointer cloneRuntime() const;
|
||||
|
||||
bool const m_optimise;
|
||||
/// Pointer to the runtime compiler in case this is a creation compiler.
|
||||
|
||||
@@ -1756,7 +1756,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
{
|
||||
// send all gas except the amount needed to execute "SUB" and "CALL"
|
||||
// @todo this retains too much gas for now, needs to be fine-tuned.
|
||||
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
|
||||
u256 gasNeededByCaller = eth::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (_functionType.valueSet())
|
||||
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
|
||||
if (!existenceChecked)
|
||||
|
||||
@@ -533,33 +533,47 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
|
||||
|
||||
void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location)
|
||||
{
|
||||
static set<solidity::Instruction> futureInstructions{
|
||||
solidity::Instruction::CREATE2,
|
||||
solidity::Instruction::RETURNDATACOPY,
|
||||
solidity::Instruction::RETURNDATASIZE,
|
||||
solidity::Instruction::STATICCALL
|
||||
};
|
||||
if (futureInstructions.count(_instr))
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available after " +
|
||||
"the Metropolis hard fork. Before that it acts as an invalid instruction."
|
||||
);
|
||||
// We assume that returndatacopy, returndatasize and staticcall are either all available
|
||||
// or all not available.
|
||||
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
|
||||
|
||||
static set<solidity::Instruction> experimentalInstructions{
|
||||
solidity::Instruction::SHL,
|
||||
solidity::Instruction::SHR,
|
||||
solidity::Instruction::SAR
|
||||
};
|
||||
if (experimentalInstructions.count(_instr))
|
||||
if (_instr == solidity::Instruction::CREATE2)
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available after " +
|
||||
"the Constantinople hard fork. Before that it acts as an invalid instruction."
|
||||
+ "\" instruction is not supported by the VM version \"" +
|
||||
"" + m_evmVersion.name() +
|
||||
"\" you are currently compiling for. " +
|
||||
"It will be interpreted as an invalid instruction on this VM."
|
||||
);
|
||||
else if ((
|
||||
_instr == solidity::Instruction::RETURNDATACOPY ||
|
||||
_instr == solidity::Instruction::RETURNDATASIZE ||
|
||||
_instr == solidity::Instruction::STATICCALL
|
||||
) && !m_evmVersion.supportsReturndata())
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available for Byzantium-compatible VMs. " +
|
||||
"You are currently compiling for \"" +
|
||||
m_evmVersion.name() +
|
||||
"\", where it will be interpreted as an invalid instruction."
|
||||
);
|
||||
else if ((
|
||||
_instr == solidity::Instruction::SHL ||
|
||||
_instr == solidity::Instruction::SHR ||
|
||||
_instr == solidity::Instruction::SAR
|
||||
) && !m_evmVersion.hasBitwiseShifting())
|
||||
m_errorReporter.warning(
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
+ "\" instruction is only available for Constantinople-compatible VMs. " +
|
||||
"You are currently compiling for \"" +
|
||||
m_evmVersion.name() +
|
||||
"\", where it will be interpreted as an invalid instruction."
|
||||
);
|
||||
|
||||
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
|
||||
@@ -54,9 +55,10 @@ public:
|
||||
explicit AsmAnalyzer(
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
ErrorReporter& _errorReporter,
|
||||
EVMVersion _evmVersion,
|
||||
AsmFlavour _flavour = AsmFlavour::Loose,
|
||||
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
|
||||
): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {}
|
||||
): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {}
|
||||
|
||||
bool analyze(assembly::Block const& _block);
|
||||
|
||||
@@ -97,6 +99,7 @@ private:
|
||||
std::set<Scope::Variable const*> m_activeVariables;
|
||||
AsmAnalysisInfo& m_info;
|
||||
ErrorReporter& m_errorReporter;
|
||||
EVMVersion m_evmVersion;
|
||||
AsmFlavour m_flavour = AsmFlavour::Loose;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann
|
||||
bool AssemblyStack::analyzeParsed()
|
||||
{
|
||||
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
|
||||
assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language));
|
||||
assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language));
|
||||
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
|
||||
return m_analysisSuccessful;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/ErrorReporter.h>
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
|
||||
#include <string>
|
||||
@@ -54,8 +56,8 @@ public:
|
||||
enum class Language { JULIA, Assembly, StrictAssembly };
|
||||
enum class Machine { EVM, EVM15, eWasm };
|
||||
|
||||
explicit AssemblyStack(Language _language = Language::Assembly):
|
||||
m_language(_language), m_errorReporter(m_errors)
|
||||
explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly):
|
||||
m_language(_language), m_evmVersion(_evmVersion), m_errorReporter(m_errors)
|
||||
{}
|
||||
|
||||
/// @returns the scanner used during parsing
|
||||
@@ -82,6 +84,7 @@ private:
|
||||
bool analyzeParsed();
|
||||
|
||||
Language m_language = Language::Assembly;
|
||||
EVMVersion m_evmVersion;
|
||||
|
||||
std::shared_ptr<Scanner> m_scanner;
|
||||
|
||||
|
||||
@@ -74,6 +74,12 @@ void CompilerStack::setRemappings(vector<string> 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();
|
||||
@@ -198,7 +205,7 @@ bool CompilerStack::analyze()
|
||||
m_contracts[contract->fullyQualifiedName()].contract = contract;
|
||||
}
|
||||
|
||||
TypeChecker typeChecker(m_errorReporter);
|
||||
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
@@ -677,7 +684,7 @@ void CompilerStack::compileContract(
|
||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||
compileContract(*dependency, _compiledContracts);
|
||||
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
|
||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||
string metadata = createMetadata(compiledContract);
|
||||
bytes cborEncodedHash =
|
||||
@@ -736,7 +743,7 @@ void CompilerStack::compileContract(
|
||||
{
|
||||
if (!_contract.isLibrary())
|
||||
{
|
||||
Compiler cloneCompiler(m_optimize, m_optimizeRuns);
|
||||
Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns);
|
||||
cloneCompiler.compileClone(_contract, _compiledContracts);
|
||||
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||
}
|
||||
@@ -838,6 +845,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
||||
}
|
||||
meta["settings"]["optimizer"]["enabled"] = m_optimize;
|
||||
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
|
||||
meta["settings"]["evmVersion"] = m_evmVersion.name();
|
||||
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
|
||||
_contract.contract->annotation().canonicalName;
|
||||
|
||||
@@ -951,11 +959,12 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||
return Json::Value();
|
||||
|
||||
using Gas = GasEstimator::GasConsumption;
|
||||
GasEstimator gasEstimator(m_evmVersion);
|
||||
Json::Value output(Json::objectValue);
|
||||
|
||||
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
|
||||
{
|
||||
Gas executionGas = GasEstimator::functionalEstimation(*items);
|
||||
Gas executionGas = gasEstimator.functionalEstimation(*items);
|
||||
u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
|
||||
Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
|
||||
|
||||
@@ -976,14 +985,14 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||
for (auto it: contract.interfaceFunctions())
|
||||
{
|
||||
string sig = it.second->externalSignature();
|
||||
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
|
||||
externalFunctions[sig] = gasToJson(gasEstimator.functionalEstimation(*items, sig));
|
||||
}
|
||||
|
||||
if (contract.fallbackFunction())
|
||||
/// This needs to be set to an invalid signature in order to trigger the fallback,
|
||||
/// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
|
||||
/// An empty string ("") would work to trigger the shortcut only.
|
||||
externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
|
||||
externalFunctions[""] = gasToJson(gasEstimator.functionalEstimation(*items, "INVALID"));
|
||||
|
||||
if (!externalFunctions.empty())
|
||||
output["external"] = externalFunctions;
|
||||
@@ -999,7 +1008,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||
size_t entry = functionEntryPoint(_contractName, *it);
|
||||
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
||||
if (entry > 0)
|
||||
gas = GasEstimator::functionalEstimation(*items, entry, *it);
|
||||
gas = gasEstimator.functionalEstimation(*items, entry, *it);
|
||||
|
||||
/// TODO: This could move into a method shared with externalSignature()
|
||||
FunctionType type(*it);
|
||||
|
||||
@@ -23,20 +23,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/ErrorReporter.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/FixedHash.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <json/json.h>
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/FixedHash.h>
|
||||
#include <libevmasm/SourceLocation.h>
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
#include <libsolidity/interface/ErrorReporter.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
|
||||
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<std::string> const& _contractNames = std::set<std::string>{})
|
||||
@@ -310,6 +318,7 @@ private:
|
||||
ReadCallback::Callback m_smtQuery;
|
||||
bool m_optimize = false;
|
||||
unsigned m_optimizeRuns = 200;
|
||||
EVMVersion m_evmVersion;
|
||||
std::set<std::string> m_requestedContractNames;
|
||||
std::map<std::string, h160> m_libraries;
|
||||
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* EVM versioning.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/operators.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/**
|
||||
* A version specifier of the EVM we want to compile to.
|
||||
* Defaults to the latest version.
|
||||
*/
|
||||
class EVMVersion:
|
||||
boost::less_than_comparable<EVMVersion>,
|
||||
boost::equality_comparable<EVMVersion>
|
||||
{
|
||||
public:
|
||||
EVMVersion() {}
|
||||
|
||||
static EVMVersion homestead() { return {Version::Homestead}; }
|
||||
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
|
||||
static EVMVersion spuriousDragon() { return {Version::SpuriousDragon}; }
|
||||
static EVMVersion byzantium() { return {Version::Byzantium}; }
|
||||
static EVMVersion constantinople() { return {Version::Constantinople}; }
|
||||
|
||||
static boost::optional<EVMVersion> fromString(std::string const& _version)
|
||||
{
|
||||
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()})
|
||||
if (_version == v.name())
|
||||
return v;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
|
||||
bool operator<(EVMVersion const& _other) const { return m_version < _other.m_version; }
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
switch (m_version)
|
||||
{
|
||||
case Version::Homestead: return "homestead";
|
||||
case Version::TangerineWhistle: return "tangerineWhistle";
|
||||
case Version::SpuriousDragon: return "spuriousDragon";
|
||||
case Version::Byzantium: return "byzantium";
|
||||
case Version::Constantinople: return "constantinople";
|
||||
}
|
||||
return "INVALID";
|
||||
}
|
||||
|
||||
/// Has the RETURNDATACOPY and RETURNDATASIZE opcodes.
|
||||
bool supportsReturndata() const { return *this >= byzantium(); }
|
||||
bool hasStaticCall() const { return *this >= byzantium(); }
|
||||
bool hasBitwiseShifting() const { return *this >= constantinople(); }
|
||||
|
||||
/// 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 { return *this >= tangerineWhistle(); }
|
||||
|
||||
private:
|
||||
enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople };
|
||||
|
||||
EVMVersion(Version _version): m_version(_version) {}
|
||||
|
||||
Version m_version = Version::Byzantium;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ using namespace dev::solidity;
|
||||
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
|
||||
AssemblyItems const& _items,
|
||||
vector<ASTNode const*> const& _ast
|
||||
)
|
||||
) const
|
||||
{
|
||||
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
|
||||
map<SourceLocation, GasConsumption> particularCosts;
|
||||
@@ -49,7 +49,7 @@ GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimatio
|
||||
for (BasicBlock const& block: cfg.optimisedBlocks())
|
||||
{
|
||||
solAssert(!!block.startState, "");
|
||||
GasMeter meter(block.startState->copy());
|
||||
GasMeter meter(block.startState->copy(), m_evmVersion);
|
||||
auto const end = _items.begin() + block.end;
|
||||
for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
|
||||
particularCosts[iter->location()] += meter.estimateMax(*iter);
|
||||
@@ -127,7 +127,7 @@ map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLeve
|
||||
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||
AssemblyItems const& _items,
|
||||
string const& _signature
|
||||
)
|
||||
) const
|
||||
{
|
||||
auto state = make_shared<KnownState>();
|
||||
|
||||
@@ -144,7 +144,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||
});
|
||||
}
|
||||
|
||||
PathGasMeter meter(_items);
|
||||
PathGasMeter meter(_items, m_evmVersion);
|
||||
return meter.estimateMax(0, state);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||
AssemblyItems const& _items,
|
||||
size_t const& _offset,
|
||||
FunctionDefinition const& _function
|
||||
)
|
||||
) const
|
||||
{
|
||||
auto state = make_shared<KnownState>();
|
||||
|
||||
@@ -167,7 +167,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||
if (parametersSize > 0)
|
||||
state->feedItem(swapInstruction(parametersSize));
|
||||
|
||||
return PathGasMeter(_items).estimateMax(_offset, state);
|
||||
return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state);
|
||||
}
|
||||
|
||||
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
|
||||
|
||||
@@ -22,11 +22,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
|
||||
#include <libevmasm/GasMeter.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <libevmasm/GasMeter.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@@ -44,13 +47,15 @@ public:
|
||||
using ASTGasConsumptionSelfAccumulated =
|
||||
std::map<ASTNode const*, std::array<GasConsumption, 2>>;
|
||||
|
||||
explicit GasEstimator(EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
|
||||
|
||||
/// Estimates the gas consumption for every assembly item in the given assembly and stores
|
||||
/// it by source location.
|
||||
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
|
||||
static ASTGasConsumptionSelfAccumulated structuralEstimation(
|
||||
ASTGasConsumptionSelfAccumulated structuralEstimation(
|
||||
eth::AssemblyItems const& _items,
|
||||
std::vector<ASTNode const*> const& _ast
|
||||
);
|
||||
) const;
|
||||
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
|
||||
/// the following source locations are part of the mapping:
|
||||
/// 1. source locations of statements that do not contain other statements
|
||||
@@ -62,23 +67,24 @@ public:
|
||||
|
||||
/// @returns the estimated gas consumption by the (public or external) function with the
|
||||
/// given signature. If no signature is given, estimates the maximum gas usage.
|
||||
static GasConsumption functionalEstimation(
|
||||
GasConsumption functionalEstimation(
|
||||
eth::AssemblyItems const& _items,
|
||||
std::string const& _signature = ""
|
||||
);
|
||||
) const;
|
||||
|
||||
/// @returns the estimated gas consumption by the given function which starts at the given
|
||||
/// offset into the list of assembly items.
|
||||
/// @note this does not work correctly for recursive functions.
|
||||
static GasConsumption functionalEstimation(
|
||||
GasConsumption functionalEstimation(
|
||||
eth::AssemblyItems const& _items,
|
||||
size_t const& _offset,
|
||||
FunctionDefinition const& _function
|
||||
);
|
||||
) const;
|
||||
|
||||
private:
|
||||
/// @returns the set of AST nodes which are the finest nodes at their location.
|
||||
static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
|
||||
EVMVersion m_evmVersion;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString());
|
||||
if (!version)
|
||||
return formatFatalError("JSONError", "Invalid EVM version requested.");
|
||||
m_compilerStack.setEVMVersion(*version);
|
||||
}
|
||||
|
||||
vector<string> remappings;
|
||||
for (auto const& remapping: settings.get("remappings", Json::Value()))
|
||||
remappings.push_back(remapping.asString());
|
||||
|
||||
Reference in New Issue
Block a user