|
|
@ -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(m_dataType); |
|
|
|
} |
|
|
|
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, "Invalid stack size."); |
|
|
|
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( |
|
|
|