mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Split external identifier access into resolving and code generation.
This commit is contained in:
parent
5d6747eb32
commit
e0849f2f3b
@ -158,21 +158,22 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
|
||||
|
||||
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
|
||||
// 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;
|
||||
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);
|
||||
if (declarations.size() != 1)
|
||||
return false;
|
||||
_inlineAssembly.annotation().externalReferences[&_identifier] = declarations.front();
|
||||
// At this stage we neither know the code to generate nor the stack size of the identifier,
|
||||
// so we do not modify assembly.
|
||||
return true;
|
||||
});
|
||||
return size_t(-1);
|
||||
_inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front();
|
||||
// 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);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -628,47 +628,44 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
||||
|
||||
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.
|
||||
auto identifierAccess = [&](
|
||||
// We run the resolve step again regardless.
|
||||
assembly::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
eth::Assembly& _assembly,
|
||||
assembly::CodeGenerator::IdentifierContext _context
|
||||
assembly::IdentifierContext _context
|
||||
)
|
||||
{
|
||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
||||
return false;
|
||||
Declaration const* declaration = ref->second;
|
||||
return size_t(-1);
|
||||
size_t valueSize = size_t(-1);
|
||||
Declaration const* declaration = ref->second.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.");
|
||||
unsigned pushes = 0;
|
||||
if (dynamic_cast<FunctionDefinition const*>(declaration))
|
||||
pushes = 1;
|
||||
valueSize = 1;
|
||||
else if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
if (var->isConstant())
|
||||
fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
|
||||
if (var->isLocalVariable())
|
||||
pushes = var->type()->sizeOnStack();
|
||||
valueSize = var->type()->sizeOnStack();
|
||||
else if (!var->type()->isValueType())
|
||||
pushes = 1;
|
||||
valueSize = 1;
|
||||
else
|
||||
pushes = 2; // slot number, intra slot offset
|
||||
valueSize = 2; // slot number, intra slot offset
|
||||
}
|
||||
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
{
|
||||
if (!contract->isLibrary())
|
||||
return false;
|
||||
pushes = 1;
|
||||
return size_t(-1);
|
||||
valueSize = 1;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
for (unsigned i = 0; i < pushes; ++i)
|
||||
_assembly.append(u256(0)); // just to verify the stack height
|
||||
return size_t(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -676,14 +673,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
if (!varDecl->isLocalVariable())
|
||||
return false; // only local variables are inline-assemlby lvalues
|
||||
for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i)
|
||||
_assembly.append(Instruction::POP); // remove value just to verify the stack height
|
||||
return size_t(-1); // only local variables are inline-assembly lvalues
|
||||
valueSize = size_t(declaration->type()->sizeOnStack());
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return size_t(-1);
|
||||
}
|
||||
return true;
|
||||
ref->second.valueSize = valueSize;
|
||||
return valueSize;
|
||||
};
|
||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
||||
if (!codeGen.typeCheck(identifierAccess))
|
||||
|
@ -117,8 +117,14 @@ struct Identifier; // forward
|
||||
|
||||
struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
{
|
||||
/// Mapping containing resolved references to external identifiers.
|
||||
std::map<assembly::Identifier const*, Declaration const*> externalReferences;
|
||||
struct ExternalIdentifierInfo
|
||||
{
|
||||
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
|
||||
|
@ -265,31 +265,38 @@ void CompilerContext::appendInlineAssembly(
|
||||
}
|
||||
|
||||
unsigned startStackHeight = stackHeight();
|
||||
auto identifierAccess = [&](
|
||||
|
||||
assembly::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](
|
||||
assembly::Identifier const& _identifier,
|
||||
eth::Assembly& _assembly,
|
||||
assembly::CodeGenerator::IdentifierContext _context
|
||||
assembly::IdentifierContext
|
||||
) {
|
||||
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
|
||||
if (it == _localVariables.end())
|
||||
return false;
|
||||
return it == _localVariables.end() ? size_t(-1) : 1;
|
||||
};
|
||||
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;
|
||||
int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
|
||||
if (_context == assembly::CodeGenerator::IdentifierContext::LValue)
|
||||
if (_context == assembly::IdentifierContext::LValue)
|
||||
stackDiff -= 1;
|
||||
if (stackDiff < 1 || stackDiff > 16)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
CompilerError() <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
|
||||
if (_context == assembly::IdentifierContext::RValue)
|
||||
_assembly.append(dupInstruction(stackDiff));
|
||||
else
|
||||
{
|
||||
_assembly.append(swapInstruction(stackDiff));
|
||||
_assembly.append(Instruction::POP);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block.");
|
||||
|
@ -522,91 +522,99 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
ErrorList errors;
|
||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), errors);
|
||||
unsigned startStackHeight = m_context.stackHeight();
|
||||
codeGen.assemble(
|
||||
m_context.nonConstAssembly(),
|
||||
[&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
|
||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
||||
return false;
|
||||
Declaration const* decl = ref->second;
|
||||
solAssert(!!decl, "");
|
||||
if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
|
||||
assembly::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext)
|
||||
{
|
||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
||||
return size_t(-1);
|
||||
return ref->second.valueSize;
|
||||
};
|
||||
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.");
|
||||
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
|
||||
functionDef = &m_context.resolveVirtualFunction(*functionDef);
|
||||
_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(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())
|
||||
{
|
||||
_assembly.append(u256(1) << 32);
|
||||
_assembly.append(Instruction::MUL);
|
||||
_assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
|
||||
_assembly.append(Instruction::OR);
|
||||
}
|
||||
_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(), "");
|
||||
if (m_context.isLocalVariable(variable))
|
||||
int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*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);
|
||||
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));
|
||||
solAssert(location.second == 0, "Intra-slot offest assumed to be zero.");
|
||||
_assembly.append(location.first);
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(m_context.isStateVariable(variable), "Invalid variable type.");
|
||||
auto const& location = m_context.storageLocationOfVariable(*variable);
|
||||
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));
|
||||
}
|
||||
_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.");
|
||||
m_context.setStackOffset(startStackHeight);
|
||||
|
@ -81,7 +81,7 @@ public:
|
||||
explicit CodeTransform(
|
||||
GeneratorState& _state,
|
||||
assembly::Block const& _block,
|
||||
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
|
||||
assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
|
||||
):
|
||||
m_state(_state),
|
||||
m_scope(*m_state.scopes.at(&_block)),
|
||||
@ -160,15 +160,23 @@ public:
|
||||
{
|
||||
return;
|
||||
}
|
||||
solAssert(m_identifierAccess, "Identifier not found and no external access available.");
|
||||
if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
|
||||
solAssert(
|
||||
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(
|
||||
Error::Type::DeclarationError,
|
||||
"Identifier not found or not unique",
|
||||
_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)
|
||||
@ -236,12 +244,20 @@ private:
|
||||
m_state.assembly.append(solidity::Instruction::POP);
|
||||
return;
|
||||
}
|
||||
solAssert(m_identifierAccess, "Identifier not found and no external access available.");
|
||||
if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
|
||||
solAssert(
|
||||
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(
|
||||
Error::Type::DeclarationError,
|
||||
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines the stack height difference to the given variables. Automatically generates
|
||||
@ -289,34 +305,34 @@ private:
|
||||
GeneratorState& m_state;
|
||||
Scope& m_scope;
|
||||
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();
|
||||
eth::Assembly 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;
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
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;
|
||||
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");
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
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);
|
||||
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");
|
||||
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||
}
|
||||
|
@ -22,8 +22,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/inlineasm/AsmStack.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -36,27 +38,19 @@ namespace solidity
|
||||
namespace assembly
|
||||
{
|
||||
struct Block;
|
||||
struct Identifier;
|
||||
|
||||
class CodeGenerator
|
||||
{
|
||||
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):
|
||||
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(IdentifierAccess const& _identifierAccess = IdentifierAccess());
|
||||
bool typeCheck(ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
||||
/// 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.
|
||||
void assemble(eth::Assembly& _assembly, IdentifierAccess const& _identifierAccess = IdentifierAccess());
|
||||
void assemble(eth::Assembly& _assembly, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess());
|
||||
|
||||
private:
|
||||
Block const& m_parsedData;
|
||||
|
@ -66,7 +66,7 @@ eth::Assembly InlineAssemblyStack::assemble()
|
||||
bool InlineAssemblyStack::parseAndAssemble(
|
||||
string const& _input,
|
||||
eth::Assembly& _assembly,
|
||||
CodeGenerator::IdentifierAccess const& _identifierAccess
|
||||
ExternalIdentifierAccess const& _identifierAccess
|
||||
)
|
||||
{
|
||||
ErrorList errors;
|
||||
|
@ -22,10 +22,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -39,6 +39,22 @@ class Scanner;
|
||||
namespace assembly
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -56,7 +72,7 @@ public:
|
||||
bool parseAndAssemble(
|
||||
std::string const& _input,
|
||||
eth::Assembly& _assembly,
|
||||
CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess()
|
||||
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
|
||||
);
|
||||
|
||||
ErrorList const& errors() const { return m_errors; }
|
||||
|
Loading…
Reference in New Issue
Block a user