Split external identifier access into resolving and code generation.

This commit is contained in:
chriseth 2017-03-14 15:41:23 +01:00 committed by chriseth
parent 5d6747eb32
commit e0849f2f3b
9 changed files with 189 additions and 144 deletions

View File

@ -158,21 +158,22 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{ {
// We need to perform a full code generation pass here as inline assembly does not distinguish
// reference resolution and code generation.
// Errors created in this stage are completely ignored because we do not yet know // Errors created in this stage are completely ignored because we do not yet know
// the type and size of external identifiers, which would result in false errors. // the type and size of external identifiers, which would result in false errors.
// The only purpose of this step is to fill the inline assembly annotation with
// external references.
ErrorList errorsIgnored; ErrorList errorsIgnored;
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errorsIgnored); assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errorsIgnored);
codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly&, assembly::CodeGenerator::IdentifierContext) { assembly::ExternalIdentifierAccess identifierAccess;
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 false; return size_t(-1);
_inlineAssembly.annotation().externalReferences[&_identifier] = declarations.front(); _inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front();
// At this stage we neither know the code to generate nor the stack size of the identifier, // At this stage we do not yet know the stack size of the identifier, so we just return 1.
// so we do not modify assembly. return size_t(1);
return true; };
}); codeGen.typeCheck(identifierAccess);
return false; return false;
} }

View File

@ -628,47 +628,44 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{ {
// Inline assembly does not have its own type-checking phase, so we just run the
// code-generator and see whether it produces any errors.
// 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.
auto identifierAccess = [&]( // We run the resolve step again regardless.
assembly::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&](
assembly::Identifier const& _identifier, assembly::Identifier const& _identifier,
eth::Assembly& _assembly, assembly::IdentifierContext _context
assembly::CodeGenerator::IdentifierContext _context
) )
{ {
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end()) if (ref == _inlineAssembly.annotation().externalReferences.end())
return false; return size_t(-1);
Declaration const* declaration = ref->second; size_t valueSize = size_t(-1);
Declaration const* declaration = ref->second.declaration;
solAssert(!!declaration, ""); solAssert(!!declaration, "");
if (_context == assembly::CodeGenerator::IdentifierContext::RValue) if (_context == assembly::IdentifierContext::RValue)
{ {
solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); solAssert(!!declaration->type(), "Type of declaration required but not yet determined.");
unsigned pushes = 0;
if (dynamic_cast<FunctionDefinition const*>(declaration)) if (dynamic_cast<FunctionDefinition const*>(declaration))
pushes = 1; valueSize = 1;
else if (auto var = dynamic_cast<VariableDeclaration const*>(declaration)) else if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{ {
if (var->isConstant()) if (var->isConstant())
fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly."); fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
if (var->isLocalVariable()) if (var->isLocalVariable())
pushes = var->type()->sizeOnStack(); valueSize = var->type()->sizeOnStack();
else if (!var->type()->isValueType()) else if (!var->type()->isValueType())
pushes = 1; valueSize = 1;
else else
pushes = 2; // slot number, intra slot offset valueSize = 2; // slot number, intra slot offset
} }
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{ {
if (!contract->isLibrary()) if (!contract->isLibrary())
return false; return size_t(-1);
pushes = 1; valueSize = 1;
} }
else else
return false; return size_t(-1);
for (unsigned i = 0; i < pushes; ++i)
_assembly.append(u256(0)); // just to verify the stack height
} }
else else
{ {
@ -676,14 +673,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) if (auto varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
{ {
if (!varDecl->isLocalVariable()) if (!varDecl->isLocalVariable())
return false; // only local variables are inline-assemlby lvalues return size_t(-1); // only local variables are inline-assembly lvalues
for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i) valueSize = size_t(declaration->type()->sizeOnStack());
_assembly.append(Instruction::POP); // remove value just to verify the stack height
} }
else else
return false; return size_t(-1);
} }
return true; ref->second.valueSize = valueSize;
return valueSize;
}; };
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors); assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
if (!codeGen.typeCheck(identifierAccess)) if (!codeGen.typeCheck(identifierAccess))

View File

