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/analysis/NameAndTypeResolver.h>
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
#include <libsolidity/analysis/ConstantEvaluator.h>
|
#include <libsolidity/analysis/ConstantEvaluator.h>
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||||
#include <libsolidity/inlineasm/AsmData.h>
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
using namespace std;
|
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
|
// The only purpose of this step is to fill the inline assembly annotation with
|
||||||
// external references.
|
// external references.
|
||||||
ErrorList errorsIgnored;
|
ErrorList errorsIgnored;
|
||||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errorsIgnored);
|
assembly::ExternalIdentifierAccess::Resolver resolver =
|
||||||
assembly::ExternalIdentifierAccess identifierAccess;
|
[&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
|
||||||
identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
|
|
||||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
|
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
|
||||||
if (declarations.size() != 1)
|
if (declarations.size() != 1)
|
||||||
return size_t(-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.
|
// At this stage we do not yet know the stack size of the identifier, so we just return 1.
|
||||||
return size_t(1);
|
return size_t(1);
|
||||||
};
|
};
|
||||||
codeGen.typeCheck(identifierAccess);
|
assembly::AsmAnalyzer::Scopes scopes;
|
||||||
|
assembly::AsmAnalyzer(scopes, errorsIgnored, resolver).analyze(_inlineAssembly.operations());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libevmasm/Assembly.h> // needed for inline assembly
|
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
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.
|
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||||
// We run the resolve step again regardless.
|
// We run the resolve step again regardless.
|
||||||
assembly::ExternalIdentifierAccess identifierAccess;
|
assembly::ExternalIdentifierAccess::Resolver identifierAccess = [&](
|
||||||
identifierAccess.resolve = [&](
|
|
||||||
assembly::Identifier const& _identifier,
|
assembly::Identifier const& _identifier,
|
||||||
assembly::IdentifierContext _context
|
assembly::IdentifierContext _context
|
||||||
)
|
)
|
||||||
@ -682,8 +680,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
ref->second.valueSize = valueSize;
|
ref->second.valueSize = valueSize;
|
||||||
return valueSize;
|
return valueSize;
|
||||||
};
|
};
|
||||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
assembly::AsmAnalyzer::Scopes scopes;
|
||||||
if (!codeGen.typeCheck(identifierAccess))
|
assembly::AsmAnalyzer analyzer(scopes, m_errors, identifierAccess);
|
||||||
|
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,12 @@ using namespace dev::solidity;
|
|||||||
using namespace dev::solidity::assembly;
|
using namespace dev::solidity::assembly;
|
||||||
|
|
||||||
|
|
||||||
AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors, bool _allowFailedLookups):
|
AsmAnalyzer::AsmAnalyzer(
|
||||||
m_allowFailedLookups(_allowFailedLookups), m_scopes(_scopes), m_errors(_errors)
|
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>(
|
m_errors.push_back(make_shared<Error>(
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
@ -223,10 +227,11 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable)
|
|||||||
{
|
{
|
||||||
if (!(*this)(_variable))
|
if (!(*this)(_variable))
|
||||||
return false;
|
return false;
|
||||||
else if (!m_allowFailedLookups)
|
|
||||||
|
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||||
{
|
{
|
||||||
// Check that it is a variable
|
// 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>(
|
m_errors.push_back(make_shared<Error>(
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
@ -236,6 +241,15 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable)
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmStack.h>
|
||||||
|
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
@ -57,9 +59,7 @@ class AsmAnalyzer: public boost::static_visitor<bool>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
|
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
|
||||||
/// @param _allowFailedLookups if true, allow failed lookups for variables (they
|
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors, ExternalIdentifierAccess::Resolver const& _resolver);
|
||||||
/// will be provided from the environment later on)
|
|
||||||
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors, bool _allowFailedLookups);
|
|
||||||
|
|
||||||
bool analyze(assembly::Block const& _block);
|
bool analyze(assembly::Block const& _block);
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ private:
|
|||||||
bool checkAssignment(assembly::Identifier const& _assignment);
|
bool checkAssignment(assembly::Identifier const& _assignment);
|
||||||
Scope& scope(assembly::Block const* _block);
|
Scope& scope(assembly::Block const* _block);
|
||||||
|
|
||||||
bool m_allowFailedLookups = false;
|
ExternalIdentifierAccess::Resolver const& m_resolver;
|
||||||
Scope* m_currentScope = nullptr;
|
Scope* m_currentScope = nullptr;
|
||||||
Scopes& m_scopes;
|
Scopes& m_scopes;
|
||||||
ErrorList& m_errors;
|
ErrorList& m_errors;
|
||||||
|
@ -308,22 +308,11 @@ private:
|
|||||||
ExternalIdentifierAccess m_identifierAccess;
|
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::CodeGenerator::assemble(ExternalIdentifierAccess const& _identifierAccess)
|
||||||
{
|
{
|
||||||
eth::Assembly assembly;
|
eth::Assembly assembly;
|
||||||
GeneratorState state(m_errors, 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");
|
solAssert(false, "Assembly error");
|
||||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||||
return assembly;
|
return assembly;
|
||||||
@ -332,7 +321,7 @@ eth::Assembly assembly::CodeGenerator::assemble(ExternalIdentifierAccess const&
|
|||||||
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, ExternalIdentifierAccess const& _identifierAccess)
|
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, ExternalIdentifierAccess const& _identifierAccess)
|
||||||
{
|
{
|
||||||
GeneratorState state(m_errors, _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");
|
solAssert(false, "Assembly error");
|
||||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,6 @@ class CodeGenerator
|
|||||||
public:
|
public:
|
||||||
CodeGenerator(Block const& _parsedData, ErrorList& _errors):
|
CodeGenerator(Block const& _parsedData, ErrorList& _errors):
|
||||||
m_parsedData(_parsedData), m_errors(_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.
|
/// Performs code generation and @returns the result.
|
||||||
eth::Assembly assemble(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
eth::Assembly assemble(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
||||||
/// Performs code generation and appends generated to to _assembly.
|
/// Performs code generation and appends generated to to _assembly.
|
||||||
|
@ -39,7 +39,10 @@ using namespace dev;
|
|||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
using namespace dev::solidity::assembly;
|
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>();
|
m_parserResult = make_shared<Block>();
|
||||||
Parser parser(m_errors);
|
Parser parser(m_errors);
|
||||||
@ -49,7 +52,7 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
|
|
||||||
*m_parserResult = std::move(*result);
|
*m_parserResult = std::move(*result);
|
||||||
AsmAnalyzer::Scopes scopes;
|
AsmAnalyzer::Scopes scopes;
|
||||||
return (AsmAnalyzer(scopes, m_errors, false)).analyze(*m_parserResult);
|
return (AsmAnalyzer(scopes, m_errors, _resolver)).analyze(*m_parserResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
string InlineAssemblyStack::toString()
|
string InlineAssemblyStack::toString()
|
||||||
|
@ -47,13 +47,15 @@ enum class IdentifierContext { LValue, RValue };
|
|||||||
/// to inline assembly (not used in standalone assembly mode).
|
/// to inline assembly (not used in standalone assembly mode).
|
||||||
struct ExternalIdentifierAccess
|
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.
|
/// 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.
|
/// @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)
|
/// 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
|
/// 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.
|
/// 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
|
class InlineAssemblyStack
|
||||||
@ -61,7 +63,10 @@ class InlineAssemblyStack
|
|||||||
public:
|
public:
|
||||||
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
|
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
|
||||||
/// @return false or error.
|
/// @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
|
/// 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).
|
/// as the source form, but it should parse into the same parsed form again).
|
||||||
std::string toString();
|
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)
|
BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers)
|
||||||
|
Loading…
Reference in New Issue
Block a user