diff options
-rw-r--r-- | AST.cpp | 11 | ||||
-rw-r--r-- | CompilerUtils.cpp | 7 | ||||
-rw-r--r-- | CompilerUtils.h | 2 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 91 | ||||
-rw-r--r-- | ExpressionCompiler.h | 18 | ||||
-rw-r--r-- | Types.cpp | 6 | ||||
-rw-r--r-- | Types.h | 6 |
7 files changed, 111 insertions, 30 deletions
@@ -450,14 +450,11 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); + // todo delete when will be implemented arrays as parameter type in internal functions + if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) + BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) - { - // todo delete when will be implemented arrays as parameter type in internal functions - if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) - BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); - else - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); - } + BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 8d3e9d2a..07bc3cda 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -155,6 +155,13 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) m_context << eth::dupInstruction(_stackDepth); } +void CompilerUtils::moveToStackTop(unsigned _stackDepth) +{ + solAssert(_stackDepth <= 15, "Stack too deep."); + for (unsigned i = 0; i < _stackDepth; ++i) + m_context << eth::swapInstruction(1 + i); +} + void CompilerUtils::popStackElement(Type const& _type) { popStackSlots(_type.getSizeOnStack()); diff --git a/CompilerUtils.h b/CompilerUtils.h index 5b809bea..45f53e12 100644 --- a/CompilerUtils.h +++ b/CompilerUtils.h @@ -77,6 +77,8 @@ public: /// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth /// to the top of the stack. void copyToStackTop(unsigned _stackDepth, unsigned _itemSize); + /// Moves a single stack element (with _stackDepth items on top of it) to the top of the stack. + void moveToStackTop(unsigned _stackDepth); /// 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/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index cf6a01ec..a11c9944 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -532,7 +532,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::SHA3: { m_context << u256(0); - appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments()); + appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments(), false, true); m_context << u256(0) << eth::Instruction::SHA3; break; } @@ -575,9 +575,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(numIndexed <= 4, "Too many indexed arguments."); // Copy all non-indexed arguments to memory (data) m_context << u256(0); + vector<ASTPointer<Expression const>> nonIndexedArgs; + TypePointers nonIndexedTypes; for (unsigned arg = 0; arg < arguments.size(); ++arg) if (!event.getParameters()[arg]->isIndexed()) - appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]); + { + nonIndexedArgs.push_back(arguments[arg]); + nonIndexedTypes.push_back(function.getParameterTypes()[arg]); + } + appendArgumentsCopyToMemory(nonIndexedArgs, nonIndexedTypes); m_context << u256(0) << eth::logInstruction(numIndexed); break; } @@ -1046,8 +1052,14 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, // do not pad it to 32 bytes. - appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), - _functionType.padArguments(), bare); + // If the function takes arbitrary parameters, copy dynamic length data in place. + appendArgumentsCopyToMemory( + _arguments, + _functionType.getParameterTypes(), + _functionType.padArguments(), + bare, + _functionType.takesArbitraryParameters() + ); // CALL arguments: outSize, outOff, inSize, (already present up to here) // inOff, value, addr, gas (stack top) @@ -1089,20 +1101,72 @@ void ExpressionCompiler::appendArgumentsCopyToMemory( vector<ASTPointer<Expression const>> const& _arguments, TypePointers const& _types, bool _padToWordBoundaries, - bool _padExceptionIfFourBytes + bool _padExceptionIfFourBytes, + bool _copyDynamicDataInPlace ) { solAssert(_types.empty() || _types.size() == _arguments.size(), ""); + TypePointers types = _types; + if (_types.empty()) + for (ASTPointer<Expression const> const& argument: _arguments) + types.push_back(argument->getType()->getRealType()); + + vector<size_t> dynamicArguments; + unsigned stackSizeOfDynamicTypes = 0; for (size_t i = 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); - TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i]; - appendTypeConversion(*_arguments[i]->getType(), *expectedType, true); + TypePointer argType = types[i]->externalType(); + solAssert(!!argType, "Externalable type expected."); + if (argType->isValueType()) + appendTypeConversion(*_arguments[i]->getType(), *argType, true); + else + argType = _arguments[i]->getType()->getRealType()->externalType(); + solAssert(!!argType, "Externalable type expected."); bool pad = _padToWordBoundaries; // Do not pad if the first argument has exactly four bytes - if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize(false) == 4) + if (i == 0 && pad && _padExceptionIfFourBytes && argType->getCalldataEncodedSize(false) == 4) pad = false; - appendTypeMoveToMemory(*expectedType, pad); + if (!_copyDynamicDataInPlace && argType->isDynamicallySized()) + { + solAssert(argType->getCategory() == Type::Category::Array, "Unknown dynamic type."); + auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType()); + // move memory reference to top of stack + CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack()); + if (arrayType.getLocation() == ArrayType::Location::CallData) + m_context << eth::Instruction::DUP2; // length is on stack + else if (arrayType.getLocation() == ArrayType::Location::Storage) + m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD; + else + { + solAssert(arrayType.getLocation() == ArrayType::Location::Memory, ""); + m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD; + } + appendTypeMoveToMemory(IntegerType(256), true); + stackSizeOfDynamicTypes += arrayType.getSizeOnStack(); + dynamicArguments.push_back(i); + } + else + appendTypeMoveToMemory(*argType, pad); + } + + // copy dynamic values to memory + unsigned dynStackPointer = stackSizeOfDynamicTypes; + // stack layout: <dyn arg 1> ... <dyn arg m> <memory pointer> + for (size_t i: dynamicArguments) + { + auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType()); + CompilerUtils(m_context).copyToStackTop(1 + dynStackPointer, arrayType.getSizeOnStack()); + dynStackPointer -= arrayType.getSizeOnStack(); + appendTypeMoveToMemory(arrayType, true); + } + solAssert(dynStackPointer == 0, ""); + + // remove dynamic values (and retain memory pointer) + if (stackSizeOfDynamicTypes > 0) + { + m_context << eth::swapInstruction(stackSizeOfDynamicTypes); + CompilerUtils(m_context).popStackSlots(stackSizeOfDynamicTypes); } } @@ -1114,8 +1178,13 @@ void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWo void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) { _expression.accept(*this); - appendTypeConversion(*_expression.getType(), _expectedType, true); - appendTypeMoveToMemory(_expectedType); + if (_expectedType.isValueType()) + { + appendTypeConversion(*_expression.getType(), _expectedType, true); + appendTypeMoveToMemory(_expectedType); + } + else + appendTypeMoveToMemory(*_expression.getType()->getRealType()); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 2577d21b..35526662 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -100,12 +100,18 @@ private: /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, bool bare = false); - /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is - /// expected to be on the stack and is updated by this call. - void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments, - TypePointers const& _types = {}, - bool _padToWordBoundaries = true, - bool _padExceptionIfFourBytes = false); + /// Appends code that evaluates the given arguments and moves the result to memory encoded as + /// specified by the ABI. The memory offset is expected to be on the stack and is updated by + /// this call. If @a _padToWordBoundaries is set to false, all values are concatenated without + /// padding. If @a _copyDynamicDataInPlace is set, dynamic types is stored (without length) + /// together with fixed-length data. + void appendArgumentsCopyToMemory( + std::vector<ASTPointer<Expression const>> const& _arguments, + TypePointers const& _types = {}, + bool _padToWordBoundaries = true, + bool _padExceptionIfFourBytes = false, + bool _copyDynamicDataInPlace = false + ); /// Appends code that moves a stack element of the given type to memory. The memory offset is /// expected below the stack element and is updated by this call. void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true); @@ -745,8 +745,6 @@ string ArrayType::toString() const TypePointer ArrayType::externalType() const { - if (m_location != Location::CallData) - return TypePointer(); if (m_isByteArray) return shared_from_this(); if (!m_baseType->externalType()) @@ -1218,7 +1216,9 @@ string FunctionType::externalSignature(std::string const& _name) const } string ret = funcName + "("; - TypePointers externalParameterTypes = externalFunctionType()->getParameterTypes(); + FunctionTypePointer external = externalFunctionType(); + solAssert(!!external, "External function type requested."); + TypePointers externalParameterTypes = external->getParameterTypes(); for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); @@ -430,7 +430,7 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + virtual unsigned getCalldataEncodedSize(bool _padded ) const override { return externalType()->getCalldataEncodedSize(_padded); } @@ -506,7 +506,7 @@ public: explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + virtual unsigned getCalldataEncodedSize(bool _padded) const override { return externalType()->getCalldataEncodedSize(_padded); } @@ -558,7 +558,7 @@ public: /// appropriate external types of input/return parameters of current function. /// Returns an empty shared pointer if one of the input/return parameters does not have an /// external type. - virtual FunctionTypePointer externalFunctionType() const; + FunctionTypePointer externalFunctionType() const; virtual TypePointer externalType() const override { return externalFunctionType(); } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); |