mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Memory structs.
This commit is contained in:
parent
ad459207a3
commit
cad3379306
@ -436,20 +436,55 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
}
|
}
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
{
|
{
|
||||||
//@todo we can probably use some of the code for arrays here.
|
|
||||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
solAssert(targetTypeCategory == stackTypeCategory, "");
|
||||||
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
auto& targetType = dynamic_cast<StructType const&>(_targetType);
|
||||||
auto& stackType = dynamic_cast<StructType const&>(_typeOnStack);
|
auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
|
||||||
solAssert(
|
solAssert(
|
||||||
targetType.location() == DataLocation::Storage &&
|
targetType.location() != DataLocation::CallData &&
|
||||||
stackType.location() == DataLocation::Storage,
|
typeOnStack.location() != DataLocation::CallData
|
||||||
"Non-storage structs not yet implemented."
|
, "");
|
||||||
);
|
switch (targetType.location())
|
||||||
|
{
|
||||||
|
case DataLocation::Storage:
|
||||||
|
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
||||||
solAssert(
|
solAssert(
|
||||||
targetType.isPointer(),
|
targetType.isPointer() &&
|
||||||
"Type conversion to non-pointer struct requested."
|
typeOnStack.location() == DataLocation::Storage,
|
||||||
|
"Invalid conversion to storage type."
|
||||||
);
|
);
|
||||||
break;
|
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:
|
default:
|
||||||
// All other types should not be convertible to non-equal types.
|
// All other types should not be convertible to non-equal types.
|
||||||
|
@ -155,8 +155,6 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
TypePointer type = _assignment.getRightHandSide().getType();
|
TypePointer type = _assignment.getRightHandSide().getType();
|
||||||
if (!_assignment.getType()->dataStoredIn(DataLocation::Storage))
|
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());
|
utils().convertType(*type, *_assignment.getType());
|
||||||
type = _assignment.getType();
|
type = _assignment.getType();
|
||||||
}
|
}
|
||||||
@ -691,7 +689,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
case DataLocation::Memory:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
76
LValue.cpp
76
LValue.cpp
@ -47,6 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
|||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
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)
|
for (unsigned i = 0; i < m_size; ++i)
|
||||||
m_context << eth::dupInstruction(stackPos + 1);
|
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
|
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
|
||||||
{
|
{
|
||||||
|
CompilerUtils utils(m_context);
|
||||||
// stack: value storage_key storage_offset
|
// stack: value storage_key storage_offset
|
||||||
if (m_dataType.isValueType())
|
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&>(m_dataType),
|
||||||
dynamic_cast<ArrayType const&>(_sourceType));
|
dynamic_cast<ArrayType const&>(_sourceType));
|
||||||
if (_move)
|
if (_move)
|
||||||
CompilerUtils(m_context).popStackElement(_sourceType);
|
utils.popStackElement(_sourceType);
|
||||||
}
|
}
|
||||||
else if (m_dataType.getCategory() == Type::Category::Struct)
|
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
|
// note that we have structs, so offsets should be zero and are ignored
|
||||||
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
||||||
|
auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
|
||||||
solAssert(
|
solAssert(
|
||||||
structType.structDefinition() ==
|
structType.structDefinition() == sourceType.structDefinition(),
|
||||||
dynamic_cast<StructType const&>(_sourceType).structDefinition(),
|
|
||||||
"Struct assignment with conversion."
|
"Struct assignment with conversion."
|
||||||
);
|
);
|
||||||
|
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
||||||
for (auto const& member: structType.getMembers())
|
for (auto const& member: structType.getMembers())
|
||||||
{
|
{
|
||||||
// assign each member that is not a mapping
|
// assign each member that is not a mapping
|
||||||
TypePointer const& memberType = member.type;
|
TypePointer const& memberType = member.type;
|
||||||
if (memberType->getCategory() == Type::Category::Mapping)
|
if (memberType->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.name);
|
TypePointer sourceMemberType = sourceType.getMemberType(member.name);
|
||||||
m_context
|
if (sourceType.location() == DataLocation::Storage)
|
||||||
<< offsets.first << u256(offsets.second)
|
{
|
||||||
<< eth::Instruction::DUP6 << eth::Instruction::DUP3
|
// stack layout: source_ref source_offset target_ref target_offset
|
||||||
<< eth::Instruction::ADD << eth::Instruction::DUP2;
|
pair<u256, unsigned> const& offsets = sourceType.getStorageOffsetsOfMember(member.name);
|
||||||
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off
|
m_context << offsets.first << eth::Instruction::DUP5 << eth::Instruction::ADD;
|
||||||
StorageItem(m_context, *memberType).retrieveValue(_location, true);
|
m_context << u256(offsets.second);
|
||||||
// stack: source_ref source_off target_ref target_off member_offset source_value...
|
// stack: source_ref source_off target_ref target_off source_member_ref source_member_off
|
||||||
solAssert(
|
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||||
4 + memberType->getSizeOnStack() <= 16,
|
// stack: source_ref source_off target_ref target_off source_value...
|
||||||
"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;
|
|
||||||
}
|
}
|
||||||
if (_move)
|
|
||||||
m_context
|
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP
|
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP;
|
|
||||||
else
|
else
|
||||||
m_context
|
{
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::POP
|
solAssert(sourceType.location() == DataLocation::Memory, "");
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::POP;
|
// 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 << 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)
|
||||||
|
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
|
else
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
|
2
LValue.h
2
LValue.h
@ -103,7 +103,7 @@ private:
|
|||||||
class MemoryItem: public LValue
|
class MemoryItem: public LValue
|
||||||
{
|
{
|
||||||
public:
|
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 unsigned sizeOnStack() const override { return 1; }
|
||||||
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
|
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
|
||||||
virtual void storeValue(
|
virtual void storeValue(
|
||||||
|
32
Types.cpp
32
Types.cpp
@ -999,6 +999,15 @@ unsigned StructType::getCalldataEncodedSize(bool _padded) const
|
|||||||
return size;
|
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
|
u256 StructType::getStorageSize() const
|
||||||
{
|
{
|
||||||
return max<u256>(1, getMembers().getStorageSize());
|
return max<u256>(1, getMembers().getStorageSize());
|
||||||
@ -1012,6 +1021,17 @@ bool StructType::canLiveOutsideStorage() const
|
|||||||
return true;
|
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 StructType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
string ret = "struct " + m_struct.getName();
|
string ret = "struct " + m_struct.getName();
|
||||||
@ -1054,6 +1074,18 @@ pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const&
|
|||||||
return *offsets;
|
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
|
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
||||||
{
|
{
|
||||||
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
|
||||||
|
5
Types.h
5
Types.h
@ -543,14 +543,14 @@ class StructType: public ReferenceType
|
|||||||
public:
|
public:
|
||||||
virtual Category getCategory() const override { return Category::Struct; }
|
virtual Category getCategory() const override { return Category::Struct; }
|
||||||
explicit StructType(StructDefinition const& _struct):
|
explicit StructType(StructDefinition const& _struct):
|
||||||
//@todo only storage until we have non-storage structs
|
|
||||||
ReferenceType(DataLocation::Storage), m_struct(_struct) {}
|
ReferenceType(DataLocation::Storage), m_struct(_struct) {}
|
||||||
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded) const override;
|
virtual unsigned getCalldataEncodedSize(bool _padded) const override;
|
||||||
|
u256 memorySize() const;
|
||||||
virtual u256 getStorageSize() const override;
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() 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 std::string toString(bool _short) const override;
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
@ -558,6 +558,7 @@ public:
|
|||||||
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
|
|
||||||
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
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; }
|
StructDefinition const& structDefinition() const { return m_struct; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user