Define stack slot names in types.

This commit is contained in:
Daniel Kirchner 2020-02-04 17:08:55 +01:00
parent e786650bef
commit 6abe0a50b1
2 changed files with 94 additions and 37 deletions

View File

@ -151,6 +151,8 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa
void Type::clearCache() const
{
m_members.clear();
m_stackSlots.reset();
m_stackSize.reset();
}
void StorageOffsets::computeOffsets(TypePointers const& _types)
@ -1701,15 +1703,14 @@ u256 ArrayType::storageSize() const
return max<u256>(1, u256(size));
}
unsigned ArrayType::sizeOnStack() const
vector<tuple<string, TypePointer>> ArrayType::makeStackSlots() const
{
if (m_location == DataLocation::CallData)
// offset [length] (stack top)
return 1 + (isDynamicallySized() ? 1 : 0);
if (m_location == DataLocation::CallData && isDynamicallySized())
return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
else
// storage slot or memory offset
// byte offset inside storage value is omitted
return 1;
return {std::make_tuple(string(), nullptr)};
}
string ArrayType::toString(bool _short) const
@ -1891,6 +1892,11 @@ string ArraySliceType::toString(bool _short) const
return m_arrayType.toString(_short) + " slice";
}
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackSlots() const
{
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
}
string ContractType::richIdentifier() const
{
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id());
@ -1989,6 +1995,14 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
return variablesAndOffsets;
}
vector<tuple<string, TypePointer>> ContractType::makeStackSlots() const
{
if (m_super)
return {};
else
return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() :TypeProvider::address())};
}
void StructType::clearCache() const
{
Type::clearCache();
@ -2422,12 +2436,17 @@ u256 TupleType::storageSize() const
solAssert(false, "Storage size of non-storable tuple type requested.");
}
unsigned TupleType::sizeOnStack() const
vector<tuple<string, TypePointer>> TupleType::makeStackSlots() const
{
unsigned size = 0;
vector<tuple<string, TypePointer>> slots;
unsigned i = 1;
for (auto const& t: components())
size += t ? t->sizeOnStack() : 0;
return size;
{
if (t)
slots.emplace_back("component_" + std::to_string(i), t);
++i;
}
return slots;
}
TypePointer TupleType::mobileType() const
@ -2883,8 +2902,9 @@ unsigned FunctionType::storageBytes() const
solAssert(false, "Storage size of non-storable function type requested.");
}
unsigned FunctionType::sizeOnStack() const
vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
{
vector<tuple<string, TypePointer>> slots;
Kind kind = m_kind;
if (m_kind == Kind::SetGas || m_kind == Kind::SetValue)
{
@ -2892,39 +2912,42 @@ unsigned FunctionType::sizeOnStack() const
kind = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_kind;
}
unsigned size = 0;
switch (kind)
{
case Kind::External:
case Kind::DelegateCall:
size = 2;
slots = {make_tuple("address", TypeProvider::address()), make_tuple("functionIdentifier", TypeProvider::fixedBytes(4))};
break;
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::Transfer:
case Kind::Send:
slots = {make_tuple("address", TypeProvider::address())};
break;
case Kind::Internal:
slots = {make_tuple(string(), nullptr)};
break;
case Kind::ArrayPush:
case Kind::ArrayPop:
case Kind::ByteArrayPush:
case Kind::Transfer:
case Kind::Send:
size = 1;
slots = {make_tuple(string(), nullptr)};
break;
default:
break;
}
if (m_gasSet)
size++;
slots.emplace_back("gas", TypeProvider::uint256());
if (m_valueSet)
size++;
slots.emplace_back("value", TypeProvider::uint256());
if (m_saltSet)
size++;
slots.emplace_back("salt", TypeProvider::uint256());
if (bound())
size += m_parameterTypes.front()->sizeOnStack();
return size;
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackSlots())
slots.emplace_back("self_" + boundName, boundType);
return slots;
}
FunctionTypePointer FunctionType::interfaceFunctionType() const
@ -3418,12 +3441,12 @@ u256 TypeType::storageSize() const
solAssert(false, "Storage size of non-storable type type requested.");
}
unsigned TypeType::sizeOnStack() const
vector<tuple<string, TypePointer>> TypeType::makeStackSlots() const
{
if (auto contractType = dynamic_cast<ContractType const*>(m_actualType))
if (contractType->contractDefinition().isLibrary())
return 1;
return 0;
return {make_tuple("address", TypeProvider::address())};
return {};
}
MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const

