diff options
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/Compiler.cpp | 18 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 29 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 45 |
4 files changed, 79 insertions, 19 deletions
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index f1d95980..18803b71 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -305,11 +305,19 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool // @todo If base type is an array or struct, it is still calldata-style encoded, so // we would have to convert it like below. solAssert(arrayType.location() == DataLocation::Memory, ""); - // compute data pointer - m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD; - m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; - m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1; - m_context << u256(0x20) << eth::Instruction::ADD; + if (arrayType.isDynamicallySized()) + { + // compute data pointer + m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD; + m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; + m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1; + m_context << u256(0x20) << eth::Instruction::ADD; + } + else + { + m_context << eth::Instruction::DUP1; + m_context << u256(arrayType.calldataEncodedSize(true)) << eth::Instruction::ADD; + } } else { diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index b57f5b29..bd0857f6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -695,18 +695,31 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize) { - solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables."); - for (unsigned j = 0; j < _itemSize; ++j) - for (unsigned i = 0; i < _stackDepth + _itemSize - 1; ++i) - m_context << eth::swapInstruction(1 + i); + moveIntoStack(_itemSize, _stackDepth); } void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize) { - solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables."); - for (unsigned j = 0; j < _itemSize; ++j) - for (unsigned i = _stackDepth; i > 0; --i) - m_context << eth::swapInstruction(i + _itemSize - 1); + if (_stackDepth <= _itemSize) + for (unsigned i = 0; i < _stackDepth; ++i) + rotateStackDown(_stackDepth + _itemSize); + else + for (unsigned i = 0; i < _itemSize; ++i) + rotateStackUp(_stackDepth + _itemSize); +} + +void CompilerUtils::rotateStackUp(unsigned _items) +{ + solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables."); + for (unsigned i = 1; i < _items; ++i) + m_context << eth::swapInstruction(_items - i); +} + +void CompilerUtils::rotateStackDown(unsigned _items) +{ + solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables."); + for (unsigned i = 1; i < _items; ++i) + m_context << eth::swapInstruction(i); } void CompilerUtils::popStackElement(Type const& _type) diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 134afd78..55254013 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -134,6 +134,12 @@ public: void moveToStackTop(unsigned _stackDepth, unsigned _itemSize = 1); /// Moves @a _itemSize elements past @a _stackDepth other stack elements void moveIntoStack(unsigned _stackDepth, unsigned _itemSize = 1); + /// Rotates the topmost @a _items items on the stack, such that the previously topmost element + /// is bottom-most. + void rotateStackUp(unsigned _items); + /// Rotates the topmost @a _items items on the stack, such that the previously bottom-most element + /// is now topmost. + void rotateStackDown(unsigned _items); /// Removes the current value from the top of the stack. void popStackElement(Type const& _type); /// Removes element from the top of the stack _amount times. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 6c288ae7..a090a28c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -422,6 +422,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) else { FunctionType const& function = *functionType; + if (function.bound()) + // Only callcode functions can be bound, this might be lifted later. + solAssert(function.location() == Location::CallCode, ""); switch (function.location()) { case Location::Internal: @@ -535,7 +538,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) {} ); break; - case Location::Suicide: + case Location::Selfdestruct: arguments.front()->accept(*this); utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; @@ -672,7 +675,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); solAssert(function.parameterTypes().size() == 1, ""); solAssert(!!function.parameterTypes()[0], ""); - TypePointer const& paramType = function.parameterTypes()[0]; + TypePointer paramType = function.parameterTypes()[0]; shared_ptr<ArrayType> arrayType = function.location() == Location::ArrayPush ? make_shared<ArrayType>(DataLocation::Storage, paramType) : @@ -766,7 +769,26 @@ bool ExpressionCompiler::visit(NewExpression const&) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); + + // Check whether the member is a bound function. ASTString const& member = _memberAccess.memberName(); + if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) + if (funType->bound()) + { + utils().convertType( + *_memberAccess.expression().annotation().type, + *funType->selfType(), + true + ); + auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope()); + solAssert(contract && contract->isLibrary(), ""); + //@TODO library name might not be unique + m_context.appendLibraryAddress(contract->name()); + m_context << funType->externalIdentifier(); + utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); + return; + } + switch (_memberAccess.expression().annotation().type->category()) { case Type::Category::Contract: @@ -1239,7 +1261,6 @@ void ExpressionCompiler::appendExternalFunctionCall( vector<ASTPointer<Expression const>> const& _arguments ) { - eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context. solAssert( _functionType.takesArbitraryParameters() || _arguments.size() == _functionType.parameterTypes().size(), "" @@ -1249,15 +1270,20 @@ void ExpressionCompiler::appendExternalFunctionCall( // <stack top> // value [if _functionType.valueSet()] // gas [if _functionType.gasSet()] + // self object [if bound - moved to top right away] // function identifier [unless bare] // contract address + unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0; unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0); - - unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (_functionType.isBareCall() ? 0 : 1)); + unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1)); unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); unsigned valueStackPos = m_context.currentToBaseStackOffset(1); + // move self object to top + if (_functionType.bound()) + utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); + using FunctionKind = FunctionType::Location; FunctionKind funKind = _functionType.location(); bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode; @@ -1275,6 +1301,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // Evaluate arguments. TypePointers argumentTypes; + TypePointers parameterTypes = _functionType.parameterTypes(); bool manualFunctionId = (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && !_arguments.empty() && @@ -1295,6 +1322,11 @@ void ExpressionCompiler::appendExternalFunctionCall( gasStackPos++; valueStackPos++; } + if (_functionType.bound()) + { + argumentTypes.push_back(_functionType.selfType()); + parameterTypes.insert(parameterTypes.begin(), _functionType.selfType()); + } for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); @@ -1313,7 +1345,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // pointer on the stack). utils().encodeToMemory( argumentTypes, - _functionType.parameterTypes(), + parameterTypes, _functionType.padArguments(), _functionType.takesArbitraryParameters(), isCallCode @@ -1346,6 +1378,7 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); else { + eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context. // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. u256 gasNeededByCaller = schedule.callGas + 10; |