mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Fetch and store packed values.
This commit is contained in:
parent
7f64584b7f
commit
02595abf6a
11
Compiler.cpp
11
Compiler.cpp
@ -274,10 +274,19 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
|||||||
|
|
||||||
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
|
vector<VariableDeclaration const*> variables;
|
||||||
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
|
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
|
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
|
||||||
if (!variable->isConstant())
|
if (!variable->isConstant())
|
||||||
m_context.addStateVariable(*variable);
|
variables.push_back(variable.get());
|
||||||
|
TypePointers types;
|
||||||
|
for (auto variable: variables)
|
||||||
|
types.push_back(variable->getType());
|
||||||
|
StorageOffsets offsets;
|
||||||
|
offsets.computeOffsets(types);
|
||||||
|
for (size_t index = 0; index < variables.size(); ++index)
|
||||||
|
if (auto const* offset = offsets.getOffset(index))
|
||||||
|
m_context.addStateVariable(*variables[index], offset->first, offset->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
|
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
|
||||||
|
@ -37,15 +37,13 @@ void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaratio
|
|||||||
m_magicGlobals.insert(&_declaration);
|
m_magicGlobals.insert(&_declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
void CompilerContext::addStateVariable(
|
||||||
|
VariableDeclaration const& _declaration,
|
||||||
|
u256 const& _storageOffset,
|
||||||
|
unsigned _byteOffset
|
||||||
|
)
|
||||||
{
|
{
|
||||||
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset);
|
||||||
bigint newSize = bigint(m_stateVariablesSize) + _declaration.getType()->getStorageSize();
|
|
||||||
if (newSize >= bigint(1) << 256)
|
|
||||||
BOOST_THROW_EXCEPTION(TypeError()
|
|
||||||
<< errinfo_comment("State variable does not fit in storage.")
|
|
||||||
<< errinfo_sourceLocation(_declaration.getLocation()));
|
|
||||||
m_stateVariablesSize = u256(newSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::startFunction(Declaration const& _function)
|
void CompilerContext::startFunction(Declaration const& _function)
|
||||||
@ -170,7 +168,7 @@ unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
|||||||
return m_asm.deposit() - _offset - 1;
|
return m_asm.deposit() - _offset - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
pair<u256, unsigned> CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
||||||
{
|
{
|
||||||
auto it = m_stateVariables.find(&_declaration);
|
auto it = m_stateVariables.find(&_declaration);
|
||||||
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
|
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <utility>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmcore/Instruction.h>
|
||||||
#include <libevmcore/Assembly.h>
|
#include <libevmcore/Assembly.h>
|
||||||
#include <libsolidity/ASTForward.h>
|
#include <libsolidity/ASTForward.h>
|
||||||
@ -42,7 +43,7 @@ class CompilerContext
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
||||||
void addStateVariable(VariableDeclaration const& _declaration);
|
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
||||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
||||||
void removeVariable(VariableDeclaration const& _declaration);
|
void removeVariable(VariableDeclaration const& _declaration);
|
||||||
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
||||||
@ -82,7 +83,7 @@ public:
|
|||||||
/// Converts an offset relative to the current stack height to a value that can be used later
|
/// Converts an offset relative to the current stack height to a value that can be used later
|
||||||
/// with baseToCurrentStackOffset to point to the same stack element.
|
/// with baseToCurrentStackOffset to point to the same stack element.
|
||||||
unsigned currentToBaseStackOffset(unsigned _offset) const;
|
unsigned currentToBaseStackOffset(unsigned _offset) const;
|
||||||
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
|
std::pair<u256, unsigned> getStorageLocationOfVariable(Declaration const& _declaration) const;
|
||||||
|
|
||||||
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
||||||
@ -144,10 +145,8 @@ private:
|
|||||||
std::set<Declaration const*> m_magicGlobals;
|
std::set<Declaration const*> m_magicGlobals;
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
||||||
/// Size of the state variables, offset of next variable to be added.
|
|
||||||
u256 m_stateVariablesSize = 0;
|
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<Declaration const*, u256> m_stateVariables;
|
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
/// Offsets of local variables on the stack (relative to stack base).
|
/// Offsets of local variables on the stack (relative to stack base).
|
||||||
std::map<Declaration const*, unsigned> m_localVariables;
|
std::map<Declaration const*, unsigned> m_localVariables;
|
||||||
/// Labels pointing to the entry points of functions.
|
/// Labels pointing to the entry points of functions.
|
||||||
|
@ -199,7 +199,6 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
return numBytes;
|
return numBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
||||||
{
|
{
|
||||||
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
||||||
|
@ -67,9 +67,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
|
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
|
||||||
|
|
||||||
// retrieve the position of the variable
|
// retrieve the position of the variable
|
||||||
m_context << m_context.getStorageLocationOfVariable(_varDecl);
|
auto const& location = m_context.getStorageLocationOfVariable(_varDecl);
|
||||||
TypePointer returnType = _varDecl.getType();
|
m_context << location.first;
|
||||||
|
|
||||||
|
TypePointer returnType = _varDecl.getType();
|
||||||
for (TypePointer const& paramType: paramTypes)
|
for (TypePointer const& paramType: paramTypes)
|
||||||
{
|
{
|
||||||
// move offset to memory
|
// move offset to memory
|
||||||
@ -81,7 +82,6 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
returnType = dynamic_cast<MappingType const&>(*returnType).getValueType();
|
returnType = dynamic_cast<MappingType const&>(*returnType).getValueType();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context << u256(0); // @todo
|
|
||||||
unsigned retSizeOnStack = 0;
|
unsigned retSizeOnStack = 0;
|
||||||
solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
|
solAssert(accessorType.getReturnParameterTypes().size() >= 1, "");
|
||||||
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
|
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
|
||||||
@ -93,21 +93,20 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
{
|
{
|
||||||
if (types[i]->getCategory() == Type::Category::Mapping)
|
if (types[i]->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
m_context
|
pair<u256, unsigned> const& offsets = structType->getStorageOffsetsOfMember(names[i]);
|
||||||
<< eth::Instruction::DUP2 << structType->getStorageOffsetOfMember(names[i])
|
m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
|
||||||
<< eth::Instruction::ADD;
|
|
||||||
m_context << u256(0); //@todo
|
|
||||||
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true);
|
||||||
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
|
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
retSizeOnStack += types[i]->getSizeOnStack();
|
retSizeOnStack += types[i]->getSizeOnStack();
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// simple value
|
// simple value
|
||||||
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
|
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
|
||||||
|
m_context << u256(location.second);
|
||||||
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
|
||||||
retSizeOnStack = returnType->getSizeOnStack();
|
retSizeOnStack = returnType->getSizeOnStack();
|
||||||
}
|
}
|
||||||
@ -666,10 +665,9 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
{
|
{
|
||||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
||||||
m_context << eth::Instruction::POP; //@todo
|
m_context << eth::Instruction::POP; // structs always align to new slot
|
||||||
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
|
pair<u256, unsigned> const& offsets = type.getStorageOffsetsOfMember(member);
|
||||||
//@todo
|
m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second);
|
||||||
m_context << u256(0);
|
|
||||||
setLValueToStorageItem(_memberAccess);
|
setLValueToStorageItem(_memberAccess);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
111
LValue.cpp
111
LValue.cpp
@ -77,7 +77,8 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const
|
|||||||
StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
|
StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
|
||||||
StorageItem(_compilerContext, *_declaration.getType())
|
StorageItem(_compilerContext, *_declaration.getType())
|
||||||
{
|
{
|
||||||
m_context << m_context.getStorageLocationOfVariable(_declaration) << u256(0);
|
auto const& location = m_context.getStorageLocationOfVariable(_declaration);
|
||||||
|
m_context << location.first << u256(location.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
||||||
@ -86,7 +87,6 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
|||||||
if (m_dataType.isValueType())
|
if (m_dataType.isValueType())
|
||||||
{
|
{
|
||||||
solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), "");
|
solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), "");
|
||||||
//@todo the meaning of getStorageSize() probably changes
|
|
||||||
solAssert(m_dataType.getStorageSize() == 1, "Invalid storage size.");
|
solAssert(m_dataType.getStorageSize() == 1, "Invalid storage size.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,12 +98,18 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
return; // no distinction between value and reference for non-value types
|
return; // no distinction between value and reference for non-value types
|
||||||
if (!_remove)
|
if (!_remove)
|
||||||
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
|
m_context << eth::Instruction::POP << eth::Instruction::SLOAD;
|
||||||
|
else
|
||||||
|
{
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1
|
<< eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1
|
||||||
<< u256(2) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
<< u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
//@todo higher order bits might be dirty. Is this bad?
|
if (m_dataType.getCategory() == Type::Category::FixedBytes)
|
||||||
//@todo this does not work for types that are left-aligned on the stack.
|
m_context << (u256(0x1) << (256 - 8 * m_dataType.getStorageBytes())) << eth::Instruction::MUL;
|
||||||
// make those types right-aligned?
|
else
|
||||||
|
m_context << ((u256(0x1) << (8 * m_dataType.getStorageBytes())) - 1) << eth::Instruction::AND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
|
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
|
||||||
@ -111,13 +117,44 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
// stack: value storage_key storage_offset
|
// stack: value storage_key storage_offset
|
||||||
if (m_dataType.isValueType())
|
if (m_dataType.isValueType())
|
||||||
{
|
{
|
||||||
//@todo OR the value into the storage like it is done for ByteArrayElement
|
solAssert(m_dataType.getStorageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
|
solAssert(m_dataType.getStorageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
|
{
|
||||||
|
// offset should be zero
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
if (!_move)
|
||||||
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
||||||
|
m_context << eth::Instruction::SSTORE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// OR the value into the other values in the storage slot
|
||||||
|
m_context << u256(0x100) << eth::Instruction::EXP;
|
||||||
|
// stack: value storage_ref multiplier
|
||||||
|
// fetch old value
|
||||||
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
||||||
|
// stack: value storege_ref multiplier old_full_value
|
||||||
|
// clear bytes in old value
|
||||||
m_context
|
m_context
|
||||||
<< u256(2) << eth::Instruction::EXP << eth::Instruction::DUP3 << eth::Instruction::MUL
|
<< eth::Instruction::DUP2 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1)
|
||||||
<< eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
<< eth::Instruction::MUL;
|
||||||
|
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
||||||
|
// stack: value storage_ref multiplier cleared_value
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::SWAP1 << eth::Instruction::DUP4;
|
||||||
|
// stack: value storage_ref cleared_value multiplier value
|
||||||
|
if (m_dataType.getCategory() == Type::Category::FixedBytes)
|
||||||
|
m_context
|
||||||
|
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(m_dataType).getNumBytes()))
|
||||||
|
<< eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
|
m_context << eth::Instruction::MUL << eth::Instruction::OR;
|
||||||
|
// stack: value storage_ref updated_value
|
||||||
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
@ -134,28 +171,31 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
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
|
||||||
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
||||||
solAssert(structType == _sourceType, "Struct assignment with conversion.");
|
solAssert(structType == _sourceType, "Struct assignment with conversion.");
|
||||||
for (auto const& member: structType.getMembers())
|
for (auto const& member: structType.getMembers())
|
||||||
{
|
{
|
||||||
//@todo actually use offsets
|
|
||||||
// assign each member that is not a mapping
|
// assign each member that is not a mapping
|
||||||
TypePointer const& memberType = member.second;
|
TypePointer const& memberType = member.second;
|
||||||
if (memberType->getCategory() == Type::Category::Mapping)
|
if (memberType->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
m_context << structType.getStorageOffsetOfMember(member.first)
|
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.first);
|
||||||
<< eth::Instruction::DUP5 << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context
|
||||||
m_context << u256(0); // zero offset
|
<< offsets.first << u256(offsets.second)
|
||||||
// stack: source_ref source_off target_ref target_off member_offset source_member_ref source_member_off
|
<< 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);
|
StorageItem(m_context, *memberType).retrieveValue(_location, true);
|
||||||
// stack: source_ref source_off target_ref target_off member_offset source_value...
|
// stack: source_ref source_off target_ref target_off member_offset source_value...
|
||||||
solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep.");
|
solAssert(4 + memberType->getSizeOnStack() <= 16, "Stack too deep.");
|
||||||
m_context << eth::dupInstruction(3 + memberType->getSizeOnStack())
|
m_context
|
||||||
<< eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD;
|
<< eth::dupInstruction(4 + memberType->getSizeOnStack())
|
||||||
// stack: source_ref source_off target_ref target_off member_offset source_value... target_member_ref
|
<< eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD
|
||||||
m_context << u256(0); // zero offset
|
<< 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);
|
StorageItem(m_context, *memberType).storeValue(*memberType, _location, true);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context
|
m_context
|
||||||
@ -185,6 +225,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
else if (m_dataType.getCategory() == Type::Category::Struct)
|
else if (m_dataType.getCategory() == Type::Category::Struct)
|
||||||
{
|
{
|
||||||
// stack layout: storage_key storage_offset
|
// stack layout: storage_key storage_offset
|
||||||
|
// @todo this can be improved for packed types
|
||||||
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
||||||
for (auto const& member: structType.getMembers())
|
for (auto const& member: structType.getMembers())
|
||||||
{
|
{
|
||||||
@ -192,11 +233,10 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
TypePointer const& memberType = member.second;
|
TypePointer const& memberType = member.second;
|
||||||
if (memberType->getCategory() == Type::Category::Mapping)
|
if (memberType->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
// @todo actually use offset
|
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.first);
|
||||||
m_context
|
m_context
|
||||||
<< structType.getStorageOffsetOfMember(member.first)
|
<< offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::ADD;
|
<< u256(offsets.second);
|
||||||
m_context << u256(0);
|
|
||||||
StorageItem(m_context, *memberType).setToZero();
|
StorageItem(m_context, *memberType).setToZero();
|
||||||
}
|
}
|
||||||
if (_removeReference)
|
if (_removeReference)
|
||||||
@ -208,7 +248,28 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
// @todo actually use offset
|
// @todo actually use offset
|
||||||
if (!_removeReference)
|
if (!_removeReference)
|
||||||
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
m_context << eth::Instruction::POP << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
|
{
|
||||||
|
// offset should be zero
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::POP << u256(0)
|
||||||
|
<< eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_context << u256(0x100) << eth::Instruction::EXP;
|
||||||
|
// stack: storage_ref multiplier
|
||||||
|
// fetch old value
|
||||||
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
||||||
|
// stack: storege_ref multiplier old_full_value
|
||||||
|
// clear bytes in old value
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::SWAP1 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1)
|
||||||
|
<< eth::Instruction::MUL;
|
||||||
|
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
||||||
|
// stack: storage_ref cleared_value
|
||||||
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
57
Types.cpp
57
Types.cpp
@ -35,16 +35,14 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
|
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
||||||
{
|
{
|
||||||
if (!m_storageOffsets)
|
|
||||||
{
|
|
||||||
bigint slotOffset = 0;
|
bigint slotOffset = 0;
|
||||||
unsigned byteOffset = 0;
|
unsigned byteOffset = 0;
|
||||||
map<string, pair<u256, unsigned>> offsets;
|
map<size_t, pair<u256, unsigned>> offsets;
|
||||||
for (auto const& nameAndType: m_memberTypes)
|
for (size_t i = 0; i < _types.size(); ++i)
|
||||||
{
|
{
|
||||||
TypePointer const& type = nameAndType.second;
|
TypePointer const& type = _types[i];
|
||||||
if (!type->canBeStored())
|
if (!type->canBeStored())
|
||||||
continue;
|
continue;
|
||||||
if (byteOffset + type->getStorageBytes() > 32)
|
if (byteOffset + type->getStorageBytes() > 32)
|
||||||
@ -55,7 +53,7 @@ std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const
|
|||||||
}
|
}
|
||||||
if (slotOffset >= bigint(1) << 256)
|
if (slotOffset >= bigint(1) << 256)
|
||||||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
||||||
offsets[nameAndType.first] = make_pair(u256(slotOffset), byteOffset);
|
offsets[i] = make_pair(u256(slotOffset), byteOffset);
|
||||||
solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
|
solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
|
||||||
if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
|
if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
|
||||||
byteOffset += type->getStorageBytes();
|
byteOffset += type->getStorageBytes();
|
||||||
@ -70,19 +68,39 @@ std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const
|
|||||||
if (slotOffset >= bigint(1) << 256)
|
if (slotOffset >= bigint(1) << 256)
|
||||||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
||||||
m_storageSize = u256(slotOffset);
|
m_storageSize = u256(slotOffset);
|
||||||
m_storageOffsets.reset(new decltype(offsets)(move(offsets)));
|
swap(m_offsets, offsets);
|
||||||
}
|
}
|
||||||
if (m_storageOffsets->count(_name))
|
|
||||||
return &((*m_storageOffsets)[_name]);
|
pair<u256, unsigned> const* StorageOffsets::getOffset(size_t _index) const
|
||||||
|
{
|
||||||
|
if (m_offsets.count(_index))
|
||||||
|
return &m_offsets.at(_index);
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
|
||||||
|
{
|
||||||
|
if (!m_storageOffsets)
|
||||||
|
{
|
||||||
|
TypePointers memberTypes;
|
||||||
|
memberTypes.reserve(m_memberTypes.size());
|
||||||
|
for (auto const& nameAndType: m_memberTypes)
|
||||||
|
memberTypes.push_back(nameAndType.second);
|
||||||
|
m_storageOffsets.reset(new StorageOffsets());
|
||||||
|
m_storageOffsets->computeOffsets(memberTypes);
|
||||||
|
}
|
||||||
|
for (size_t index = 0; index < m_memberTypes.size(); ++index)
|
||||||
|
if (m_memberTypes[index].first == _name)
|
||||||
|
return m_storageOffsets->getOffset(index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
u256 const& MemberList::getStorageSize() const
|
u256 const& MemberList::getStorageSize() const
|
||||||
{
|
{
|
||||||
// trigger lazy computation
|
// trigger lazy computation
|
||||||
getMemberStorageOffset("");
|
getMemberStorageOffset("");
|
||||||
return m_storageSize;
|
return m_storageOffsets->getStorageSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
||||||
@ -830,18 +848,11 @@ MemberList const& StructType::getMembers() const
|
|||||||
return *m_members;
|
return *m_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 StructType::getStorageOffsetOfMember(string const& _name) const
|
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
||||||
{
|
{
|
||||||
|
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
||||||
//@todo cache member offset?
|
solAssert(offsets, "Storage offset of non-existing member requested.");
|
||||||
u256 offset;
|
return *offsets;
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
|
|
||||||
{
|
|
||||||
if (variable->getName() == _name)
|
|
||||||
return offset;
|
|
||||||
offset += variable->getType()->getStorageSize();
|
|
||||||
}
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
||||||
|
25
Types.h
25
Types.h
@ -43,6 +43,26 @@ using TypePointer = std::shared_ptr<Type const>;
|
|||||||
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
||||||
using TypePointers = std::vector<TypePointer>;
|
using TypePointers = std::vector<TypePointer>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to compute storage offsets of members of structs and contracts.
|
||||||
|
*/
|
||||||
|
class StorageOffsets
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Resets the StorageOffsets objects and determines the position in storage for each
|
||||||
|
/// of the elements of @a _types.
|
||||||
|
void computeOffsets(TypePointers const& _types);
|
||||||
|
/// @returns the offset of the given member, might be null if the member is not part of storage.
|
||||||
|
std::pair<u256, unsigned> const* getOffset(size_t _index) const;
|
||||||
|
/// @returns the total number of slots occupied by all members.
|
||||||
|
u256 const& getStorageSize() const { return m_storageSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
u256 m_storageSize;
|
||||||
|
std::map<size_t, std::pair<u256, unsigned>> m_offsets;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of members of a type.
|
* List of members of a type.
|
||||||
*/
|
*/
|
||||||
@ -71,8 +91,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
MemberMap m_memberTypes;
|
MemberMap m_memberTypes;
|
||||||
mutable u256 m_storageSize = 0;
|
mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
|
||||||
mutable std::unique_ptr<std::map<std::string, std::pair<u256, unsigned>>> m_storageOffsets;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,7 +430,7 @@ public:
|
|||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
u256 getStorageOffsetOfMember(std::string const& _name) const;
|
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StructDefinition const& m_struct;
|
StructDefinition const& m_struct;
|
||||||
|
Loading…
Reference in New Issue
Block a user