aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/CompilerContext.cpp48
-rw-r--r--libsolidity/codegen/CompilerContext.h9
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp73
-rw-r--r--libsolidity/codegen/ContractCompiler.h7
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp70
-rw-r--r--libsolidity/codegen/LValue.cpp2
-rw-r--r--libsolidity/codegen/LValue.h2
7 files changed, 163 insertions, 48 deletions
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index c1dc8dfb..3ac5bd3c 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -23,9 +23,12 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <utility>
#include <numeric>
+#include <boost/algorithm/string/replace.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/Version.h>
+#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmStack.h>
using namespace std;
@@ -172,6 +175,51 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
updateSourceLocation();
}
+void CompilerContext::appendInlineAssembly(
+ string const& _assembly,
+ vector<string> const& _localVariables,
+ map<string, string> const& _replacements
+)
+{
+ string replacedAssembly;
+ string const* assembly = &_assembly;
+ if (!_replacements.empty())
+ {
+ replacedAssembly = _assembly;
+ for (auto const& replacement: _replacements)
+ replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second);
+ assembly = &replacedAssembly;
+ }
+
+ unsigned startStackHeight = stackHeight();
+ auto identifierAccess = [&](
+ assembly::Identifier const& _identifier,
+ eth::Assembly& _assembly,
+ assembly::CodeGenerator::IdentifierContext _context
+ ) {
+ auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
+ if (it == _localVariables.end())
+ return false;
+ unsigned stackDepth = _localVariables.end() - it;
+ int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
+ if (stackDiff < 1 || stackDiff > 16)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ if (_context == assembly::CodeGenerator::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), "");
+}
+
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
{
eth::Assembly& sub = m_asm.sub(_subIndex);
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index a56335ce..0c1500b0 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -132,6 +132,15 @@ public:
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.
+ /// @param _localVariables assigns stack positions to variables with the last one being the stack top
+ void appendInlineAssembly(
+ std::string const& _assembly,
+ std::vector<std::string> const& _localVariables = std::vector<std::string>(),
+ std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{}
+ );
+
/// Prepends "PUSH <compiler version number> POP"
void injectVersionStampIntoSub(size_t _subIndex);
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index bcfd33f2..715852be 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -431,16 +431,16 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope())))
appendBaseConstructor(*c);
- m_returnTag = m_context.newTag();
+ solAssert(m_returnTags.empty(), "");
m_breakTags.clear();
m_continueTags.clear();
m_stackCleanupForReturn = 0;
m_currentFunction = &_function;
- m_modifierDepth = 0;
+ m_modifierDepth = -1;
appendModifierOrFunctionCode();
- m_context << m_returnTag;
+ solAssert(m_returnTags.empty(), "");
// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
// that shows the target positions of the elements, where "-1" denotes that this element needs
@@ -695,7 +695,7 @@ bool ContractCompiler::visit(Return const& _return)
}
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
m_context << Instruction::POP;
- m_context.appendJumpTo(m_returnTag);
+ m_context.appendJumpTo(m_returnTags.back());
m_context.adjustStackOffset(m_stackCleanupForReturn);
return false;
}
@@ -755,9 +755,7 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement)
{
StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
- ++m_modifierDepth;
appendModifierOrFunctionCode();
- --m_modifierDepth;
checker.check();
return true;
}
@@ -775,10 +773,15 @@ void ContractCompiler::appendMissingFunctions()
void ContractCompiler::appendModifierOrFunctionCode()
{
solAssert(m_currentFunction, "");
+ unsigned stackSurplus = 0;
+ Block const* codeBlock = nullptr;
+
+ m_modifierDepth++;
+
if (m_modifierDepth >= m_currentFunction->modifiers().size())
{
solAssert(m_currentFunction->isImplemented(), "");
- m_currentFunction->body().accept(*this);
+ codeBlock = &m_currentFunction->body();
}
else
{
@@ -786,37 +789,45 @@ void ContractCompiler::appendModifierOrFunctionCode()
// constructor call should be excluded
if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration))
- {
- ++m_modifierDepth;
appendModifierOrFunctionCode();
- --m_modifierDepth;
- return;
- }
-
- ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name());
- CompilerContext::LocationSetter locationSetter(m_context, modifier);
- solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), "");
- for (unsigned i = 0; i < modifier.parameters().size(); ++i)
+ else
{
- m_context.addVariable(*modifier.parameters()[i]);
- compileExpression(
- *modifierInvocation->arguments()[i],
- modifier.parameters()[i]->annotation().type
- );
+ ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name());
+ CompilerContext::LocationSetter locationSetter(m_context, modifier);
+ solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), "");
+ for (unsigned i = 0; i < modifier.parameters().size(); ++i)
+ {
+ m_context.addVariable(*modifier.parameters()[i]);
+ compileExpression(
+ *modifierInvocation->arguments()[i],
+ modifier.parameters()[i]->annotation().type
+ );
+ }
+ for (VariableDeclaration const* localVariable: modifier.localVariables())
+ appendStackVariableInitialisation(*localVariable);
+
+ stackSurplus =
+ CompilerUtils::sizeOnStack(modifier.parameters()) +
+ CompilerUtils::sizeOnStack(modifier.localVariables());
+ codeBlock = &modifier.body();
+
+ codeBlock = &modifier.body();
}
- for (VariableDeclaration const* localVariable: modifier.localVariables())
- appendStackVariableInitialisation(*localVariable);
+ }
+
+ if (codeBlock)
+ {
+ m_returnTags.push_back(m_context.newTag());
- unsigned const c_stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()) +
- CompilerUtils::sizeOnStack(modifier.localVariables());
- m_stackCleanupForReturn += c_stackSurplus;
+ codeBlock->accept(*this);
- modifier.body().accept(*this);
+ solAssert(!m_returnTags.empty(), "");
+ m_context << m_returnTags.back();
+ m_returnTags.pop_back();
- for (unsigned i = 0; i < c_stackSurplus; ++i)
- m_context << Instruction::POP;
- m_stackCleanupForReturn -= c_stackSurplus;
+ CompilerUtils(m_context).popStackSlots(stackSurplus);
}
+ m_modifierDepth--;
}
void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration const& _variable)
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index d1517e88..0799a543 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -40,11 +40,9 @@ class ContractCompiler: private ASTConstVisitor
public:
explicit ContractCompiler(CompilerContext& _context, bool _optimise):
m_optimise(_optimise),
- m_context(_context),
- m_returnTag(eth::Tag, u256(-1))
+ m_context(_context)
{
m_context = CompilerContext();
- m_returnTag = m_context.newTag();
}
void compileContract(
@@ -122,7 +120,8 @@ private:
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
- eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
+ /// Tag to jump to for a "return" statement, needs to be stacked because of modifiers.
+ std::vector<eth::AssemblyItem> m_returnTags;
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction = nullptr;
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 80009a90..4a81e27d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolidity/ast/AST.h>
@@ -297,9 +298,6 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::BitNot: // ~
m_context << Instruction::NOT;
break;
- case Token::After: // after
- m_context << Instruction::TIMESTAMP << Instruction::ADD;
- break;
case Token::Delete: // delete
solAssert(!!m_currentLValue, "LValue not retrieved.");
m_currentLValue->setToZero(_unaryOperation.location());
@@ -536,6 +534,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
m_context << u256(0);
m_context << Instruction::CREATE;
+ // Check if zero (out of stack or not enough balance).
+ m_context << Instruction::DUP1 << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
break;
@@ -1323,11 +1324,18 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
m_context << Instruction::MUL;
break;
case Token::Div:
- m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
- break;
case Token::Mod:
- m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
+ {
+ // Test for division by zero
+ m_context << Instruction::DUP2 << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+
+ if (_operator == Token::Div)
+ m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
+ else
+ m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
break;
+ }
case Token::Exp:
m_context << Instruction::EXP;
break;
@@ -1448,6 +1456,19 @@ void ExpressionCompiler::appendExternalFunctionCall(
argumentTypes.push_back(_arguments[i]->annotation().type);
}
+ if (funKind == FunctionKind::ECRecover)
+ {
+ // Clears 32 bytes of currently free memory and advances free memory pointer.
+ // Output area will be "start of input area" - 32.
+ // The reason is that a failing ECRecover cannot be detected, it will just return
+ // zero bytes (which we cannot detect).
+ solAssert(0 < retSize && retSize <= 32, "");
+ utils().fetchFreeMemoryPointer();
+ m_context << Instruction::DUP1 << u256(0) << Instruction::MSTORE;
+ m_context << u256(32) << Instruction::ADD;
+ utils().storeFreeMemoryPointer();
+ }
+
// Copy function identifier to memory.
utils().fetchFreeMemoryPointer();
if (!_functionType.isBareCall() || manualFunctionId)
@@ -1456,7 +1477,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
}
// If the function takes arbitrary parameters, copy dynamic length data in place.
- // Move argumenst to memory, will not update the free memory pointer (but will update the memory
+ // Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
utils().encodeToMemory(
argumentTypes,
@@ -1474,12 +1495,24 @@ void ExpressionCompiler::appendExternalFunctionCall(
// function identifier [unless bare]
// contract address
- // Output data will replace input data.
+ // Output data will replace input data, unless we have ECRecover (then, output
+ // area will be 32 bytes just before input area).
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
m_context << u256(retSize);
- utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB;
- m_context << Instruction::DUP2;
+ utils().fetchFreeMemoryPointer(); // This is the start of input
+ if (funKind == FunctionKind::ECRecover)
+ {
+ // In this case, output is 32 bytes before input and has already been cleared.
+ m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1;
+ // Here: <input end> <output size> <outpos> <input pos>
+ m_context << Instruction::DUP1 << Instruction::DUP5 << Instruction::SUB;
+ m_context << Instruction::SWAP1;
+ }
+ else
+ {
+ m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB;
+ m_context << Instruction::DUP2;
+ }
// CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
// [value,] addr, gas (stack top)
@@ -1491,6 +1524,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+ // Check the the target contract exists (has code) for non-low-level calls.
+ if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
+ {
+ m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
@@ -1542,6 +1582,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().loadFromMemoryDynamic(IntegerType(160), false, true, false);
utils().convertType(IntegerType(160), FixedBytesType(20));
}
+ else if (funKind == FunctionKind::ECRecover)
+ {
+ // Output is 32 bytes before input / free mem pointer.
+ // Failing ecrecover cannot be detected, so we clear output before the call.
+ m_context << u256(32);
+ utils().fetchFreeMemoryPointer();
+ m_context << Instruction::SUB << Instruction::MLOAD;
+ }
else if (!_functionType.returnParameterTypes().empty())
{
utils().fetchFreeMemoryPointer();
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index ea8bc1ba..553e5518 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -17,7 +17,7 @@
/**
* @author Christian <c@ethdev.com>
* @date 2015
- * LValues for use in the expresison compiler.
+ * LValues for use in the expression compiler.
*/
#include <libsolidity/codegen/LValue.h>
diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h
index e8c3aa80..a2f979db 100644
--- a/libsolidity/codegen/LValue.h
+++ b/libsolidity/codegen/LValue.h
@@ -17,7 +17,7 @@
/**
* @author Christian <c@ethdev.com>
* @date 2015
- * LValues for use in the expresison compiler.
+ * LValues for use in the expression compiler.
*/
#pragma once