aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2016-11-18 00:32:21 +0800
committerGitHub <noreply@github.com>2016-11-18 00:32:21 +0800
commitb46a14f4a8e128c08336763abf8bbf7c111f464d (patch)
treeee20fa35cbc19eddcddd1013e745b387e7371be3 /libsolidity/codegen
parentc811691861eb51520d9fd51d56770f14990b0320 (diff)
parent2c14a96820233809db4360b39f5f02039be5730a (diff)
downloaddexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar.gz
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar.bz2
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar.lz
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar.xz
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.tar.zst
dexon-solidity-b46a14f4a8e128c08336763abf8bbf7c111f464d.zip
Merge pull request #1122 from ethereum/firstClassFunctions
Functions as first-class types.
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/Compiler.cpp17
-rw-r--r--libsolidity/codegen/Compiler.h8
-rw-r--r--libsolidity/codegen/CompilerContext.cpp30
-rw-r--r--libsolidity/codegen/CompilerContext.h84
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp94
-rw-r--r--libsolidity/codegen/CompilerUtils.h11
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp53
-rw-r--r--libsolidity/codegen/ContractCompiler.h12
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp36
-rw-r--r--libsolidity/codegen/LValue.cpp48
10 files changed, 285 insertions, 108 deletions
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index 1ae5dda9..54639515 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -33,14 +33,15 @@ void Compiler::compileContract(
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
)
{
- ContractCompiler runtimeCompiler(m_runtimeContext, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
runtimeCompiler.compileContract(_contract, _contracts);
- ContractCompiler creationCompiler(m_context, m_optimize);
- m_runtimeSub = creationCompiler.compileConstructor(m_runtimeContext, _contract, _contracts);
+ // This might modify m_runtimeContext because it can access runtime functions at
+ // creation time.
+ ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
+ m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
- if (m_optimize)
- m_context.optimise(m_optimizeRuns);
+ m_context.optimise(m_optimize, m_optimizeRuns);
if (_contract.isLibrary())
{
@@ -54,11 +55,11 @@ void Compiler::compileClone(
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
)
{
- ContractCompiler cloneCompiler(m_context, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
+ ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
- if (m_optimize)
- m_context.optimise(m_optimizeRuns);
+ m_context.optimise(m_optimize, m_optimizeRuns);
}
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index fccb68a9..4a87de0e 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -35,7 +35,9 @@ class Compiler
public:
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_optimize(_optimize),
- m_optimizeRuns(_runs)
+ m_optimizeRuns(_runs),
+ m_runtimeContext(),
+ m_context(&m_runtimeContext)
{ }
void compileContract(
@@ -69,9 +71,9 @@ public:
private:
bool const m_optimize;
unsigned const m_optimizeRuns;
- CompilerContext m_context;
- size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
CompilerContext m_runtimeContext;
+ size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
+ CompilerContext m_context;
};
}
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 3ac5bd3c..c76db2a6 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -60,8 +60,8 @@ void CompilerContext::startFunction(Declaration const& _function)
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
- solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
- m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
+ solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
+ m_localVariables[&_declaration] = unsigned(m_asm->deposit()) - _offsetToCurrent;
}
void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
@@ -92,22 +92,22 @@ eth::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const&
return m_functionCompilationQueue.entryLabelIfExists(_declaration);
}
-eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(FunctionDefinition const& _function)
+FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function)
{
// Libraries do not allow inheritance and their functions can be inlined, so we should not
// search the inheritance hierarchy (which will be the wrong one in case the function
// is inlined).
if (auto scope = dynamic_cast<ContractDefinition const*>(_function.scope()))
if (scope->isLibrary())
- return functionEntryLabel(_function);
+ return _function;
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
- return virtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin());
+ return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin());
}
-eth::AssemblyItem CompilerContext::superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base)
+FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base)
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
- return virtualFunctionEntryLabel(_function, superContract(_base));
+ return resolveVirtualFunction(_function, superContract(_base));
}
FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const
@@ -145,12 +145,12 @@ unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declarat
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
{
- return m_asm.deposit() - _baseOffset - 1;
+ return m_asm->deposit() - _baseOffset - 1;
}
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
{
- return m_asm.deposit() - _offset - 1;
+ return m_asm->deposit() - _offset - 1;
}
pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const
@@ -217,17 +217,17 @@ void CompilerContext::appendInlineAssembly(
return true;
};
- solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, m_asm, identifierAccess), "");
+ solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "");
}
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
{
- eth::Assembly& sub = m_asm.sub(_subIndex);
+ eth::Assembly& sub = m_asm->sub(_subIndex);
sub.injectStart(Instruction::POP);
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
}
-eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
+FunctionDefinition const& CompilerContext::resolveVirtualFunction(
FunctionDefinition const& _function,
vector<ContractDefinition const*>::const_iterator _searchStart
)
@@ -242,9 +242,9 @@ eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
!function->isConstructor() &&
FunctionType(*function).hasEqualArgumentTypes(functionType)
)
- return functionEntryLabel(*function);
+ return *function;
solAssert(false, "Super function " + name + " not found.");
- return m_asm.newTag(); // not reached
+ return _function; // not reached
}
vector<ContractDefinition const*>::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const
@@ -257,7 +257,7 @@ vector<ContractDefinition const*>::const_iterator CompilerContext::superContract
void CompilerContext::updateSourceLocation()
{
- m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
+ m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
}
eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 0c1500b0..8ccbddfd 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -44,6 +44,14 @@ namespace solidity {
class CompilerContext
{
public:
+ CompilerContext(CompilerContext* _runtimeContext = nullptr):
+ m_asm(std::make_shared<eth::Assembly>()),
+ m_runtimeContext(_runtimeContext)
+ {
+ if (m_runtimeContext)
+ m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
+ }
+
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
@@ -52,9 +60,9 @@ public:
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
- void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
- void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
- unsigned stackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
+ void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
+ void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
+ unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;
@@ -67,10 +75,10 @@ public:
eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @returns the entry label of the given function and takes overrides into account.
- eth::AssemblyItem virtualFunctionEntryLabel(FunctionDefinition const& _function);
- /// @returns the entry label of a function that overrides the given declaration from the most derived class just
+ FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function);
+ /// @returns the function that overrides the given declaration from the most derived class just
/// above _base in the current inheritance hierarchy.
- eth::AssemblyItem superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base);
+ FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base);
FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const;
/// @returns the next function in the queue of functions that are still to be compiled
@@ -95,30 +103,33 @@ public:
std::pair<u256, unsigned> storageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag
- eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
+ eth::AssemblyItem appendConditionalJump() { return m_asm->appendJumpI().tag(); }
/// Appends a JUMPI instruction to @a _tag
- CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
+ CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJumpI(_tag); return *this; }
/// Appends a JUMP to a new tag and @returns the tag
- eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
+ eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Returns an "ErrorTag"
- eth::AssemblyItem errorTag() { return m_asm.errorTag(); }
+ eth::AssemblyItem errorTag() { return m_asm->errorTag(); }
/// Appends a JUMP to a specific tag
- CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
+ CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
- eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
+ eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
/// @returns a new tag without pushing any opcodes or data
- eth::AssemblyItem newTag() { return m_asm.newTag(); }
+ eth::AssemblyItem newTag() { return m_asm->newTag(); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
- /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
- eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
+ /// on the stack. @returns the pushsub assembly item.
+ eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { auto sub = m_asm->newSub(_assembly); m_asm->append(m_asm->newPushSubSize(size_t(sub.data()))); return sub; }
+ void pushSubroutineSize(size_t _subRoutine) { m_asm->append(m_asm->newPushSubSize(_subRoutine)); }
+ /// Pushes the offset of the subroutine.
+ void pushSubroutineOffset(size_t _subRoutine) { m_asm->append(eth::AssemblyItem(eth::PushSub, _subRoutine)); }
/// Pushes the size of the final program
- void appendProgramSize() { return m_asm.appendProgramSize(); }
+ void appendProgramSize() { m_asm->appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
- eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
+ eth::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); }
/// Appends the address (virtual, will be filled in by linker) of a library.
- void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); }
+ void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); }
/// Resets the stack of visited nodes with a new stack having only @c _node
void resetVisitedNodes(ASTNode const* _node);
/// Pops the stack of visited nodes
@@ -127,10 +138,10 @@ public:
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
/// Append elements to the current instruction list and adjust @a m_stackOffset.
- CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
- CompilerContext& operator<<(Instruction _instruction) { m_asm.append(_instruction); return *this; }
- CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
- CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
+ CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
+ CompilerContext& operator<<(Instruction _instruction) { m_asm->append(_instruction); return *this; }
+ CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
+ CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
/// prior to parsing the inline assembly.
@@ -144,22 +155,27 @@ public:
/// Prepends "PUSH <compiler version number> POP"
void injectVersionStampIntoSub(size_t _subIndex);
- void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
+ void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
+
+ /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
+ CompilerContext* runtimeContext() { return m_runtimeContext; }
+ /// @returns the identifier of the runtime subroutine.
+ size_t runtimeSub() const { return m_runtimeSub; }
- eth::Assembly const& assembly() const { return m_asm; }
+ eth::Assembly const& assembly() const { return *m_asm; }
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
/// wrappers in this class.
- eth::Assembly& nonConstAssembly() { return m_asm; }
+ eth::Assembly& nonConstAssembly() { return *m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFormat shows whether the out should be in Json format
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
{
- return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
+ return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat);
}
- eth::LinkerObject const& assembledObject() { return m_asm.assemble(); }
- eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm.sub(_subIndex).assemble(); }
+ eth::LinkerObject const& assembledObject() { return m_asm->assemble(); }
+ eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm->sub(_subIndex).assemble(); }
/**
* Helper class to pop the visited nodes stack when a scope closes
@@ -172,9 +188,9 @@ public:
};
private:
- /// @returns the entry label of the given function - searches the inheritance hierarchy
- /// startig from the given point towards the base.
- eth::AssemblyItem virtualFunctionEntryLabel(
+ /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns
+ /// the first function definition that is overwritten by _function.
+ FunctionDefinition const& resolveVirtualFunction(
FunctionDefinition const& _function,
std::vector<ContractDefinition const*>::const_iterator _searchStart
);
@@ -215,7 +231,7 @@ private:
mutable std::queue<Declaration const*> m_functionsToCompile;
} m_functionCompilationQueue;
- eth::Assembly m_asm;
+ eth::AssemblyPointer m_asm;
/// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals;
/// Other already compiled contracts to be used in contract creation calls.
@@ -228,6 +244,10 @@ private:
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
/// Stack of current visited AST nodes, used for location attachment
std::stack<ASTNode const*> m_visitedNodes;
+ /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
+ CompilerContext *m_runtimeContext;
+ /// The index of the runtime subroutine.
+ size_t m_runtimeSub = -1;
};
}
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 58d1caa9..bfe5386b 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -133,6 +133,16 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << u256(str->value().size());
m_context << Instruction::ADD;
}
+ else if (
+ _type.category() == Type::Category::Function &&
+ dynamic_cast<FunctionType const&>(_type).location() == FunctionType::Location::External
+ )
+ {
+ solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
+ combineExternalFunctionType(true);
+ m_context << Instruction::DUP2 << Instruction::MSTORE;
+ m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
+ }
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
@@ -206,7 +216,8 @@ void CompilerUtils::encodeToMemory(
else if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
- _givenTypes[i]->category() == Type::Category::StringLiteral
+ _givenTypes[i]->category() == Type::Category::StringLiteral ||
+ _givenTypes[i]->category() == Type::Category::Function
)
type = _givenTypes[i]; // delay conversion
else
@@ -304,6 +315,49 @@ void CompilerUtils::memoryCopy()
m_context << Instruction::POP; // ignore return value
}
+void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
+{
+ // We have to split the left-aligned <address><function identifier> into two stack slots:
+ // address (right aligned), function identifier (right aligned)
+ if (_leftAligned)
+ {
+ m_context << Instruction::DUP1 << (u256(1) << (64 + 32)) << Instruction::SWAP1 << Instruction::DIV;
+ // <input> <address>
+ m_context << Instruction::SWAP1 << (u256(1) << 64) << Instruction::SWAP1 << Instruction::DIV;
+ }
+ else
+ {
+ m_context << Instruction::DUP1 << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
+ }
+ m_context << u256(0xffffffffUL) << Instruction::AND;
+}
+
+void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
+{
+ // <address> <function_id>
+ m_context << u256(0xffffffffUL) << Instruction::AND << Instruction::SWAP1;
+ if (!_leftAligned)
+ m_context << ((u256(1) << 160) - 1) << Instruction::AND;
+ m_context << (u256(1) << 32) << Instruction::MUL;
+ m_context << Instruction::OR;
+ if (_leftAligned)
+ m_context << (u256(1) << 64) << Instruction::MUL;
+}
+
+void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
+{
+ m_context << m_context.functionEntryLabel(_function).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())
+ m_context <<
+ (u256(1) << 32) <<
+ Instruction::MUL <<
+ rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
+ Instruction::OR;
+}
+
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
@@ -678,6 +732,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
void CompilerUtils::pushZeroValue(Type const& _type)
{
+ if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
+ {
+ if (funType->location() == FunctionType::Location::Internal)
+ {
+ m_context << m_context.errorTag();
+ return;
+ }
+ }
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
if (!referenceType || referenceType->location() == DataLocation::Storage)
{
@@ -822,21 +884,27 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
- bool leftAligned = _type.category() == Type::Category::FixedBytes;
+ bool isExternalFunctionType = false;
+ if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
+ if (funType->location() == FunctionType::Location::External)
+ isExternalFunctionType = true;
if (numBytes == 0)
+ {
m_context << Instruction::POP << u256(0);
- else
+ return numBytes;
+ }
+ solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
+ m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD);
+ if (isExternalFunctionType)
+ splitExternalFunctionType(true);
+ else if (numBytes != 32)
{
- solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
- m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD);
- if (numBytes != 32)
- {
- // add leading or trailing zeros by dividing/multiplying depending on alignment
- u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
- m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
- if (leftAligned)
- m_context << shiftFactor << Instruction::MUL;
- }
+ bool leftAligned = _type.category() == Type::Category::FixedBytes;
+ // add leading or trailing zeros by dividing/multiplying depending on alignment
+ u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
+ m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
+ if (leftAligned)
+ m_context << shiftFactor << Instruction::MUL;
}
return numBytes;
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index da74dc90..8e4033d6 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -114,6 +114,17 @@ public:
/// Stack post:
void memoryCopy();
+ /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
+ /// external function type <address><function identifier> into two stack slots:
+ /// address (right aligned), function identifier (right aligned)
+ void splitExternalFunctionType(bool _rightAligned);
+ /// Performs the opposite operation of splitExternalFunctionType(_rightAligned)
+ void combineExternalFunctionType(bool _rightAligned);
+ /// Appends code that combines the construction-time (if available) and runtime function
+ /// entry label of the given function into a single stack slot.
+ /// Note: This might cause the compilation queue of the runtime context to be extended.
+ void pushCombinedFunctionEntryLabel(Declaration const& _function);
+
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
/// if a reference type is converted from calldata or storage to memory.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 2aec3055..2e3f5d7f 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -60,14 +60,13 @@ void ContractCompiler::compileContract(
}
size_t ContractCompiler::compileConstructor(
- CompilerContext const& _runtimeContext,
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
initializeContext(_contract, _contracts);
- return packIntoContractCreator(_contract, _runtimeContext);
+ return packIntoContractCreator(_contract);
}
size_t ContractCompiler::compileClone(
@@ -80,7 +79,7 @@ size_t ContractCompiler::compileClone(
appendInitAndConstructorCode(_contract);
//@todo determine largest return size of all runtime functions
- eth::AssemblyItem runtimeSub = m_context.addSubroutine(cloneRuntime());
+ auto runtimeSub = m_context.addSubroutine(cloneRuntime());
// stack contains sub size
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
@@ -88,7 +87,6 @@ size_t ContractCompiler::compileClone(
appendMissingFunctions();
- solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
return size_t(runtimeSub.data());
}
@@ -141,21 +139,31 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
appendBaseConstructor(*c);
}
-size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
+size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
{
+ solAssert(!!m_runtimeCompiler, "");
+
appendInitAndConstructorCode(_contract);
- eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly());
+ // We jump to the deploy routine because we first have to append all missing functions,
+ // which can cause further functions to be added to the runtime context.
+ eth::AssemblyItem deployRoutine = m_context.appendJumpToNew();
- // stack contains sub size
- m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
- m_context << u256(0) << Instruction::RETURN;
-
- // note that we have to include the functions again because of absolute jump labels
+ // We have to include copies of functions in the construction time and runtime context
+ // because of absolute jumps.
appendMissingFunctions();
+ m_runtimeCompiler->appendMissingFunctions();
- solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
- return size_t(runtimeSub.data());
+ m_context << deployRoutine;
+
+ solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
+ m_context.pushSubroutineSize(m_context.runtimeSub());
+ m_context << Instruction::DUP1;
+ m_context.pushSubroutineOffset(m_context.runtimeSub());
+ m_context << u256(0) << Instruction::CODECOPY;
+ m_context << u256(0) << Instruction::RETURN;
+
+ return m_context.runtimeSub();
}
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
@@ -293,6 +301,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
{
// stack: v1 v2 ... v(k-1) base_offset current_offset
TypePointer type = parameterType->decodingType();
+ solAssert(type, "No decoding type found.");
if (type->category() == Type::Category::Array)
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
@@ -515,7 +524,19 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
- _assembly.append(m_context.virtualFunctionEntryLabel(*functionDef).pushTag());
+ {
+ 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);
+ }
+ }
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->isConstant(), "");
@@ -871,7 +892,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
}
-eth::Assembly ContractCompiler::cloneRuntime()
+eth::AssemblyPointer ContractCompiler::cloneRuntime()
{
eth::Assembly a;
a << Instruction::CALLDATASIZE;
@@ -889,5 +910,5 @@ eth::Assembly ContractCompiler::cloneRuntime()
a.appendJumpI(a.errorTag());
//@todo adjust for larger return values, make this dynamic.
a << u256(0x20) << u256(0) << Instruction::RETURN;
- return a;
+ return make_shared<eth::Assembly>(a);
}
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 0799a543..2244a3be 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -38,11 +38,12 @@ namespace solidity {
class ContractCompiler: private ASTConstVisitor
{
public:
- explicit ContractCompiler(CompilerContext& _context, bool _optimise):
+ explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise):
m_optimise(_optimise),
+ m_runtimeCompiler(_runtimeCompiler),
m_context(_context)
{
- m_context = CompilerContext();
+ m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
}
void compileContract(
@@ -52,7 +53,6 @@ public:
/// Compiles the constructor part of the contract.
/// @returns the identifier of the runtime sub-assembly.
size_t compileConstructor(
- CompilerContext const& _runtimeContext,
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
);
@@ -74,7 +74,7 @@ private:
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. Adds the constructor code.
/// @returns the identifier of the runtime sub assembly
- size_t packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
+ size_t packIntoContractCreator(ContractDefinition const& _contract);
/// Appends state variable initialisation and constructor code.
void appendInitAndConstructorCode(ContractDefinition const& _contract);
void appendBaseConstructor(FunctionDefinition const& _constructor);
@@ -114,9 +114,11 @@ private:
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
/// @returns the runtime assembly for clone contracts.
- static eth::Assembly cloneRuntime();
+ static eth::AssemblyPointer cloneRuntime();
bool const m_optimise;
+ /// Pointer to the runtime compiler in case this is a creation compiler.
+ ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index e3f05c21..7a328528 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -488,6 +488,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
parameterSize += function.selfType()->sizeOnStack();
}
+ if (m_context.runtimeContext())
+ // We have a runtime context, so we need the creation part.
+ m_context << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ else
+ // Extract the runtime part.
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
@@ -521,8 +528,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// copy the contract's code into memory
eth::Assembly const& assembly = m_context.compiledContract(contract);
utils().fetchFreeMemoryPointer();
+ // TODO we create a copy here, which is actually what we want.
+ // This should be revisited at the point where we fix
+ // https://github.com/ethereum/solidity/issues/1092
// pushes size
- eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
+ auto subroutine = m_context.addSubroutine(make_shared<eth::Assembly>(assembly));
m_context << Instruction::DUP1 << subroutine;
m_context << Instruction::DUP4 << Instruction::CODECOPY;
@@ -845,9 +855,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
if (funType->location() == FunctionType::Location::Internal)
{
- m_context << m_context.functionEntryLabel(
- dynamic_cast<FunctionDefinition const&>(funType->declaration())
- ).pushTag();
+ FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration());
+ utils().pushCombinedFunctionEntryLabel(funDef);
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1);
}
else
@@ -883,7 +892,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
// us to link against it although we actually do not need it.
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(!!function, "Function not found in member access");
- m_context << m_context.functionEntryLabel(*function).pushTag();
+ utils().pushCombinedFunctionEntryLabel(*function);
}
}
else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get()))
@@ -915,10 +924,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (type.isSuper())
{
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
- m_context << m_context.superFunctionEntryLabel(
+ utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
type.contractDefinition()
- ).pushTag();
+ ));
}
else
{
@@ -1203,7 +1212,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
- m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
+ utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef));
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
appendVariable(*variable, static_cast<Expression const&>(_identifier));
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
@@ -1266,6 +1275,17 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
{
if (_operator == Token::Equal || _operator == Token::NotEqual)
{
+ if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
+ {
+ if (funType->location() == FunctionType::Location::Internal)
+ {
+ // We have to remove the upper bits (construction time value) because they might
+ // be "unknown" in one of the operands and not in the other.
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+ m_context << Instruction::SWAP1;
+ m_context << ((u256(1) << 32) - 1) << Instruction::AND;
+ }
+ }
m_context << Instruction::EQ;
if (_operator == Token::NotEqual)
m_context << Instruction::ISZERO;
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index 69a80b6a..2ec7f800 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -153,7 +153,8 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
{
if (m_dataType->isValueType())
{
- solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
+ if (m_dataType->category() != Type::Category::Function)
+ solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
}
}
@@ -176,6 +177,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
m_context << Instruction::POP << Instruction::SLOAD;
else
{
+ bool cleaned = false;
m_context
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
@@ -183,14 +185,31 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
// implementation should be very similar to the integer case.
solUnimplemented("Not yet implemented - FixedPointType.");
if (m_dataType->category() == Type::Category::FixedBytes)
+ {
m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL;
+ cleaned = true;
+ }
else if (
m_dataType->category() == Type::Category::Integer &&
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
)
+ {
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
- else
+ cleaned = true;
+ }
+ else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
+ {
+ if (fun->location() == FunctionType::Location::External)
+ {
+ CompilerUtils(m_context).splitExternalFunctionType(false);
+ cleaned = true;
+ }
+ }
+ if (!cleaned)
+ {
+ solAssert(m_dataType->sizeOnStack() == 1, "");
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
+ }
}
}
@@ -204,6 +223,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
if (m_dataType->storageBytes() == 32)
{
+ solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size.");
// offset should be zero
m_context << Instruction::POP;
if (!_move)
@@ -222,16 +242,27 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
m_context
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
<< Instruction::MUL;
- m_context << Instruction::NOT << Instruction::AND;
- // stack: value storage_ref multiplier cleared_value
- m_context
- << Instruction::SWAP1 << Instruction::DUP4;
+ m_context << Instruction::NOT << Instruction::AND << Instruction::SWAP1;
+ // stack: value storage_ref cleared_value multiplier
+ utils.copyToStackTop(3 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack());
// stack: value storage_ref cleared_value multiplier value
- if (m_dataType->category() == Type::Category::FixedBytes)
+ if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
+ {
+ if (fun->location() == FunctionType::Location::External)
+ // Combine the two-item function type into a single stack slot.
+ utils.combineExternalFunctionType(false);
+ else
+ m_context <<
+ ((u256(1) << (8 * m_dataType->storageBytes())) - 1) <<
+ Instruction::AND;
+ }
+ else if (m_dataType->category() == Type::Category::FixedBytes)
m_context
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
<< Instruction::SWAP1 << Instruction::DIV;
else
+ {
+ solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size for opaque type.");
// remove the higher order bits
m_context
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
@@ -239,11 +270,12 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
<< Instruction::DUP2
<< Instruction::MUL
<< Instruction::DIV;
+ }
m_context << Instruction::MUL << Instruction::OR;
// stack: value storage_ref updated_value
m_context << Instruction::SWAP1 << Instruction::SSTORE;
if (_move)
- m_context << Instruction::POP;
+ utils.popStackElement(*m_dataType);
}
}
else