@ -117,8 +117,14 @@ struct Identifier; // forward
struct InlineAssemblyAnnotation: StatementAnnotation struct InlineAssemblyAnnotation: StatementAnnotation
{ {
/// Mapping containing resolved references to external identifiers. struct ExternalIdentifierInfo
std::map<assembly::Identifier const*, Declaration const*> externalReferences; {
Declaration const* declaration = nullptr;
size_t valueSize = size_t(-1);
};
/// Mapping containing resolved references to external identifiers and their value size
std::map<assembly::Identifier const*, ExternalIdentifierInfo> externalReferences;
}; };
struct ReturnAnnotation: StatementAnnotation struct ReturnAnnotation: StatementAnnotation

View File

@ -265,31 +265,38 @@ void CompilerContext::appendInlineAssembly(
} }
unsigned startStackHeight = stackHeight(); unsigned startStackHeight = stackHeight();
auto identifierAccess = [&](
assembly::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&](
assembly::Identifier const& _identifier, assembly::Identifier const& _identifier,
eth::Assembly& _assembly, assembly::IdentifierContext
assembly::CodeGenerator::IdentifierContext _context
) { ) {
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
if (it == _localVariables.end()) return it == _localVariables.end() ? size_t(-1) : 1;
return false; };
identifierAccess.generateCode = [&](
assembly::Identifier const& _identifier,
assembly::IdentifierContext _context,
eth::Assembly& _assembly
) {
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
solAssert(it != _localVariables.end(), "");
unsigned stackDepth = _localVariables.end() - it; unsigned stackDepth = _localVariables.end() - it;
int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
if (_context == assembly::CodeGenerator::IdentifierContext::LValue) if (_context == assembly::IdentifierContext::LValue)
stackDiff -= 1; stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16) if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
CompilerError() << CompilerError() <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );
if (_context == assembly::CodeGenerator::IdentifierContext::RValue) if (_context == assembly::IdentifierContext::RValue)
_assembly.append(dupInstruction(stackDiff)); _assembly.append(dupInstruction(stackDiff));
else else
{ {
_assembly.append(swapInstruction(stackDiff)); _assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP); _assembly.append(Instruction::POP);
} }
return true;
}; };
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block."); solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block.");

View File

