diff options
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 48 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 9 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 73 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.h | 7 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 70 | ||||
-rw-r--r-- | libsolidity/codegen/LValue.cpp | 2 | ||||
-rw-r--r-- | libsolidity/codegen/LValue.h | 2 |
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 |