From 3fed790a56cea7bae481d01c15949aa500823968 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 17 Jan 2017 10:47:44 +0100 Subject: [PATCH 1/4] Type identifiers. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 189 ++++++++++++++++++++++++++++- libsolidity/ast/Types.h | 24 ++++ test/libsolidity/SolidityTypes.cpp | 66 ++++++++++ 4 files changed, 277 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1e5dd8e1c..b9e4ecc03 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * Compiler Interface: Contracts and libraries can be referenced with a `file:` prefix to make them unique. * AST: Use deterministic node identifiers. + * Type system: Introduce type identifier strings. * Metadata: Do not include platform in the version number. ### 0.4.8 (2017-01-13) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 03ff84718..9e7224741 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -272,7 +272,15 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(_bits) - ); + ); +} + +string IntegerType::identifier() const +{ + if (isAddress()) + return "t_address"; + else + return "t_" + string(isSigned() ? "" : "u") + "int" + std::to_string(numBits()); } bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -412,7 +420,12 @@ FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPoint m_fractionalBits % 8 == 0, "Invalid bit number(s) for fixed type: " + dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) - ); + ); +} + +string FixedPointType::identifier() const +{ + return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(integerBits()) + "x" + std::to_string(fractionalBits()); } bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -770,6 +783,11 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } +string RationalNumberType::identifier() const +{ + return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); +} + bool RationalNumberType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -909,6 +927,13 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } +string StringLiteralType::identifier() const +{ + // Since we have to return a valid identifier and the string itself may contain + // anything, we hash it. + return "t_stringliteral_" + toHex(keccak256(m_value).asBytes()); +} + bool StringLiteralType::operator==(const Type& _other) const { if (_other.category() != category()) @@ -1002,6 +1027,11 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared(8)}}; } +string FixedBytesType::identifier() const +{ + return "t_bytes" + std::to_string(m_bytes); +} + bool FixedBytesType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1115,6 +1145,20 @@ string ReferenceType::stringForReferencePart() const return ""; } +string ReferenceType::identifierLocationSuffix() const +{ + string id; + if (location() == DataLocation::Storage) + id += "_storage"; + else if (location() == DataLocation::Memory) + id += "_memory"; + else + id += "_calldata"; + if (isPointer()) + id += "_ptr"; + return id; +} + bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { if (_convertTo.category() != category()) @@ -1170,6 +1214,26 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } +string ArrayType::identifier() const +{ + string id; + if (isString()) + id += "t_string"; + else if (isByteArray()) + id += "t_bytes"; + else + { + id = baseType()->identifier(); + if (isDynamicallySized()) + id += "_arraydyn"; + else + id += string("_array") + length().str(); + } + id += identifierLocationSuffix(); + + return id; +} + bool ArrayType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1184,7 +1248,7 @@ bool ArrayType::operator==(Type const& _other) const return false; if (*other.baseType() != *baseType()) return false; - return isDynamicallySized() || length() == other.length(); + return isDynamicallySized() || length() == other.length(); } unsigned ArrayType::calldataEncodedSize(bool _padded) const @@ -1356,6 +1420,11 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } +string ContractType::identifier() const +{ + return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); +} + bool ContractType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1465,6 +1534,11 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } +string StructType::identifier() const +{ + return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); +} + bool StructType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1605,6 +1679,11 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared() : TypePointer(); } +string EnumType::identifier() const +{ + return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); +} + bool EnumType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1686,6 +1765,18 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } +string TupleType::identifier() const +{ + string id = "t_tuple" + std::to_string(components().size()) + "_"; + for (auto const& c: components()) + if (c) + id += c->identifier() + "_"; + else + id += "t_empty_"; + id += "tuple_end"; + return id; +} + bool TupleType::operator==(Type const& _other) const { if (auto tupleType = dynamic_cast(&_other)) @@ -1934,6 +2025,59 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } +string FunctionType::identifier() const +{ + string id = "t_function_"; + switch (location()) + { + case Location::Internal: id += "internal"; break; + case Location::External: id += "external"; break; + case Location::CallCode: id += "callcode"; break; + case Location::DelegateCall: id += "delegatecall"; break; + case Location::Bare: id += "bare"; break; + case Location::BareCallCode: id += "barecallcode"; break; + case Location::BareDelegateCall: id += "baredelegatecall"; break; + case Location::Creation: id += "creation"; break; + case Location::Send: id += "send"; break; + case Location::SHA3: id += "sha3"; break; + case Location::Selfdestruct: id += "selfdestruct"; break; + case Location::ECRecover: id += "ecrecover"; break; + case Location::SHA256: id += "sha256"; break; + case Location::RIPEMD160: id += "ripemd160"; break; + case Location::Log0: id += "log0"; break; + case Location::Log1: id += "log1"; break; + case Location::Log2: id += "log2"; break; + case Location::Log3: id += "log3"; break; + case Location::Log4: id += "log4"; break; + case Location::Event: id += "event"; break; + case Location::SetGas: id += "setgas"; break; + case Location::SetValue: id += "setvalue"; break; + case Location::BlockHash: id += "blockhash"; break; + case Location::AddMod: id += "addmod"; break; + case Location::MulMod: id += "mulmod"; break; + case Location::ArrayPush: id += "arraypush"; break; + case Location::ByteArrayPush: id += "bytearraypush"; break; + case Location::ObjectCreation: id += "objectcreation"; break; + default: solAssert(false, "Unknown function location."); break; + } + if (isConstant()) + id += "_constant"; + id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; + for (auto const& r: m_returnParameterTypes) + id += r->identifier() + "_"; + if (m_gasSet) + id += "gas_set_"; + if (m_valueSet) + id += "value_set_"; + if (bound()) + id += "bound_to" + selfType()->identifier() + "_"; + id += "function_end"; + return id; +} + bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2354,6 +2498,11 @@ ASTPointer FunctionType::documentation() const return ASTPointer(); } +string MappingType::identifier() const +{ + return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; +} + bool MappingType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2372,6 +2521,11 @@ string MappingType::canonicalName(bool) const return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; } +string TypeType::identifier() const +{ + return "t_type_" + actualType()->identifier(); +} + bool TypeType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2456,6 +2610,14 @@ u256 ModifierType::storageSize() const << errinfo_comment("Storage size of non-storable type type requested.")); } +string ModifierType::identifier() const +{ + string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; + for (auto const& p: m_parameterTypes) + id += p->identifier() + "_"; + return id + "end_modifier"; +} + bool ModifierType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2480,6 +2642,11 @@ string ModifierType::toString(bool _short) const return name + ")"; } +string ModuleType::identifier() const +{ + return "t_module_" + std::to_string(m_sourceUnit.id()); +} + bool ModuleType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -2501,6 +2668,22 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } +string MagicType::identifier() const +{ + switch (m_kind) + { + case Kind::Block: + return "t_magic_block"; + case Kind::Message: + return "t_magic_message"; + case Kind::Transaction: + return "t_magic_transaction"; + default: + solAssert(false, "Unknown kind of magic"); + } + return ""; +} + bool MagicType::operator==(Type const& _other) const { if (_other.category() != category()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 26e2b8f29..c1f6a476a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -155,6 +155,10 @@ public: static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); virtual Category category() const = 0; + /// @returns a valid solidity identifier such that two types should compare equal if and + /// only if they have the same identifier. + /// The identifier should start with "t_". + virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { @@ -288,6 +292,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -329,6 +334,7 @@ public: explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -378,6 +384,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -416,6 +423,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -449,6 +457,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -476,6 +485,7 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } + virtual std::string identifier() const override { return "t_bool"; } virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -533,6 +543,8 @@ protected: TypePointer copyForLocationIfReference(TypePointer const& _type) const; /// @returns a human-readable description of the reference part of the type. std::string stringForReferencePart() const; + /// @returns the suffix computed from the reference part to be used by identifier(); + std::string identifierLocationSuffix() const; DataLocation m_location = DataLocation::Storage; bool m_isPointer = true; @@ -573,6 +585,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(const Type& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -622,6 +635,7 @@ public: /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { @@ -677,6 +691,7 @@ public: explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; u256 memorySize() const; @@ -720,6 +735,7 @@ public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { @@ -760,6 +776,7 @@ public: virtual Category category() const override { return Category::Tuple; } explicit TupleType(std::vector const& _types = std::vector()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -897,6 +914,7 @@ public: /// @returns the "self" parameter type for a bound function TypePointer selfType() const; + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual std::string canonicalName(bool /*_addDataLocation*/) const override; @@ -995,6 +1013,7 @@ public: MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; @@ -1029,6 +1048,7 @@ public: TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1056,6 +1076,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1080,6 +1101,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1109,6 +1131,7 @@ public: return TypePointer(); } + virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1132,6 +1155,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } + virtual std::string identifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index dc3143c87..1e7bcc7bb 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -21,6 +21,8 @@ */ #include +#include +#include #include using namespace std; @@ -86,6 +88,70 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_identifiers) +{ + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint128")->identifier(), "t_uint128"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("int128")->identifier(), "t_int128"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("address")->identifier(), "t_address"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint8")->identifier(), "t_uint8"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("ufixed8x64")->identifier(), "t_ufixed8x64"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("fixed128x8")->identifier(), "t_fixed128x8"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(7, 1)).identifier(), "t_rational_7_by_1"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(200, 77)).identifier(), "t_rational_200_by_77"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77"); + BOOST_CHECK_EQUAL( + StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared("abc - def"))).identifier(), + "t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4" + ); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes8")->identifier(), "t_bytes8"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes32")->identifier(), "t_bytes32"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bool")->identifier(), "t_bool"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr"); + ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); + BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_int128_array2535301200456458802993406410752_memory_ptr"); + TypePointer stringArray = make_shared(DataLocation::Storage, Type::fromElementaryTypeName("string"), u256("20")); + TypePointer multiArray = make_shared(DataLocation::Storage, stringArray); + BOOST_CHECK_EQUAL(multiArray->identifier(), "t_string_storage_array20_storage_arraydyn_storage_ptr"); + + ContractDefinition c(SourceLocation{}, make_shared("MyContract"), {}, {}, {}, false); + BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type_t_contract_MyContract_2"); + BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super_MyContract_2"); + + StructDefinition s({}, make_shared("Struct"), {}); + BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type_t_struct_Struct_3_storage_ptr"); + + EnumDefinition e({}, make_shared("Enum"), {}); + BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type_t_enum_Enum_4"); + + TupleType t({e.type(), s.type(), stringArray, nullptr}); + BOOST_CHECK_EQUAL(t.identifier(), "t_tuple4_t_type_t_enum_Enum_4_t_type_t_struct_Struct_3_storage_ptr_t_string_storage_array20_storage_ptr_t_empty_tuple_end"); + + TypePointer sha3fun = make_shared(strings{}, strings{}, FunctionType::Location::SHA3); + BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_param0_return0_function_end"); + + FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_param1_t_function_sha3_param0_return0_function_end_return1_t_type_t_struct_Struct_3_storage_ptr_function_end"); + + TypePointer m = make_shared(Type::fromElementaryTypeName("bytes32"), s.type()); + MappingType m2(Type::fromElementaryTypeName("uint64"), m); + BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping_t_uint64_to_t_mapping_t_bytes32_to_t_type_t_struct_Struct_3_storage_ptr_mapping_end_mapping_end"); + + // TypeType is tested with contract + + auto emptyParams = make_shared(SourceLocation(), std::vector>()); + ModifierDefinition mod(SourceLocation{}, make_shared("modif"), {}, emptyParams, {}); + BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier_param0_end_modifier"); + + SourceUnit su({}, {}); + BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Block).identifier(), "t_magic_block"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Message).identifier(), "t_magic_message"); + BOOST_CHECK_EQUAL(MagicType(MagicType::Kind::Transaction).identifier(), "t_magic_transaction"); + + BOOST_CHECK_EQUAL(InaccessibleDynamicType().identifier(), "t_inaccessible"); +} + BOOST_AUTO_TEST_SUITE_END() } From da178d967fb66ca508d16bbe3feecf1962dcf6ef Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Jan 2017 13:18:56 +0100 Subject: [PATCH 2/4] Properly escape user strings and lists. --- libsolidity/ast/Types.cpp | 108 +++++++++++++++++++---------- libsolidity/ast/Types.h | 22 +++--- test/libsolidity/SolidityTypes.cpp | 24 +++---- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9e7224741..6e9e9d7e2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -21,15 +21,21 @@ */ #include -#include -#include -#include + +#include +#include + #include #include #include #include -#include -#include + +#include +#include +#include +#include + +#include using namespace std; using namespace dev; @@ -117,6 +123,51 @@ u256 const& MemberList::storageSize() const return m_storageOffsets->storageSize(); } +/// Helper functions for type identifier +namespace +{ + +string parenthesizeIdentifier(string const& _internal) +{ + return "$_" + _internal + "_$"; +} + +template +string identifierList(Range const&& _list) +{ + return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); +} + +string identifier(TypePointer const& _type) +{ + return _type ? _type->identifier() : ""; +} + +string identifierList(vector const& _list) +{ + return identifierList(_list | boost::adaptors::transformed(identifier)); +} + +string identifierList(TypePointer const& _type) +{ + return parenthesizeIdentifier(identifier(_type)); +} + +string identifierList(TypePointer const& _type1, TypePointer const& _type2) +{ + TypePointers list; + list.push_back(_type1); + list.push_back(_type2); + return identifierList(list); +} + +string parenthesizeUserIdentifier(string const& _internal) +{ + return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); +} + +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), @@ -1218,16 +1269,17 @@ string ArrayType::identifier() const { string id; if (isString()) - id += "t_string"; + id = "t_string"; else if (isByteArray()) - id += "t_bytes"; + id = "t_bytes"; else { - id = baseType()->identifier(); + id = "t_array"; + id += identifierList(baseType()); if (isDynamicallySized()) - id += "_arraydyn"; + id += "dyn"; else - id += string("_array") + length().str(); + id += length().str(); } id += identifierLocationSuffix(); @@ -1422,7 +1474,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) string ContractType::identifier() const { - return (m_super ? "t_super_" : "t_contract_") + m_contract.name() + "_" + std::to_string(m_contract.id()); + return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } bool ContractType::operator==(Type const& _other) const @@ -1536,7 +1588,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const string StructType::identifier() const { - return "t_struct_" + m_struct.name() + "_" + std::to_string(m_struct.id()) + identifierLocationSuffix(); + return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } bool StructType::operator==(Type const& _other) const @@ -1681,7 +1733,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const string EnumType::identifier() const { - return "t_enum_" + m_enum.name() + "_" + std::to_string(m_enum.id()); + return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } bool EnumType::operator==(Type const& _other) const @@ -1767,14 +1819,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const string TupleType::identifier() const { - string id = "t_tuple" + std::to_string(components().size()) + "_"; - for (auto const& c: components()) - if (c) - id += c->identifier() + "_"; - else - id += "t_empty_"; - id += "tuple_end"; - return id; + return "t_tuple" + identifierList(components()); } bool TupleType::operator==(Type const& _other) const @@ -2062,19 +2107,13 @@ string FunctionType::identifier() const } if (isConstant()) id += "_constant"; - id += "_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - id += "return" + std::to_string(m_returnParameterTypes.size()) + "_"; - for (auto const& r: m_returnParameterTypes) - id += r->identifier() + "_"; + id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) id += "gas_set_"; if (m_valueSet) id += "value_set_"; if (bound()) - id += "bound_to" + selfType()->identifier() + "_"; - id += "function_end"; + id += "bound_to" + identifierList(selfType()); return id; } @@ -2482,7 +2521,7 @@ vector const FunctionType::returnParameterTypeNames(bool _addDataLocatio return names; } -TypePointer FunctionType::selfType() const +TypePointer const& FunctionType::selfType() const { solAssert(bound(), "Function is not bound."); solAssert(m_parameterTypes.size() > 0, "Function has no self type."); @@ -2500,7 +2539,7 @@ ASTPointer FunctionType::documentation() const string MappingType::identifier() const { - return "t_mapping_" + m_keyType->identifier() + "_to_" + m_valueType->identifier() + "_mapping_end"; + return "t_mapping" + identifierList(m_keyType, m_valueType); } bool MappingType::operator==(Type const& _other) const @@ -2523,7 +2562,7 @@ string MappingType::canonicalName(bool) const string TypeType::identifier() const { - return "t_type_" + actualType()->identifier(); + return "t_type" + identifierList(actualType()); } bool TypeType::operator==(Type const& _other) const @@ -2612,10 +2651,7 @@ u256 ModifierType::storageSize() const string ModifierType::identifier() const { - string id = "t_modifier_param" + std::to_string(m_parameterTypes.size()) + "_"; - for (auto const& p: m_parameterTypes) - id += p->identifier() + "_"; - return id + "end_modifier"; + return "t_modifier" + identifierList(m_parameterTypes); } bool ModifierType::operator==(Type const& _other) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c1f6a476a..1e94631e0 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -22,18 +22,21 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include #include #include + +#include +#include #include +#include +#include + +#include +#include +#include + namespace dev { namespace solidity @@ -158,6 +161,9 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". + /// More complex identifier strings use "parentheses", where $_ is interpreted as as + /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that + /// appears as part of a user-supplied identifier is escaped as _$$$_. virtual std::string identifier() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -912,7 +918,7 @@ public: std::vector const& returnParameterNames() const { return m_returnParameterNames; } std::vector const returnParameterTypeNames(bool _addDataLocation) const; /// @returns the "self" parameter type for a bound function - TypePointer selfType() const; + TypePointer const& selfType() const; virtual std::string identifier() const override; virtual bool operator==(Type const& _other) const override; diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 1e7bcc7bb..b4c068731 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -109,39 +109,39 @@ BOOST_AUTO_TEST_CASE(type_identifiers) BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr"); ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); - BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_int128_array2535301200456458802993406410752_memory_ptr"); + BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_array$_t_int128_$2535301200456458802993406410752_memory_ptr"); TypePointer stringArray = make_shared(DataLocation::Storage, Type::fromElementaryTypeName("string"), u256("20")); TypePointer multiArray = make_shared(DataLocation::Storage, stringArray); - BOOST_CHECK_EQUAL(multiArray->identifier(), "t_string_storage_array20_storage_arraydyn_storage_ptr"); + BOOST_CHECK_EQUAL(multiArray->identifier(), "t_array$_t_array$_t_string_storage_$20_storage_$dyn_storage_ptr"); - ContractDefinition c(SourceLocation{}, make_shared("MyContract"), {}, {}, {}, false); - BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type_t_contract_MyContract_2"); - BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super_MyContract_2"); + ContractDefinition c(SourceLocation{}, make_shared("MyContract$"), {}, {}, {}, false); + BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type$_t_contract$_MyContract$$$_$2_$"); + BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super$_MyContract$$$_$2"); StructDefinition s({}, make_shared("Struct"), {}); - BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type_t_struct_Struct_3_storage_ptr"); + BOOST_CHECK_EQUAL(s.type()->identifier(), "t_type$_t_struct$_Struct_$3_storage_ptr_$"); EnumDefinition e({}, make_shared("Enum"), {}); - BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type_t_enum_Enum_4"); + BOOST_CHECK_EQUAL(e.type()->identifier(), "t_type$_t_enum$_Enum_$4_$"); TupleType t({e.type(), s.type(), stringArray, nullptr}); - BOOST_CHECK_EQUAL(t.identifier(), "t_tuple4_t_type_t_enum_Enum_4_t_type_t_struct_Struct_3_storage_ptr_t_string_storage_array20_storage_ptr_t_empty_tuple_end"); + BOOST_CHECK_EQUAL(t.identifier(), "t_tuple$_t_type$_t_enum$_Enum_$4_$_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_t_array$_t_string_storage_$20_storage_ptr_$__$"); TypePointer sha3fun = make_shared(strings{}, strings{}, FunctionType::Location::SHA3); - BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_param0_return0_function_end"); + BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3$__$returns$__$"); FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); - BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_param1_t_function_sha3_param0_return0_function_end_return1_t_type_t_struct_Struct_3_storage_ptr_function_end"); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal$_t_function_sha3$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); TypePointer m = make_shared(Type::fromElementaryTypeName("bytes32"), s.type()); MappingType m2(Type::fromElementaryTypeName("uint64"), m); - BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping_t_uint64_to_t_mapping_t_bytes32_to_t_type_t_struct_Struct_3_storage_ptr_mapping_end_mapping_end"); + BOOST_CHECK_EQUAL(m2.identifier(), "t_mapping$_t_uint64_$_t_mapping$_t_bytes32_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_$"); // TypeType is tested with contract auto emptyParams = make_shared(SourceLocation(), std::vector>()); ModifierDefinition mod(SourceLocation{}, make_shared("modif"), {}, emptyParams, {}); - BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier_param0_end_modifier"); + BOOST_CHECK_EQUAL(ModifierType(mod).identifier(), "t_modifier$__$"); SourceUnit su({}, {}); BOOST_CHECK_EQUAL(ModuleType(su).identifier(), "t_module_7"); From 7159944f0fa5d9eb3205cd8a3e1d6ec4f133a4ad Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 11:47:20 +0100 Subject: [PATCH 3/4] Reset AST node IDs between compilation runs. --- libsolidity/ast/AST.cpp | 22 ++++++++++++++++++++-- libsolidity/ast/AST.h | 2 ++ libsolidity/ast/Types.cpp | 7 ++++--- libsolidity/interface/CompilerStack.cpp | 1 + test/libsolidity/SolidityTypes.cpp | 1 + 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index fcd6e38c0..8a43c3f75 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -34,11 +34,24 @@ using namespace std; using namespace dev; using namespace dev::solidity; +class IDDispenser +{ +public: + static size_t next() { return ++instance(); } + static void reset() { instance() = 0; } +private: + static size_t& instance() + { + static IDDispenser dispenser; + return dispenser.id; + } + size_t id = 0; +}; + ASTNode::ASTNode(SourceLocation const& _location): + m_id(IDDispenser::next()), m_location(_location) { - static size_t id = 0; - m_id = ++id; } ASTNode::~ASTNode() @@ -46,6 +59,11 @@ ASTNode::~ASTNode() delete m_annotation; } +void ASTNode::resetID() +{ + IDDispenser::reset(); +} + ASTAnnotation& ASTNode::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4af649634..116275c34 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -59,6 +59,8 @@ public: /// @returns an identifier of this AST node that is unique for a single compilation run. size_t id() const { return m_id; } + /// Resets the global ID counter. This invalidates all previous IDs. + static void resetID(); virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6e9e9d7e2..cefd0603d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -163,7 +164,7 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); } } @@ -2109,9 +2110,9 @@ string FunctionType::identifier() const id += "_constant"; id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); if (m_gasSet) - id += "gas_set_"; + id += "gas"; if (m_valueSet) - id += "value_set_"; + id += "value"; if (bound()) id += "bound_to" + identifierList(selfType()); return id; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 61fc77287..85ec0fb1a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -112,6 +112,7 @@ bool CompilerStack::parse() { //reset m_errors.clear(); + ASTNode::resetID(); m_parseSuccessful = false; if (SemVerVersion{string(VersionString)}.isPrerelease()) diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index b4c068731..2dcb92265 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -90,6 +90,7 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_AUTO_TEST_CASE(type_identifiers) { + ASTNode::resetID(); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint128")->identifier(), "t_uint128"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("int128")->identifier(), "t_int128"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("address")->identifier(), "t_address"); From 07b0a0a56028c5ea9bf3dc4fc3b7fda6e4d97ec9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Jan 2017 15:56:56 +0100 Subject: [PATCH 4/4] Make m_id const. --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 116275c34..c7c374459 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -99,7 +99,7 @@ public: ///@} protected: - size_t m_id = 0; + size_t const m_id = 0; /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). mutable ASTAnnotation* m_annotation = nullptr;