Compute packing offsets.

This commit is contained in:
chriseth 2015-03-13 10:52:34 +01:00
parent fff3f98f58
commit 7f64584b7f
2 changed files with 114 additions and 11 deletions

View File

@ -35,6 +35,56 @@ namespace dev
namespace solidity
{
std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
{
if (!m_storageOffsets)
{
bigint slotOffset = 0;
unsigned byteOffset = 0;
map<string, pair<u256, unsigned>> offsets;
for (auto const& nameAndType: m_memberTypes)
{
TypePointer const& type = nameAndType.second;
if (!type->canBeStored())
continue;
if (byteOffset + type->getStorageBytes() > 32)
{
// would overflow, go to next slot
++slotOffset;
byteOffset = 0;
}
if (slotOffset >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
offsets[nameAndType.first] = make_pair(u256(slotOffset), byteOffset);
solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
byteOffset += type->getStorageBytes();
else
{
slotOffset += type->getStorageSize();
byteOffset = 0;
}
}
if (byteOffset > 0)
++slotOffset;
if (slotOffset >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
m_storageSize = u256(slotOffset);
m_storageOffsets.reset(new decltype(offsets)(move(offsets)));
}
if (m_storageOffsets->count(_name))
return &((*m_storageOffsets)[_name]);
else
return nullptr;
}
u256 const& MemberList::getStorageSize() const
{
// trigger lazy computation
getMemberStorageOffset("");
return m_storageSize;
}
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{
char const* tokenCstr = Token::toString(_typeToken);
@ -751,12 +801,7 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
bigint size = 0;
for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize();
if (size >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Struct too large for storage."));
return max<u256>(1, u256(size));
return max<u256>(1, getMembers().getStorageSize());
}
bool StructType::canLiveOutsideStorage() const
@ -787,6 +832,7 @@ MemberList const& StructType::getMembers() const
u256 StructType::getStorageOffsetOfMember(string const& _name) const
{
//@todo cache member offset?
u256 offset;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
@ -811,6 +857,15 @@ bool EnumType::operator==(Type const& _other) const
return other.m_enum == m_enum;
}
unsigned EnumType::getStorageBytes() const
{
size_t elements = m_enum.getMembers().size();
if (elements <= 1)
return 1;
else
return dev::bytesRequired(elements - 1);
}
string EnumType::toString() const
{
return string("enum ") + m_enum.getName();
@ -955,6 +1010,13 @@ string FunctionType::toString() const
return name + ")";
}
u256 FunctionType::getStorageSize() const
{
BOOST_THROW_EXCEPTION(
InternalCompilerError()
<< errinfo_comment("Storage size of non-storable function type requested."));
}
unsigned FunctionType::getSizeOnStack() const
{
Location location = m_location;
@ -1077,6 +1139,13 @@ string MappingType::toString() const
return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")";
}
u256 VoidType::getStorageSize() const
{
BOOST_THROW_EXCEPTION(
InternalCompilerError()
<< errinfo_comment("Storage size of non-storable void type requested."));
}
bool TypeType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -1085,6 +1154,13 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType();
}
u256 TypeType::getStorageSize() const
{
BOOST_THROW_EXCEPTION(
InternalCompilerError()
<< errinfo_comment("Storage size of non-storable type type requested."));
}
MemberList const& TypeType::getMembers() const
{
// We need to lazy-initialize it because of recursive references.
@ -1122,6 +1198,13 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
swap(params, m_parameterTypes);
}
u256 ModifierType::getStorageSize() const
{
BOOST_THROW_EXCEPTION(
InternalCompilerError()
<< errinfo_comment("Storage size of non-storable type type requested."));
}
bool ModifierType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())

30
Types.h
View File

@ -60,12 +60,19 @@ public:
return it.second;
return TypePointer();
}
/// @returns the offset of the given member in storage slots and bytes inside a slot or
/// a nullptr if the member is not part of storage.
std::pair<u256, unsigned> const* getMemberStorageOffset(std::string const& _name) const;
/// @returns the number of storage slots occupied by the members.
u256 const& getStorageSize() const;
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
private:
MemberMap m_memberTypes;
mutable u256 m_storageSize = 0;
mutable std::unique_ptr<std::map<std::string, std::pair<u256, unsigned>>> m_storageOffsets;
};
/**
@ -97,6 +104,8 @@ public:
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
/// Calculates the
virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
@ -126,9 +135,15 @@ public:
unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); }
/// @returns true if the type is dynamically encoded in calldata
virtual bool isDynamicallySized() const { return false; }
/// @returns number of bytes required to hold this value in storage.
/// @returns the number of storage slots required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
/// Multiple small types can be packed into a single storage slot. If such a packing is possible
/// this function @returns the size in bytes smaller than 32. Data is moved to the next slot if
/// it does not fit.
/// In order to avoid computation at runtime of whether such moving is necessary, structs and
/// array data (not each element) always start a new slot.
virtual unsigned getStorageBytes() const { return 32; }
/// Returns true if the type can be stored in storage.
virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
@ -179,6 +194,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; }
virtual unsigned getStorageBytes() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; }
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
@ -251,6 +267,7 @@ public:
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
virtual unsigned getStorageBytes() const override { return m_bytes; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); }
@ -275,6 +292,7 @@ public:
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; }
virtual unsigned getStorageBytes() const override { return 1; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; }
@ -348,6 +366,7 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getStorageBytes() const override { return 20; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override;
@ -411,6 +430,7 @@ public:
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getSizeOnStack() const override { return 1; }
virtual unsigned getStorageBytes() const override;
virtual std::string toString() const override;
virtual bool isValueType() const override { return true; }
@ -480,7 +500,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
virtual MemberList const& getMembers() const override;
@ -565,7 +585,7 @@ public:
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual std::string toString() const override { return "void"; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
};
@ -585,7 +605,7 @@ public:
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
@ -611,7 +631,7 @@ public:
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override { return 0; }
virtual bool operator==(Type const& _other) const override;