Use actual type checking phase of assembler.

This commit is contained in:
chriseth 2017-03-21 19:38:37 +01:00
parent e0849f2f3b
commit c6fa78c73e
9 changed files with 49 additions and 42 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()

View File

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

View File

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