Provide EVM version to assembly analysis.

This commit is contained in:
chriseth 2018-02-23 11:42:53 +01:00
parent a53d6b499d
commit dc317a44e0
15 changed files with 91 additions and 44 deletions

View File

@ -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;
}

View File

@ -875,6 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
m_evmVersion,
assembly::AsmFlavour::Loose,
identifierAccess
);

View File

@ -329,6 +329,7 @@ void CompilerContext::appendInlineAssembly(
analyzerResult = assembly::AsmAnalyzer(
analysisInfo,
errorReporter,
m_evmVersion,
assembly::AsmFlavour::Strict,
identifierAccess.resolve
).analyze(*parserResult);

View File

@ -533,19 +533,33 @@ 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))
// We assume that returndatacopy, returndatasize and staticcall are either all available
// or all not available.
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
if (_instr == solidity::Instruction::CREATE2)
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."
+ "\" 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."
);
static set<solidity::Instruction> experimentalInstructions{

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -748,6 +748,19 @@ bool CommandLineInterface::processInput()
if (!parseLibraryOption(library))
return false;
EVMVersion evmVersion;
if (m_args.count(g_strEVMVersion))
{
string versionOptionStr = m_args[g_strEVMVersion].as<string>();
boost::optional<EVMVersion> versionOption = EVMVersion::fromString(versionOptionStr);
if (!versionOption)
{
cerr << "Invalid option for --evm-version: " << versionOptionStr << endl;
return false;
}
evmVersion = *versionOption;
}
if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia))
{
// switch to assembly mode
@ -771,7 +784,7 @@ bool CommandLineInterface::processInput()
return false;
}
}
return assemble(inputLanguage, targetMachine);
return assemble(evmVersion, inputLanguage, targetMachine);
}
if (m_args.count(g_argLink))
{
@ -782,19 +795,6 @@ bool CommandLineInterface::processInput()
m_compiler.reset(new CompilerStack(fileReader));
EVMVersion evmVersion;
if (m_args.count(g_strEVMVersion))
{
string versionOptionStr = m_args[g_strEVMVersion].as<string>();
boost::optional<EVMVersion> 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);
@ -1081,6 +1081,7 @@ void CommandLineInterface::writeLinkedFiles()
}
bool CommandLineInterface::assemble(
EVMVersion _evmVersion,
AssemblyStack::Language _language,
AssemblyStack::Machine _targetMachine
)
@ -1089,7 +1090,7 @@ bool CommandLineInterface::assemble(
map<string, AssemblyStack> assemblyStacks;
for (auto const& src: m_sourceCodes)
{
auto& stack = assemblyStacks[src.first] = AssemblyStack(_language);
auto& stack = assemblyStacks[src.first] = AssemblyStack(_evmVersion, _language);
try
{
if (!stack.parseAndAnalyze(src.first, src.second))

View File

@ -54,7 +54,7 @@ private:
bool link();
void writeLinkedFiles();
bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine);
bool assemble(EVMVersion _evmVersion, AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine);
void outputCompilationResults();

View File

@ -51,17 +51,11 @@ string getIPCSocketPath()
ExecutionFramework::ExecutionFramework() :
m_rpc(RPCSession::instance(getIPCSocketPath())),
m_evmVersion(dev::test::Options::get().evmVersion()),
m_optimize(dev::test::Options::get().optimize),
m_showMessages(dev::test::Options::get().showMessages),
m_sender(m_rpc.account(0))
{
if (!dev::test::Options::get().evmVersion.empty())
{
auto version = solidity::EVMVersion::fromString(dev::test::Options::get().evmVersion);
BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + dev::test::Options::get().evmVersion);
m_evmVersion = *version;
}
m_rpc.test_rewindToBlock(0);
}

View File

@ -47,7 +47,7 @@ Options::Options()
optimize = true;
else if (string(suite.argv[i]) == "--evm-version")
{
evmVersion = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID";
evmVersionString = i + 1 < suite.argc ? suite.argv[i + 1] : "INVALID";
++i;
}
else if (string(suite.argv[i]) == "--show-messages")
@ -61,3 +61,17 @@ Options::Options()
if (auto path = getenv("ETH_TEST_IPC"))
ipcPath = path;
}
dev::solidity::EVMVersion Options::evmVersion() const
{
if (!evmVersionString.empty())
{
// We do this check as opposed to in the constructor because the BOOST_REQUIRE
// macros cannot yet be used in the constructor.
auto version = solidity::EVMVersion::fromString(evmVersionString);
BOOST_REQUIRE_MESSAGE(version, "Invalid EVM version: " + evmVersionString);
return *version;
}
else
return dev::solidity::EVMVersion();
}

View File

@ -36,14 +36,17 @@ struct Options: boost::noncopyable
{
std::string ipcPath;
bool showMessages = false;
std::string evmVersion;
bool optimize = false;
bool disableIPC = false;
bool disableSMT = false;
solidity::EVMVersion evmVersion() const;
static Options const& get();
private:
std::string evmVersionString;
Options();
};

View File

@ -21,6 +21,8 @@
#include <test/libjulia/Common.h>
#include <test/TestHelper.h>
#include <libjulia/optimiser/Disambiguator.h>
#include <libsolidity/parsing/Scanner.h>
@ -61,7 +63,12 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test:
{
BOOST_REQUIRE(errorReporter.errors().empty());
auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, flavour);
assembly::AsmAnalyzer analyzer(
*analysisInfo,
errorReporter,
dev::test::Options::get().evmVersion(),
flavour
);
if (analyzer.analyze(*parserResult))
{
BOOST_REQUIRE(errorReporter.errors().empty());

View File

@ -56,7 +56,12 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
if (parserResult)
{
assembly::AsmAnalysisInfo analysisInfo;
return (assembly::AsmAnalyzer(analysisInfo, errorReporter, assembly::AsmFlavour::IULIA)).analyze(*parserResult);
return (assembly::AsmAnalyzer(
analysisInfo,
errorReporter,
dev::test::Options::get().evmVersion(),
assembly::AsmFlavour::IULIA
)).analyze(*parserResult);
}
}
catch (FatalError const&)

View File

@ -55,7 +55,7 @@ boost::optional<Error> parseAndReturnFirstError(
AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM
)
{
AssemblyStack stack(_language);
AssemblyStack stack(dev::test::Options::get().evmVersion(), _language);
bool success = false;
try
{
@ -117,7 +117,7 @@ Error expectError(
void parsePrintCompare(string const& _source, bool _canWarn = false)
{
AssemblyStack stack;
AssemblyStack stack(dev::test::Options::get().evmVersion());
BOOST_REQUIRE(stack.parseAndAnalyze("", _source));
if (_canWarn)
BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors()));
@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
{
string source = "{ let x := \"\\u1bac\" }";
string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}";
AssemblyStack stack;
AssemblyStack stack(dev::test::Options::get().evmVersion());
BOOST_REQUIRE(stack.parseAndAnalyze("", source));
BOOST_REQUIRE(stack.errors().empty());
BOOST_CHECK_EQUAL(stack.print(), parsed);