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 void Type::clearCache() const
{ {
m_members.clear(); m_members.clear();
m_stackSlots.reset();
m_stackSize.reset();
} }
void StorageOffsets::computeOffsets(TypePointers const& _types) void StorageOffsets::computeOffsets(TypePointers const& _types)
@ -1701,15 +1703,14 @@ u256 ArrayType::storageSize() const
return max<u256>(1, u256(size)); return max<u256>(1, u256(size));
} }
unsigned ArrayType::sizeOnStack() const vector<tuple<string, TypePointer>> ArrayType::makeStackSlots() const
{ {
if (m_location == DataLocation::CallData) if (m_location == DataLocation::CallData && isDynamicallySized())
// offset [length] (stack top) return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
return 1 + (isDynamicallySized() ? 1 : 0);
else else
// storage slot or memory offset // storage slot or memory offset
// byte offset inside storage value is omitted // byte offset inside storage value is omitted
return 1; return {std::make_tuple(string(), nullptr)};
} }
string ArrayType::toString(bool _short) const string ArrayType::toString(bool _short) const
@ -1891,6 +1892,11 @@ string ArraySliceType::toString(bool _short) const
return m_arrayType.toString(_short) + " slice"; 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 string ContractType::richIdentifier() const
{ {
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id()); 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; 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 void StructType::clearCache() const
{ {
Type::clearCache(); Type::clearCache();
@ -2422,12 +2436,17 @@ u256 TupleType::storageSize() const
solAssert(false, "Storage size of non-storable tuple type requested."); 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()) 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 TypePointer TupleType::mobileType() const
@ -2883,8 +2902,9 @@ unsigned FunctionType::storageBytes() const
solAssert(false, "Storage size of non-storable function type requested."); 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; Kind kind = m_kind;
if (m_kind == Kind::SetGas || m_kind == Kind::SetValue) 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; kind = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_kind;
} }
unsigned size = 0;
switch (kind) switch (kind)
{ {
case Kind::External: case Kind::External:
case Kind::DelegateCall: case Kind::DelegateCall:
size = 2; slots = {make_tuple("address", TypeProvider::address()), make_tuple("functionIdentifier", TypeProvider::fixedBytes(4))};
break; break;
case Kind::BareCall: case Kind::BareCall:
case Kind::BareCallCode: case Kind::BareCallCode:
case Kind::BareDelegateCall: case Kind::BareDelegateCall:
case Kind::BareStaticCall: case Kind::BareStaticCall:
case Kind::Transfer:
case Kind::Send:
slots = {make_tuple("address", TypeProvider::address())};
break;
case Kind::Internal: case Kind::Internal:
slots = {make_tuple(string(), nullptr)};
break;
case Kind::ArrayPush: case Kind::ArrayPush:
case Kind::ArrayPop: case Kind::ArrayPop:
case Kind::ByteArrayPush: case Kind::ByteArrayPush:
case Kind::Transfer: slots = {make_tuple(string(), nullptr)};
case Kind::Send:
size = 1;
break; break;
default: default:
break; break;
} }
if (m_gasSet) if (m_gasSet)
size++; slots.emplace_back("gas", TypeProvider::uint256());
if (m_valueSet) if (m_valueSet)
size++; slots.emplace_back("value", TypeProvider::uint256());
if (m_saltSet) if (m_saltSet)
size++; slots.emplace_back("salt", TypeProvider::uint256());
if (bound()) if (bound())
size += m_parameterTypes.front()->sizeOnStack(); for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackSlots())
return size; slots.emplace_back("self_" + boundName, boundType);
return slots;
} }
FunctionTypePointer FunctionType::interfaceFunctionType() const FunctionTypePointer FunctionType::interfaceFunctionType() const
@ -3418,12 +3441,12 @@ u256 TypeType::storageSize() const
solAssert(false, "Storage size of non-storable type type requested."); 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 (auto contractType = dynamic_cast<ContractType const*>(m_actualType))
if (contractType->contractDefinition().isLibrary()) if (contractType->contractDefinition().isLibrary())
return 1; return {make_tuple("address", TypeProvider::address())};
return 0; return {};
} }
MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const 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, /// 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. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } 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 /// If it is possible to initialize such a value in memory by just writing zeros
/// of the size memoryHeadSize(). /// of the size memoryHeadSize().
virtual bool hasSimpleZeroValueInMemory() const { return true; } virtual bool hasSimpleZeroValueInMemory() const { return true; }
@ -336,9 +355,16 @@ protected:
{ {
return MemberList::MemberMap(); 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. /// List of member types (parameterised by scape), will be lazy-initialized.
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members; 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 canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; } bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override { return 0; }
std::string toString(bool) const override; std::string toString(bool) const override;
TypePointer mobileType() const override; TypePointer mobileType() const override;
@ -571,6 +596,8 @@ public:
std::string const& value() const { return m_value; } std::string const& value() const { return m_value; }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private: private:
std::string m_value; std::string m_value;
}; };
@ -725,7 +752,6 @@ public:
bool isDynamicallyEncoded() const override; bool isDynamicallyEncoded() const override;
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
unsigned sizeOnStack() const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override; std::string signatureInExternalFunction(bool _structsByName) const override;
@ -756,6 +782,8 @@ public:
void clearCache() const override; void clearCache() const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
/// String is interpreted as a subtype of Bytes. /// String is interpreted as a subtype of Bytes.
enum class ArrayKind { Ordinary, Bytes, String }; enum class ArrayKind { Ordinary, Bytes, String };
@ -785,7 +813,6 @@ public:
bool isDynamicallySized() const override { return true; } bool isDynamicallySized() const override { return true; }
bool isDynamicallyEncoded() const override { return true; } bool isDynamicallyEncoded() const override { return true; }
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); } bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
unsigned sizeOnStack() const override { return 2; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
/// @returns true if this is valid to be stored in calldata /// @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, ""); } std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
ArrayType const& m_arrayType; ArrayType const& m_arrayType;
}; };
@ -825,7 +854,6 @@ public:
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool canLiveOutsideStorage() const override { return !isSuper(); } bool canLiveOutsideStorage() const override { return !isSuper(); }
unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
bool isValueType() const override { return !isSuper(); } bool isValueType() const override { return !isSuper(); }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() 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 /// @returns a list of all state variables (including inherited) of the contract and their
/// offsets in storage. /// offsets in storage.
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const; std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
/// If true, this is a special "super" type of m_contract containing only members that m_contract inherited /// 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; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; } bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
TypePointer mobileType() const override; TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching. /// 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; } std::vector<TypePointer> const& components() const { return m_components; }
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
std::vector<TypePointer> const m_components; std::vector<TypePointer> const m_components;
}; };
@ -1158,7 +1188,6 @@ public:
unsigned storageBytes() const override; unsigned storageBytes() const override;
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
TypePointer encodingType() const override; TypePointer encodingType() const override;
@ -1252,6 +1281,8 @@ public:
/// @param _bound if true, the function type is set to be bound. /// @param _bound if true, the function type is set to be bound.
FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const; FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
static TypePointers parseElementaryTypeVector(strings const& _types); static TypePointers parseElementaryTypeVector(strings const& _types);
@ -1321,12 +1352,13 @@ public:
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; } bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override;
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
private: private:
TypePointer m_actualType; TypePointer m_actualType;
}; };
@ -1346,12 +1378,13 @@ public:
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; } bool canLiveOutsideStorage() const override { return false; }
unsigned sizeOnStack() const override { return 0; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private: private:
TypePointers m_parameterTypes; TypePointers m_parameterTypes;
}; };
@ -1374,11 +1407,12 @@ public:
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; } bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
unsigned sizeOnStack() const override { return 0; }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private: private:
SourceUnit const& m_sourceUnit; SourceUnit const& m_sourceUnit;
}; };
@ -1413,7 +1447,6 @@ public:
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; } bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
unsigned sizeOnStack() const override { return 0; }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
@ -1422,6 +1455,8 @@ public:
TypePointer typeArgument() const; TypePointer typeArgument() const;
protected:
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
private: private:
Kind m_kind; Kind m_kind;
/// Contract type used for contract metadata magic. /// Contract type used for contract metadata magic.
@ -1445,7 +1480,6 @@ public:
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; } bool canLiveOutsideStorage() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
unsigned sizeOnStack() const override { return 1; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool) const override { return "inaccessible dynamic type"; } std::string toString(bool) const override { return "inaccessible dynamic type"; }
TypePointer decodingType() const override; TypePointer decodingType() const override;