mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Compute packing offsets.
This commit is contained in:
parent
fff3f98f58
commit
7f64584b7f
95
Types.cpp
95
Types.cpp
@ -35,6 +35,56 @@ namespace dev
|
|||||||
namespace solidity
|
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)
|
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
||||||
{
|
{
|
||||||
char const* tokenCstr = Token::toString(_typeToken);
|
char const* tokenCstr = Token::toString(_typeToken);
|
||||||
@ -751,12 +801,7 @@ bool StructType::operator==(Type const& _other) const
|
|||||||
|
|
||||||
u256 StructType::getStorageSize() const
|
u256 StructType::getStorageSize() const
|
||||||
{
|
{
|
||||||
bigint size = 0;
|
return max<u256>(1, getMembers().getStorageSize());
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StructType::canLiveOutsideStorage() const
|
bool StructType::canLiveOutsideStorage() const
|
||||||
@ -787,6 +832,7 @@ MemberList const& StructType::getMembers() const
|
|||||||
|
|
||||||
u256 StructType::getStorageOffsetOfMember(string const& _name) const
|
u256 StructType::getStorageOffsetOfMember(string const& _name) const
|
||||||
{
|
{
|
||||||
|
|
||||||
//@todo cache member offset?
|
//@todo cache member offset?
|
||||||
u256 offset;
|
u256 offset;
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
|
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;
|
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
|
string EnumType::toString() const
|
||||||
{
|
{
|
||||||
return string("enum ") + m_enum.getName();
|
return string("enum ") + m_enum.getName();
|
||||||
@ -955,6 +1010,13 @@ string FunctionType::toString() const
|
|||||||
return name + ")";
|
return name + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 FunctionType::getStorageSize() const
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable function type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned FunctionType::getSizeOnStack() const
|
unsigned FunctionType::getSizeOnStack() const
|
||||||
{
|
{
|
||||||
Location location = m_location;
|
Location location = m_location;
|
||||||
@ -1077,6 +1139,13 @@ string MappingType::toString() const
|
|||||||
return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")";
|
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
|
bool TypeType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
@ -1085,6 +1154,13 @@ bool TypeType::operator==(Type const& _other) const
|
|||||||
return *getActualType() == *other.getActualType();
|
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
|
MemberList const& TypeType::getMembers() const
|
||||||
{
|
{
|
||||||
// We need to lazy-initialize it because of recursive references.
|
// We need to lazy-initialize it because of recursive references.
|
||||||
@ -1122,6 +1198,13 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
|
|||||||
swap(params, m_parameterTypes);
|
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
|
bool ModifierType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
|
30
Types.h
30
Types.h
@ -60,12 +60,19 @@ public:
|
|||||||
return it.second;
|
return it.second;
|
||||||
return TypePointer();
|
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 begin() const { return m_memberTypes.begin(); }
|
||||||
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MemberMap m_memberTypes;
|
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
|
/// @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);
|
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
|
||||||
|
|
||||||
|
/// Calculates the
|
||||||
|
|
||||||
virtual Category getCategory() const = 0;
|
virtual Category getCategory() const = 0;
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
@ -126,9 +135,15 @@ public:
|
|||||||
unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); }
|
unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); }
|
||||||
/// @returns true if the type is dynamically encoded in calldata
|
/// @returns true if the type is dynamically encoded in calldata
|
||||||
virtual bool isDynamicallySized() const { return false; }
|
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,
|
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
||||||
virtual u256 getStorageSize() const { return 1; }
|
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.
|
/// Returns true if the type can be stored in storage.
|
||||||
virtual bool canBeStored() const { return true; }
|
virtual bool canBeStored() const { return true; }
|
||||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
/// 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 bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; }
|
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 bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
|
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 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 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 bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); }
|
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 TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; }
|
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 bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual std::string toString() const override { return "bool"; }
|
virtual std::string toString() const override { return "bool"; }
|
||||||
@ -348,6 +366,7 @@ public:
|
|||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual bool operator==(Type const& _other) 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 bool isValueType() const override { return true; }
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
@ -411,6 +430,7 @@ public:
|
|||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; }
|
virtual unsigned getSizeOnStack() const override { return 1; }
|
||||||
|
virtual unsigned getStorageBytes() const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
@ -480,7 +500,7 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override;
|
virtual unsigned getSizeOnStack() const override;
|
||||||
virtual MemberList const& getMembers() 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 TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual std::string toString() const override { return "void"; }
|
virtual std::string toString() const override { return "void"; }
|
||||||
virtual bool canBeStored() const override { return false; }
|
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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
};
|
};
|
||||||
@ -585,7 +605,7 @@ public:
|
|||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
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 TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual bool canBeStored() const override { return false; }
|
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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
Loading…
Reference in New Issue
Block a user