@ -522,91 +522,99 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
ErrorList errors; ErrorList errors;
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors); assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors);
unsigned startStackHeight = m_context.stackHeight(); unsigned startStackHeight = m_context.stackHeight();
codeGen.assemble( assembly::ExternalIdentifierAccess identifierAccess;
m_context.nonConstAssembly(), identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext)
[&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) { {
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end()) if (ref == _inlineAssembly.annotation().externalReferences.end())
return false; return size_t(-1);
Declaration const* decl = ref->second; return ref->second.valueSize;
solAssert(!!decl, ""); };
if (_context == assembly::CodeGenerator::IdentifierContext::RValue) identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, eth::Assembly& _assembly)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), "");
Declaration const* decl = ref->second.declaration;
solAssert(!!decl, "");
if (_context == assembly::IdentifierContext::RValue)
{
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
{ {
solAssert(!!decl->type(), "Type of declaration required but not yet determined."); functionDef = &m_context.resolveVirtualFunction(*functionDef);
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl)) _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
{ {
functionDef = &m_context.resolveVirtualFunction(*functionDef); _assembly.append(u256(1) << 32);
_assembly.append(m_context.functionEntryLabel(*functionDef).pushTag()); _assembly.append(Instruction::MUL);
// If there is a runtime context, we have to merge both labels into the same _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
// stack slot in case we store it in storage. _assembly.append(Instruction::OR);
if (CompilerContext* rtc = m_context.runtimeContext())
{
_assembly.append(u256(1) << 32);
_assembly.append(Instruction::MUL);
_assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
_assembly.append(Instruction::OR);
}
} }
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl)) }
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->isConstant(), "");
if (m_context.isLocalVariable(variable))
{ {
solAssert(!variable->isConstant(), ""); int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
if (m_context.isLocalVariable(variable)) if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
_assembly.append(dupInstruction(stackDiff));
}
else
{
solAssert(m_context.isStateVariable(variable), "Invalid variable type.");
auto const& location = m_context.storageLocationOfVariable(*variable);
if (!variable->type()->isValueType())
{ {
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable); solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
if (stackDiff < 1 || stackDiff > 16) _assembly.append(location.first);
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < variable->type()->sizeOnStack(); ++i)
_assembly.append(dupInstruction(stackDiff));
} }
else else
{ {
solAssert(m_context.isStateVariable(variable), "Invalid variable type."); _assembly.append(location.first);
auto const& location = m_context.storageLocationOfVariable(*variable); _assembly.append(u256(location.second));
if (!variable->type()->isValueType())
{
solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
_assembly.append(location.first);
}
else
{
_assembly.append(location.first);
_assembly.append(u256(location.second));
}
} }
} }
else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
{
solAssert(contract->isLibrary(), "");
_assembly.appendLibraryAddress(contract->fullyQualifiedName());
}
else
solAssert(false, "Invalid declaration type.");
} else {
// lvalue context
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
unsigned size = variable->type()->sizeOnStack();
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i) {
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
} }
return true; else if (auto contract = dynamic_cast<ContractDefinition const*>(decl))
{
solAssert(contract->isLibrary(), "");
_assembly.appendLibraryAddress(contract->fullyQualifiedName());
}
else
solAssert(false, "Invalid declaration type.");
} else {
// lvalue context
auto variable = dynamic_cast<VariableDeclaration const*>(decl);
solAssert(
!!variable && m_context.isLocalVariable(variable),
"Can only assign to stack variables in inline assembly."
);
unsigned size = variable->type()->sizeOnStack();
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - size;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
for (unsigned i = 0; i < size; ++i) {
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
} }
};
codeGen.assemble(
m_context.nonConstAssembly(),
identifierAccess
); );
solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested."); solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested.");
m_context.setStackOffset(startStackHeight); m_context.setStackOffset(startStackHeight);

View File

@ -81,7 +81,7 @@ public:
explicit CodeTransform( explicit CodeTransform(
GeneratorState& _state, GeneratorState& _state,
assembly::Block const& _block, assembly::Block const& _block,
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess() assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
): ):
m_state(_state), m_state(_state),
m_scope(*m_state.scopes.at(&_block)), m_scope(*m_state.scopes.at(&_block)),
@ -160,15 +160,23 @@ public:
{ {
return; return;
} }
solAssert(m_identifierAccess, "Identifier not found and no external access available."); solAssert(
if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue)) m_identifierAccess.resolve && m_identifierAccess.generateCode,
"Identifier not found and no external access available."
);
// @TODO refactor: Store resolved identifier.
size_t size = m_identifierAccess.resolve(_identifier, IdentifierContext::RValue);
if (size != size_t(-1))
m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly);
else
{ {
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
"Identifier not found or not unique", "Identifier not found or not unique",
_identifier.location _identifier.location
); );
m_state.assembly.append(u256(0)); for (size_t i = 0; i < size; ++i)
m_state.assembly.append(u256(0));
} }
} }
void operator()(FunctionalInstruction const& _instr) void operator()(FunctionalInstruction const& _instr)
@ -236,12 +244,20 @@ private:
m_state.assembly.append(solidity::Instruction::POP); m_state.assembly.append(solidity::Instruction::POP);
return; return;
} }
solAssert(m_identifierAccess, "Identifier not found and no external access available."); solAssert(
if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue)) m_identifierAccess.resolve && m_identifierAccess.generateCode,
"Identifier not found and no external access available."
);
size_t size = m_identifierAccess.resolve(_variableName, IdentifierContext::LValue);
if (size != size_t(-1))
m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly);
else
{
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue." "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
); );
}
} }
/// Determines the stack height difference to the given variables. Automatically generates /// Determines the stack height difference to the given variables. Automatically generates
@ -289,34 +305,34 @@ private:
GeneratorState& m_state; GeneratorState& m_state;
Scope& m_scope; Scope& m_scope;
int const m_initialDeposit; int const m_initialDeposit;
assembly::CodeGenerator::IdentifierAccess m_identifierAccess; ExternalIdentifierAccess m_identifierAccess;
}; };
bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) bool assembly::CodeGenerator::typeCheck(ExternalIdentifierAccess const& _identifierAccess)
{ {
size_t initialErrorLen = m_errors.size(); size_t initialErrorLen = m_errors.size();
eth::Assembly assembly; eth::Assembly assembly;
GeneratorState state(m_errors, assembly); GeneratorState state(m_errors, assembly);
if (!(AsmAnalyzer(state.scopes, m_errors, !!_identifierAccess)).analyze(m_parsedData)) if (!(AsmAnalyzer(state.scopes, m_errors, !!_identifierAccess.resolve)).analyze(m_parsedData))
return false; return false;
CodeTransform(state, m_parsedData, _identifierAccess); CodeTransform(state, m_parsedData, _identifierAccess);
return m_errors.size() == initialErrorLen; return m_errors.size() == initialErrorLen;
} }
eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess 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)).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;
} }
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess 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)).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);
} }

