diff options
-rw-r--r-- | CompilerUtils.cpp | 55 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 5 | ||||
-rw-r--r-- | LValue.cpp | 74 | ||||
-rw-r--r-- | LValue.h | 2 | ||||
-rw-r--r-- | Types.cpp | 32 | ||||
-rw-r--r-- | Types.h | 5 |
6 files changed, 128 insertions, 45 deletions
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 208d7cec..eab20e0e 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -436,19 +436,54 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp } case Type::Category::Struct: { - //@todo we can probably use some of the code for arrays here. solAssert(targetTypeCategory == stackTypeCategory, ""); auto& targetType = dynamic_cast<StructType const&>(_targetType); - auto& stackType = dynamic_cast<StructType const&>(_typeOnStack); + auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack); solAssert( - targetType.location() == DataLocation::Storage && - stackType.location() == DataLocation::Storage, - "Non-storage structs not yet implemented." - ); - solAssert( - targetType.isPointer(), - "Type conversion to non-pointer struct requested." - ); + targetType.location() != DataLocation::CallData && + typeOnStack.location() != DataLocation::CallData + , ""); + switch (targetType.location()) + { + case DataLocation::Storage: + // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. + solAssert( + targetType.isPointer() && + typeOnStack.location() == DataLocation::Storage, + "Invalid conversion to storage type." + ); + break; + case DataLocation::Memory: + // Copy the array to a free position in memory, unless it is already in memory. + if (typeOnStack.location() != DataLocation::Memory) + { + solAssert(typeOnStack.location() == DataLocation::Storage, ""); + // stack: <source ref> <source byte offset> + m_context << eth::Instruction::POP; + m_context << typeOnStack.memorySize(); + allocateMemory(); + m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; + // stack: <memory ptr> <source ref> <memory ptr> + for (auto const& member: typeOnStack.getMembers()) + { + if (!member.type->canLiveOutsideStorage()) + continue; + pair<u256, unsigned> const& offsets = typeOnStack.getStorageOffsetsOfMember(member.name); + m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD; + m_context << u256(offsets.second); + StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true); + TypePointer targetMemberType = targetType.getMemberType(member.name); + solAssert(!!targetMemberType, "Member not found in target type."); + convertType(*member.type, *targetMemberType, true); + storeInMemoryDynamic(*targetMemberType, true); + } + m_context << eth::Instruction::POP << eth::Instruction::POP; + } + break; + case DataLocation::CallData: + solAssert(false, ""); + break; + } break; } default: diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 59907b14..b36c7283 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -155,8 +155,6 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) TypePointer type = _assignment.getRightHandSide().getType(); if (!_assignment.getType()->dataStoredIn(DataLocation::Storage)) { - //@todo we should delay conversion here if RHS is not in memory, LHS is a MemoryItem - // and not dynamically-sized. utils().convertType(*type, *_assignment.getType()); type = _assignment.getType(); } @@ -691,7 +689,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) } case DataLocation::Memory: { - solAssert(false, "Member access for memory structs not yet implemented."); + m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD; + setLValue<MemoryItem>(_memberAccess, *_memberAccess.getType()); break; } default: @@ -47,6 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep, try removing local variables.") ); + solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch."); for (unsigned i = 0; i < m_size; ++i) m_context << eth::dupInstruction(stackPos + 1); } @@ -175,6 +176,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const { + CompilerUtils utils(m_context); // stack: value storage_key storage_offset if (m_dataType.isValueType()) { @@ -238,52 +240,66 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc dynamic_cast<ArrayType const&>(m_dataType), dynamic_cast<ArrayType const&>(_sourceType)); if (_move) - CompilerUtils(m_context).popStackElement(_sourceType); + utils.popStackElement(_sourceType); } else if (m_dataType.getCategory() == Type::Category::Struct) { - // stack layout: source_ref source_offset target_ref target_offset + // stack layout: source_ref [source_offset] target_ref target_offset // note that we have structs, so offsets should be zero and are ignored auto const& structType = dynamic_cast<StructType const&>(m_dataType); + auto const& sourceType = dynamic_cast<StructType const&>(_sourceType); solAssert( - structType.structDefinition() == - dynamic_cast<StructType const&>(_sourceType).structDefinition(), + structType.structDefinition() == sourceType.structDefinition(), "Struct assignment with conversion." ); + solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); for (auto const& member: structType.getMembers()) { // assign each member that is not a mapping TypePointer const& memberType = member.type; if (memberType->getCategory() == Type::Category::Mapping) continue; + TypePointer sourceMemberType = sourceType.getMemberType(member.name); + if (sourceType.location() == DataLocation::Storage) + { + // stack layout: source_ref source_offset target_ref target_offset + pair<u256, unsigned> const& offsets = sourceType.getStorageOffsetsOfMember(member.name); + m_context << offsets.first << eth::Instruction::DUP5 << eth::Instruction::ADD; + m_context << u256(offsets.second); + // stack: source_ref source_off target_ref target_off source_member_ref source_member_off + StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack: source_ref source_off target_ref target_off source_value... + } + else + { + solAssert(sourceType.location() == DataLocation::Memory, ""); + // stack layout: source_ref target_ref target_offset + TypePointer sourceMemberType = sourceType.getMemberType(member.name); + m_context << sourceType.memoryOffsetOfMember(member.name); + m_context << eth::Instruction::DUP4 << eth::Instruction::ADD; + MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack layout: source_ref target_ref target_offset source_value... + } + unsigned stackSize = sourceMemberType->getSizeOnStack(); pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.name); - m_context - << offsets.first << u256(offsets.second) - << eth::Instruction::DUP6 << eth::Instruction::DUP3 - << eth::Instruction::ADD << eth::Instruction::DUP2; - // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off - StorageItem(m_context, *memberType).retrieveValue(_location, true); - // stack: source_ref source_off target_ref target_off member_offset source_value... - solAssert( - 4 + memberType->getSizeOnStack() <= 16, - "Stack too deep, try removing local varibales." - ); - m_context - << eth::dupInstruction(4 + memberType->getSizeOnStack()) - << eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD - << eth::dupInstruction(2 + memberType->getSizeOnStack()); - // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_value... target_member_ref target_member_byte_off - StorageItem(m_context, *memberType).storeValue(*memberType, _location, true); - m_context << eth::Instruction::POP << eth::Instruction::POP; + m_context << eth::dupInstruction(2 + stackSize) << offsets.first << eth::Instruction::ADD; + m_context << u256(offsets.second); + // stack: source_ref [source_off] target_ref target_off source_value... target_member_ref target_member_byte_off + StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); } + // stack layout: source_ref [source_offset] target_ref target_offset + unsigned sourceStackSize = sourceType.getSizeOnStack(); if (_move) - m_context - << eth::Instruction::POP << eth::Instruction::POP - << eth::Instruction::POP << eth::Instruction::POP; - else - m_context - << eth::Instruction::SWAP2 << eth::Instruction::POP - << eth::Instruction::SWAP2 << eth::Instruction::POP; + utils.popStackSlots(2 + sourceType.getSizeOnStack()); + else if (sourceType.getSizeOnStack() >= 1) + { + // remove the source ref + solAssert(sourceStackSize <= 2, ""); + m_context << eth::swapInstruction(sourceStackSize); + if (sourceStackSize == 2) + m_context << eth::Instruction::POP; + m_context << eth::Instruction::SWAP2 << eth::Instruction::POP; + } } else BOOST_THROW_EXCEPTION( @@ -103,7 +103,7 @@ private: class MemoryItem: public LValue { public: - MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded); + MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true); virtual unsigned sizeOnStack() const override { return 1; } virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; virtual void storeValue( @@ -999,6 +999,15 @@ unsigned StructType::getCalldataEncodedSize(bool _padded) const return size; } +u256 StructType::memorySize() const +{ + u256 size; + for (auto const& member: getMembers()) + if (member.type->canLiveOutsideStorage()) + size += member.type->memoryHeadSize(); + return size; +} + u256 StructType::getStorageSize() const { return max<u256>(1, getMembers().getStorageSize()); @@ -1012,6 +1021,17 @@ bool StructType::canLiveOutsideStorage() const return true; } +unsigned StructType::getSizeOnStack() const +{ + switch (location()) + { + case DataLocation::Storage: + return 2; // slot and offset + default: + return 1; + } +} + string StructType::toString(bool _short) const { string ret = "struct " + m_struct.getName(); @@ -1054,6 +1074,18 @@ pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& return *offsets; } +u256 StructType::memoryOffsetOfMember(string const& _name) const +{ + u256 offset; + for (auto const& member: getMembers()) + if (member.name == _name) + return offset; + else + offset += member.type->memoryHeadSize(); + solAssert(false, "Member not found in struct."); + return 0; +} + TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); @@ -543,14 +543,14 @@ class StructType: public ReferenceType public: virtual Category getCategory() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct): - //@todo only storage until we have non-storage structs ReferenceType(DataLocation::Storage), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool operator==(Type const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override; + u256 memorySize() const; virtual u256 getStorageSize() const override; virtual bool canLiveOutsideStorage() const override; - virtual unsigned getSizeOnStack() const override { return 2; } + virtual unsigned getSizeOnStack() const override; virtual std::string toString(bool _short) const override; virtual MemberList const& getMembers() const override; @@ -558,6 +558,7 @@ public: TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const; + u256 memoryOffsetOfMember(std::string const& _name) const; StructDefinition const& structDefinition() const { return m_struct; } |