View File

@ -259,7 +259,26 @@ public:
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned sizeOnStack() const { return 1; }
std::vector<std::tuple<std::string, TypePointer>> const& stackSlots() const
{
if (!m_stackSlots)
m_stackSlots = makeStackSlots();
return *m_stackSlots;
}
unsigned sizeOnStack() const
{
if (!m_stackSize)
{
size_t sizeOnStack = 0;
for (auto const& slot: stackSlots())
if (std::get<1>(slot))
sizeOnStack += std::get<1>(slot)->sizeOnStack();
else
++sizeOnStack;
m_stackSize = sizeOnStack;
}
return *m_stackSize;
}
/// If it is possible to initialize such a value in memory by just writing zeros
/// of the size memoryHeadSize().
virtual bool hasSimpleZeroValueInMemory() const { return true; }
@ -336,9 +355,16 @@ protected:
{
return MemberList::MemberMap();
}
virtual std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const
{
return {std::make_tuple(std::string(), nullptr)};
}
/// List of member types (parameterised by scape), will be lazy-initialized.
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
mutable std::optional<std::vector<std::tuple<std::string, TypePointer>>> m_stackSlots;
mutable std::optional<size_t> m_stackSize;
};
/**
@ -562,7 +588,6 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override { return 0; }
std::string toString(bool) const override;
TypePointer mobileType() const override;
@ -571,6 +596,8 @@ public:
std::string const& value() const { return m_value; }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private:
std::string m_value;
};
@ -725,7 +752,6 @@ public:
bool isDynamicallyEncoded() const override;
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
unsigned sizeOnStack() const override;
std::string toString(bool _short) const override;
std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override;
@ -756,6 +782,8 @@ public:
void clearCache() const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
/// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String };
@ -785,7 +813,6 @@ public:
bool isDynamicallySized() const override { return true; }
bool isDynamicallyEncoded() const override { return true; }
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
unsigned sizeOnStack() const override { return 2; }
std::string toString(bool _short) const override;
/// @returns true if this is valid to be stored in calldata
@ -796,6 +823,8 @@ public:
std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
ArrayType const& m_arrayType;
};
@ -825,7 +854,6 @@ public:
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool canLiveOutsideStorage() const override { return !isSuper(); }
unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
bool isValueType() const override { return !isSuper(); }
std::string toString(bool _short) const override;
std::string canonicalName() const override;
@ -856,7 +884,8 @@ public:
/// @returns a list of all state variables (including inherited) of the contract and their
/// offsets in storage.
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
ContractDefinition const& m_contract;
/// If true, this is a special "super" type of m_contract containing only members that m_contract inherited
@ -989,7 +1018,6 @@ public:
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { return false; }
TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching.
@ -997,6 +1025,8 @@ public:
std::vector<TypePointer> const& components() const { return m_components; }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
std::vector<TypePointer> const m_components;
};
@ -1158,7 +1188,6 @@ public:
unsigned storageBytes() const override;
bool isValueType() const override { return true; }
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { return false; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
TypePointer encodingType() const override;
@ -1252,6 +1281,8 @@ public:
/// @param _bound if true, the function type is set to be bound.
FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
static TypePointers parseElementaryTypeVector(strings const& _types);
@ -1321,12 +1352,13 @@ public:
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private:
TypePointer m_actualType;
};
@ -1346,12 +1378,13 @@ public:
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override { return 0; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private:
TypePointers m_parameterTypes;
};
@ -1374,11 +1407,12 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
unsigned sizeOnStack() const override { return 0; }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
std::string toString(bool _short) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private:
SourceUnit const& m_sourceUnit;
};
@ -1413,7 +1447,6 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
unsigned sizeOnStack() const override { return 0; }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
std::string toString(bool _short) const override;
@ -1422,6 +1455,8 @@ public:
TypePointer typeArgument() const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private:
Kind m_kind;
/// Contract type used for contract metadata magic.
@ -1445,7 +1480,6 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
bool isValueType() const override { return true; }
unsigned sizeOnStack() const override { return 1; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool) const override { return "inaccessible dynamic type"; }
TypePointer decodingType() const override;