mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Use actual type checking phase of assembler.
This commit is contained in:
parent
e0849f2f3b
commit
c6fa78c73e
@ -25,7 +25,7 @@
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
using namespace std;
|
||||
@ -163,9 +163,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
// The only purpose of this step is to fill the inline assembly annotation with
|
||||
// external references.
|
||||
ErrorList errorsIgnored;
|
||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errorsIgnored);
|
||||
assembly::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
|
||||
assembly::ExternalIdentifierAccess::Resolver resolver =
|
||||
[&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
|
||||
if (declarations.size() != 1)
|
||||
return size_t(-1);
|
||||
@ -173,7 +172,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
// At this stage we do not yet know the stack size of the identifier, so we just return 1.
|
||||
return size_t(1);
|
||||
};
|
||||
codeGen.typeCheck(identifierAccess);
|
||||
assembly::AsmAnalyzer::Scopes scopes;
|
||||
assembly::AsmAnalyzer(scopes, errorsIgnored, resolver).analyze(_inlineAssembly.operations());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,7 @@
|
||||
#include <memory>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libevmasm/Assembly.h> // needed for inline assembly
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -630,8 +629,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||
// We run the resolve step again regardless.
|
||||
assembly::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](
|
||||
assembly::ExternalIdentifierAccess::Resolver identifierAccess = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
assembly::IdentifierContext _context
|
||||
)
|
||||
@ -682,8 +680,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
ref->second.valueSize = valueSize;
|
||||
return valueSize;
|
||||
};
|
||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
||||
if (!codeGen.typeCheck(identifierAccess))
|
||||
assembly::AsmAnalyzer::Scopes scopes;
|
||||
assembly::AsmAnalyzer analyzer(scopes, m_errors, identifierAccess);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -38,8 +38,12 @@ using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
|
||||
AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors, bool _allowFailedLookups):
|
||||
m_allowFailedLookups(_allowFailedLookups), m_scopes(_scopes), m_errors(_errors)
|
||||
AsmAnalyzer::AsmAnalyzer(
|
||||
AsmAnalyzer::Scopes& _scopes,
|
||||
ErrorList& _errors,
|
||||
ExternalIdentifierAccess::Resolver const& _resolver
|
||||
):
|
||||
m_resolver(_resolver), m_scopes(_scopes), m_errors(_errors)
|
||||
{
|
||||
}
|
||||
|
||||
@ -92,7 +96,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
|
||||
)))
|
||||
{
|
||||
}
|
||||
else if (!m_allowFailedLookups)
|
||||
else if (!m_resolver || m_resolver(_identifier, IdentifierContext::RValue) == size_t(-1))
|
||||
{
|
||||
m_errors.push_back(make_shared<Error>(
|
||||
Error::Type::DeclarationError,
|
||||
@ -223,10 +227,11 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable)
|
||||
{
|
||||
if (!(*this)(_variable))
|
||||
return false;
|
||||
else if (!m_allowFailedLookups)
|
||||
|
||||
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||
{
|
||||
// Check that it is a variable
|
||||
if (m_currentScope->lookup(_variable.name)->type() != typeid(Scope::Variable))
|
||||
if (var->type() != typeid(Scope::Variable))
|
||||
{
|
||||
m_errors.push_back(make_shared<Error>(
|
||||
Error::Type::TypeError,
|
||||
@ -236,6 +241,15 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!m_resolver || m_resolver(_variable, IdentifierContext::LValue) == size_t(-1))
|
||||
{
|
||||
m_errors.push_back(make_shared<Error>(
|
||||
Error::Type::DeclarationError,
|
||||
"Variable not found.",
|
||||
_variable.location
|
||||
));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmStack.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
@ -57,9 +59,7 @@ class AsmAnalyzer: public boost::static_visitor<bool>
|
||||
{
|
||||
public:
|
||||
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
|
||||
/// @param _allowFailedLookups if true, allow failed lookups for variables (they
|
||||
/// will be provided from the environment later on)
|
||||
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors, bool _allowFailedLookups);
|
||||
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors, ExternalIdentifierAccess::Resolver const& _resolver);
|
||||
|
||||
bool analyze(assembly::Block const& _block);
|
||||
|
||||
@ -79,7 +79,7 @@ private:
|
||||
bool checkAssignment(assembly::Identifier const& _assignment);
|
||||
Scope& scope(assembly::Block const* _block);
|
||||
|
||||
bool m_allowFailedLookups = false;
|
||||
ExternalIdentifierAccess::Resolver const& m_resolver;
|
||||
Scope* m_currentScope = nullptr;
|
||||
Scopes& m_scopes;
|
||||
ErrorList& m_errors;
|
||||
|
@ -308,22 +308,11 @@ private:
|
||||
ExternalIdentifierAccess m_identifierAccess;
|
||||
};
|
||||
|
||||
bool assembly::CodeGenerator::typeCheck(ExternalIdentifierAccess const& _identifierAccess)
|
||||
{
|
||||
size_t initialErrorLen = m_errors.size();
|
||||
eth::Assembly assembly;
|
||||
GeneratorState state(m_errors, assembly);
|
||||
if (!(AsmAnalyzer(state.scopes, m_errors, !!_identifierAccess.resolve)).analyze(m_parsedData))
|
||||
return false;
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
return m_errors.size() == initialErrorLen;
|
||||
}
|
||||
|
||||
eth::Assembly assembly::CodeGenerator::assemble(ExternalIdentifierAccess const& _identifierAccess)
|
||||
{
|
||||
eth::Assembly assembly;
|
||||
GeneratorState state(m_errors, assembly);
|
||||
if (!(AsmAnalyzer(state.scopes, m_errors, !!_identifierAccess.resolve)).analyze(m_parsedData))
|
||||
if (!(AsmAnalyzer(state.scopes, m_errors, _identifierAccess.resolve)).analyze(m_parsedData))
|
||||
solAssert(false, "Assembly error");
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
return assembly;
|
||||
@ -332,7 +321,7 @@ eth::Assembly assembly::CodeGenerator::assemble(ExternalIdentifierAccess const&
|
||||
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, ExternalIdentifierAccess const& _identifierAccess)
|
||||
{
|
||||
GeneratorState state(m_errors, _assembly);
|
||||
if (!(AsmAnalyzer(state.scopes, m_errors, !!_identifierAccess.resolve)).analyze(m_parsedData))
|
||||
if (!(AsmAnalyzer(state.scopes, m_errors, _identifierAccess.resolve)).analyze(m_parsedData))
|
||||
solAssert(false, "Assembly error");
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
}
|
||||
|
@ -44,9 +44,6 @@ class CodeGenerator
|
||||
public:
|
||||
CodeGenerator(Block const& _parsedData, ErrorList& _errors):
|
||||
m_parsedData(_parsedData), m_errors(_errors) {}
|
||||
/// Performs type checks and @returns false on error.
|
||||
/// Actually runs the full code generation but discards the result.
|
||||
bool typeCheck(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
||||
/// Performs code generation and @returns the result.
|
||||
eth::Assembly assemble(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
||||
/// Performs code generation and appends generated to to _assembly.
|
||||
|
@ -39,7 +39,10 @@ using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
|
||||
bool InlineAssemblyStack::parse(
|
||||
shared_ptr<Scanner> const& _scanner,
|
||||
ExternalIdentifierAccess::Resolver const& _resolver
|
||||
)
|
||||
{
|
||||
m_parserResult = make_shared<Block>();
|
||||
Parser parser(m_errors);
|
||||
@ -49,7 +52,7 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
|
||||
|
||||
*m_parserResult = std::move(*result);
|
||||
AsmAnalyzer::Scopes scopes;
|
||||
return (AsmAnalyzer(scopes, m_errors, false)).analyze(*m_parserResult);
|
||||
return (AsmAnalyzer(scopes, m_errors, _resolver)).analyze(*m_parserResult);
|
||||
}
|
||||
|
||||
string InlineAssemblyStack::toString()
|
||||
|
@ -47,13 +47,15 @@ enum class IdentifierContext { LValue, RValue };
|
||||
/// to inline assembly (not used in standalone assembly mode).
|
||||
struct ExternalIdentifierAccess
|
||||
{
|
||||
using Resolver = std::function<size_t(assembly::Identifier const&, IdentifierContext)>;
|
||||
/// Resolve a an external reference given by the identifier in the given context.
|
||||
/// @returns the size of the value (number of stack slots) or size_t(-1) if not found.
|
||||
std::function<size_t(assembly::Identifier const&, IdentifierContext)> resolve;
|
||||
Resolver resolve;
|
||||
using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)>;
|
||||
/// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context)
|
||||
/// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed
|
||||
/// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack.
|
||||
std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)> generateCode;
|
||||
CodeGenerator generateCode;
|
||||
};
|
||||
|
||||
class InlineAssemblyStack
|
||||
@ -61,7 +63,10 @@ class InlineAssemblyStack
|
||||
public:
|
||||
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
|
||||
/// @return false or error.
|
||||
bool parse(std::shared_ptr<Scanner> const& _scanner);
|
||||
bool parse(
|
||||
std::shared_ptr<Scanner> const& _scanner,
|
||||
ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = ExternalIdentifierAccess::Resolver()
|
||||
);
|
||||
/// Converts the parser result back into a string form (not necessarily the same form
|
||||
/// as the source form, but it should parse into the same parsed form again).
|
||||
std::string toString();
|
||||
|
@ -5058,7 +5058,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage)
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "not found, not unique or not lvalue.");
|
||||
CHECK_ERROR(text, DeclarationError, "Variable not found.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers)
|
||||
|
Loading…
Reference in New Issue
Block a user