Browse Source

Compute packing offsets.

cl-refactor
chriseth 10 years ago
parent
commit
72152a6081
  1. 95
      libsolidity/Types.cpp
  2. 30
      libsolidity/Types.h
  3. 82
      test/SolidityTypes.cpp

95
libsolidity/Types.cpp

@ -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
libsolidity/Types.h

@ -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;

82
test/SolidityTypes.cpp

@ -0,0 +1,82 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Unit tests for the type system of Solidity.
*/
#include <libsolidity/Types.h>
#include <boost/test/unit_test.hpp>
using namespace std;
namespace dev
{
namespace solidity
{
namespace test
{
BOOST_AUTO_TEST_SUITE(SolidityTypes)
BOOST_AUTO_TEST_CASE(storage_layout_simple)
{
MemberList members(MemberList::MemberMap({
{string("first"), Type::fromElementaryTypeName("uint128")},
{string("second"), Type::fromElementaryTypeName("uint120")},
{string("wraps"), Type::fromElementaryTypeName("uint16")}
}));
BOOST_REQUIRE_EQUAL(u256(2), members.getStorageSize());
BOOST_REQUIRE(members.getMemberStorageOffset("first") != nullptr);
BOOST_REQUIRE(members.getMemberStorageOffset("second") != nullptr);
BOOST_REQUIRE(members.getMemberStorageOffset("wraps") != nullptr);
BOOST_CHECK(*members.getMemberStorageOffset("first") == make_pair(u256(0), unsigned(0)));
BOOST_CHECK(*members.getMemberStorageOffset("second") == make_pair(u256(0), unsigned(16)));
BOOST_CHECK(*members.getMemberStorageOffset("wraps") == make_pair(u256(1), unsigned(0)));
}
BOOST_AUTO_TEST_CASE(storage_layout_mapping)
{
MemberList members(MemberList::MemberMap({
{string("first"), Type::fromElementaryTypeName("uint128")},
{string("second"), make_shared<MappingType>(
Type::fromElementaryTypeName("uint8"),
Type::fromElementaryTypeName("uint8")
)},
{string("third"), Type::fromElementaryTypeName("uint16")},
{string("final"), make_shared<MappingType>(
Type::fromElementaryTypeName("uint8"),
Type::fromElementaryTypeName("uint8")
)},
}));
BOOST_REQUIRE_EQUAL(u256(4), members.getStorageSize());
BOOST_REQUIRE(members.getMemberStorageOffset("first") != nullptr);
BOOST_REQUIRE(members.getMemberStorageOffset("second") != nullptr);
BOOST_REQUIRE(members.getMemberStorageOffset("third") != nullptr);
BOOST_REQUIRE(members.getMemberStorageOffset("final") != nullptr);
BOOST_CHECK(*members.getMemberStorageOffset("first") == make_pair(u256(0), unsigned(0)));
BOOST_CHECK(*members.getMemberStorageOffset("second") == make_pair(u256(1), unsigned(0)));
BOOST_CHECK(*members.getMemberStorageOffset("third") == make_pair(u256(2), unsigned(0)));
BOOST_CHECK(*members.getMemberStorageOffset("final") == make_pair(u256(3), unsigned(0)));
}
BOOST_AUTO_TEST_SUITE_END()
}
}
}
Loading…
Cancel
Save