View File

@ -22,8 +22,10 @@
#pragma once #pragma once
#include <functional>
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
#include <libsolidity/inlineasm/AsmStack.h>
#include <functional>
namespace dev namespace dev
{ {
@ -36,27 +38,19 @@ namespace solidity
namespace assembly namespace assembly
{ {
struct Block; struct Block;
struct Identifier;
class CodeGenerator class CodeGenerator
{ {
public: public:
enum class IdentifierContext { LValue, RValue };
/// Function type that is called for external identifiers. Such a function should search for
/// the identifier and append appropriate assembly items to the assembly. If in lvalue context,
/// the value to assign is assumed to be on the stack and an assignment is to be performed.
/// If in rvalue context, the function is assumed to append instructions to
/// push the value of the identifier onto the stack. On error, the function should return false.
using IdentifierAccess = std::function<bool(assembly::Identifier const&, eth::Assembly&, IdentifierContext)>;
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. /// Performs type checks and @returns false on error.
/// Actually runs the full code generation but discards the result. /// Actually runs the full code generation but discards the result.
bool typeCheck(IdentifierAccess const& _identifierAccess = IdentifierAccess()); bool typeCheck(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
/// Performs code generation and @returns the result. /// Performs code generation and @returns the result.
eth::Assembly assemble(IdentifierAccess const& _identifierAccess = IdentifierAccess()); 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.
void assemble(eth::Assembly& _assembly, IdentifierAccess const& _identifierAccess = IdentifierAccess()); void assemble(eth::Assembly& _assembly, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
private: private:
Block const& m_parsedData; Block const& m_parsedData;

View File

@ -66,7 +66,7 @@ eth::Assembly InlineAssemblyStack::assemble()
bool InlineAssemblyStack::parseAndAssemble( bool InlineAssemblyStack::parseAndAssemble(
string const& _input, string const& _input,
eth::Assembly& _assembly, eth::Assembly& _assembly,
CodeGenerator::IdentifierAccess const& _identifierAccess ExternalIdentifierAccess const& _identifierAccess
) )
{ {
ErrorList errors; ErrorList errors;

View File

@ -22,10 +22,10 @@
#pragma once #pragma once
#include <libsolidity/interface/Exceptions.h>
#include <string> #include <string>
#include <functional> #include <functional>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
namespace dev namespace dev
{ {
@ -39,6 +39,22 @@ class Scanner;
namespace assembly namespace assembly
{ {
struct Block; struct Block;
struct Identifier;
enum class IdentifierContext { LValue, RValue };
/// Object that is used to resolve references and generate code for access to identifiers external
/// to inline assembly (not used in standalone assembly mode).
struct ExternalIdentifierAccess
{
/// 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;
/// 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;
};
class InlineAssemblyStack class InlineAssemblyStack
{ {
@ -56,7 +72,7 @@ public:
bool parseAndAssemble( bool parseAndAssemble(
std::string const& _input, std::string const& _input,
eth::Assembly& _assembly, eth::Assembly& _assembly,
CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess() ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
); );
ErrorList const& errors() const { return m_errors; } ErrorList const& errors() const { return m_errors; }