diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/ExpressionCompiler.cpp | 81 | ||||
-rw-r--r-- | libsolidity/Types.cpp | 26 | ||||
-rw-r--r-- | libsolidity/Types.h | 12 |
3 files changed, 95 insertions, 24 deletions
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f6eed0de..85302afc 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -623,6 +623,44 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) appendExternalFunctionCall(function, arguments); break; } + case Location::ByteArrayPush: + case Location::ArrayPush: + { + _functionCall.expression().accept(*this); + solAssert(function.parameterTypes().size() == 1, ""); + solAssert(!!function.parameterTypes()[0], ""); + TypePointer const& paramType = function.parameterTypes()[0]; + shared_ptr<ArrayType> arrayType = + function.location() == Location::ArrayPush ? + make_shared<ArrayType>(DataLocation::Storage, paramType) : + make_shared<ArrayType>(DataLocation::Storage); + // get the current length + ArrayUtils(m_context).retrieveLength(*arrayType); + m_context << eth::Instruction::DUP1; + // stack: ArrayReference currentLength currentLength + m_context << u256(1) << eth::Instruction::ADD; + // stack: ArrayReference currentLength newLength + m_context << eth::Instruction::DUP3 << eth::Instruction::DUP2; + ArrayUtils(m_context).resizeDynamicArray(*arrayType); + m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1; + // stack: newLength ArrayReference oldLength + ArrayUtils(m_context).accessIndex(*arrayType, false); + + // stack: newLength storageSlot slotOffset + arguments[0]->accept(*this); + // stack: newLength storageSlot slotOffset argValue + TypePointer type = arguments[0]->annotation().type; + utils().convertType(*type, *arrayType->baseType()); + type = arrayType->baseType(); + utils().moveToStackTop(1 + type->sizeOnStack()); + utils().moveToStackTop(1 + type->sizeOnStack()); + // stack: newLength argValue storageSlot slotOffset + if (function.location() == Location::ArrayPush) + StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true); + else + StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } @@ -784,26 +822,37 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) } case Type::Category::Array: { - solAssert(member == "length", "Illegal array member."); auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); - if (!type.isDynamicallySized()) + if (member == "length") { - utils().popStackElement(type); - m_context << type.length(); - } - else - switch (type.location()) + if (!type.isDynamicallySized()) { - case DataLocation::CallData: - m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; - break; - case DataLocation::Storage: - setLValue<StorageArrayLength>(_memberAccess, type); - break; - case DataLocation::Memory: - m_context << eth::Instruction::MLOAD; - break; + utils().popStackElement(type); + m_context << type.length(); } + else + switch (type.location()) + { + case DataLocation::CallData: + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; + break; + case DataLocation::Storage: + setLValue<StorageArrayLength>(_memberAccess, type); + break; + case DataLocation::Memory: + m_context << eth::Instruction::MLOAD; + break; + } + } + else if (member == "push") + { + solAssert( + type.isDynamicallySized() && type.location() == DataLocation::Storage, + "Tried to use .push() on a non-dynamically sized array" + ); + } + else + solAssert(false, "Illegal array member."); break; } default: diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 3ccaaf5d..51df5fbf 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -858,6 +858,28 @@ string ArrayType::canonicalName(bool _addDataLocation) const return ret; } +MemberList const& ArrayType::members() const +{ + if (!m_members) + { + MemberList::MemberMap members; + if (!isString()) + { + members.push_back({"length", make_shared<IntegerType>(256)}); + if (isDynamicallySized() && location() == DataLocation::Storage) + members.push_back({"push", make_shared<FunctionType>( + TypePointers{baseType()}, + TypePointers{make_shared<IntegerType>(256)}, + strings{string()}, + strings{string()}, + isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush + )}); + } + m_members.reset(new MemberList(members)); + } + return *m_members; +} + TypePointer ArrayType::encodingType() const { if (location() == DataLocation::Storage) @@ -913,8 +935,6 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } -const MemberList ArrayType::s_arrayTypeMemberList({{"length", make_shared<IntegerType>(256)}}); - bool ContractType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1422,6 +1442,8 @@ unsigned FunctionType::sizeOnStack() const size = 1; else if (location == Location::Internal) size = 1; + else if (location == Location::ArrayPush || location == Location::ByteArrayPush) + size = 1; if (m_gasSet) size++; if (m_valueSet) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index e73cd3cd..5c4aacdc 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -505,10 +505,7 @@ public: virtual unsigned sizeOnStack() const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; - virtual MemberList const& members() const override - { - return isString() ? EmptyMemberList : s_arrayTypeMemberList; - } + virtual MemberList const& members() const override; virtual TypePointer encodingType() const override; virtual TypePointer decodingType() const override; virtual TypePointer interfaceType(bool _inLibrary) const override; @@ -532,7 +529,8 @@ private: TypePointer m_baseType; bool m_hasDynamicLength = true; u256 m_length; - static const MemberList s_arrayTypeMemberList; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; }; /** @@ -736,7 +734,9 @@ public: Event, ///< syntactic sugar for LOG* SetGas, ///< modify the default gas value for the function call SetValue, ///< modify the default value transfer for the function call - BlockHash ///< BLOCKHASH + BlockHash, ///< BLOCKHASH + ArrayPush, ///< .push() to a dynamically sized array in storage + ByteArrayPush ///< .push() to a dynamically sized byte array in storage }; virtual Category category() const override { return Category::Function; } |