From 66f3fcdd74d2281c2c2b940089ef39ddd894249a Mon Sep 17 00:00:00 2001 From: Ryan Shepherd Date: Wed, 3 Feb 2021 16:33:35 -0800 Subject: [PATCH] Support nested types, pointers, and generalized arrays (#14) * Feature coverage, including nested types, arrays and pointers in signatures * Tweaks to nested type logic --- src/impl/winmd_reader/cache.h | 22 ++++++++- src/impl/winmd_reader/column.h | 48 ++++++++++++++++++- src/impl/winmd_reader/helpers.h | 61 +++++++++++++++++++++++- src/impl/winmd_reader/key.h | 36 ++++++++++++++ src/impl/winmd_reader/schema.h | 34 ++++++++++++- src/impl/winmd_reader/signature.h | 71 +++++++++++++++++++++++++++- src/impl/winmd_reader/type_helpers.h | 37 +++------------ src/impl/winmd_reader/view.h | 5 ++ 8 files changed, 277 insertions(+), 37 deletions(-) diff --git a/src/impl/winmd_reader/cache.h b/src/impl/winmd_reader/cache.h index 34dba0e..543ce5e 100644 --- a/src/impl/winmd_reader/cache.h +++ b/src/impl/winmd_reader/cache.h @@ -16,7 +16,7 @@ namespace winmd::reader for (auto&& type : db.TypeDef) { - if (!type.Flags().WindowsRuntime()) + if (type.Flags().value == 0 || is_nested(type)) { continue; } @@ -24,6 +24,11 @@ namespace winmd::reader auto& ns = m_namespaces[type.TypeNamespace()]; ns.types.try_emplace(type.TypeName(), type); } + + for (auto&& row : db.NestedClass) + { + m_nested_types[row.EnclosingType()].push_back(row.NestedType()); + } } for (auto&&[namespace_name, members] : m_namespaces) @@ -160,6 +165,20 @@ namespace winmd::reader remove(members.delegates, name); } + std::vector const& nested_types(TypeDef const& enclosing_type) const + { + auto it = m_nested_types.find(enclosing_type); + if (it != m_nested_types.end()) + { + return it->second; + } + else + { + static const std::vector empty; + return empty; + } + } + struct namespace_members { std::map types; @@ -178,5 +197,6 @@ namespace winmd::reader std::list m_databases; std::map m_namespaces; + std::map> m_nested_types; }; } \ No newline at end of file diff --git a/src/impl/winmd_reader/column.h b/src/impl/winmd_reader/column.h index 62d6d02..4ab754a 100644 --- a/src/impl/winmd_reader/column.h +++ b/src/impl/winmd_reader/column.h @@ -316,7 +316,7 @@ namespace winmd::reader inline auto Constant::ValueString() const { XLANG_ASSERT(Type() == ConstantType::String); - return get_blob(2).as_string(); + return get_blob(2).as_u16string_constant(); } inline auto Constant::ValueClass() const @@ -502,4 +502,50 @@ namespace winmd::reader { return get_target_row(2); } + + inline TypeDef NestedClass::NestedType() const + { + return get_target_row(0); + } + + inline TypeDef NestedClass::EnclosingType() const + { + return get_target_row(1); + } + + inline auto TypeDef::EnclosingType() const + { + auto const range = equal_range(get_database().NestedClass, *this); + TypeDef result; + if (range.first != range.second) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first.EnclosingType(); + } + return result; + } + + inline auto Field::FieldMarshal() const + { + auto const range = equal_range(get_database().FieldMarshal, coded_index()); + reader::FieldMarshal result; + if (range.first != range.second) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first; + } + return result; + } + + inline auto Param::FieldMarshal() const + { + auto const range = equal_range(get_database().FieldMarshal, coded_index()); + reader::FieldMarshal result; + if (range.first != range.second) + { + XLANG_ASSERT(range.second - range.first == 1); + result = range.first; + } + return result; + } } diff --git a/src/impl/winmd_reader/helpers.h b/src/impl/winmd_reader/helpers.h index fe5ddfc..75ecc3b 100644 --- a/src/impl/winmd_reader/helpers.h +++ b/src/impl/winmd_reader/helpers.h @@ -15,12 +15,69 @@ namespace winmd::reader inline auto find(TypeRef const& type) { - return type.get_database().get_cache().find(type.TypeNamespace(), type.TypeName()); + if (type.ResolutionScope().type() != ResolutionScope::TypeRef) + { + return type.get_database().get_cache().find(type.TypeNamespace(), type.TypeName()); + } + else + { + auto enclosing_type = find(type.ResolutionScope().TypeRef()); + if (!enclosing_type) + { + return TypeDef{}; + } + auto const& nested_types = enclosing_type.get_cache().nested_types(enclosing_type); + auto iter = std::find_if(nested_types.begin(), nested_types.end(), + [name = type.TypeName()](TypeDef const& arg) + { + return name == arg.TypeName(); + }); + if (iter == nested_types.end()) + { + return TypeDef{}; + } + return *iter; + } } inline auto find_required(TypeRef const& type) { - return type.get_database().get_cache().find_required(type.TypeNamespace(), type.TypeName()); + if (type.ResolutionScope().type() != ResolutionScope::TypeRef) + { + return type.get_database().get_cache().find_required(type.TypeNamespace(), type.TypeName()); + } + else + { + auto enclosing_type = find_required(type.ResolutionScope().TypeRef()); + auto const& nested_types = enclosing_type.get_cache().nested_types(enclosing_type); + auto iter = std::find_if(nested_types.begin(), nested_types.end(), + [name = type.TypeName()](TypeDef const& arg) + { + return name == arg.TypeName(); + }); + if (iter == nested_types.end()) + { + impl::throw_invalid("Type '", enclosing_type.TypeName(), ".", type.TypeName(), "' could not be found"); + } + return *iter; + } + } + + inline TypeDef find(coded_index const& type) + { + if (type.type() == TypeDefOrRef::TypeRef) + { + return find(type.TypeRef()); + } + else if (type.type() == TypeDefOrRef::TypeDef) + { + return type.TypeDef(); + } + else + { + XLANG_ASSERT(false); + return {}; + } } inline TypeDef find_required(coded_index const& type) diff --git a/src/impl/winmd_reader/key.h b/src/impl/winmd_reader/key.h index 0d20f40..f26aefa 100644 --- a/src/impl/winmd_reader/key.h +++ b/src/impl/winmd_reader/key.h @@ -174,4 +174,40 @@ namespace winmd::reader return {}; } + + enum class category + { + interface_type, + class_type, + enum_type, + struct_type, + delegate_type + }; + + inline category get_category(TypeDef const& type) + { + if (type.Flags().Semantics() == TypeSemantics::Interface || get_attribute(type, "System.Runtime.InteropServices"sv, "GuidAttribute"sv)) + { + return category::interface_type; + } + + auto const& [extends_namespace, extends_name] = get_base_class_namespace_and_name(type); + + if (extends_name == "Enum"sv && extends_namespace == "System"sv) + { + return category::enum_type; + } + + if (extends_name == "ValueType"sv && extends_namespace == "System"sv) + { + return category::struct_type; + } + + if (extends_name == "MulticastDelegate"sv && extends_namespace == "System"sv) + { + return category::delegate_type; + } + + return category::class_type; + } } diff --git a/src/impl/winmd_reader/schema.h b/src/impl/winmd_reader/schema.h index 837b2b5..c3ba25d 100644 --- a/src/impl/winmd_reader/schema.h +++ b/src/impl/winmd_reader/schema.h @@ -76,6 +76,8 @@ namespace winmd::reader auto EventList() const; auto MethodImplList() const; + auto EnclosingType() const; + bool is_enum() const; auto get_enum_definition() const; }; @@ -179,6 +181,7 @@ namespace winmd::reader auto CustomAttribute() const; auto Constant() const; auto Parent() const; + auto FieldMarshal() const; }; struct Param : row_base @@ -202,6 +205,7 @@ namespace winmd::reader auto CustomAttribute() const; auto Constant() const; + auto FieldMarshal() const; }; struct InterfaceImpl : row_base @@ -222,7 +226,7 @@ namespace winmd::reader { using row_base::row_base; - using constant_type = std::variant; + using constant_type = std::variant; auto Type() const { @@ -255,6 +259,11 @@ namespace winmd::reader struct FieldMarshal : row_base { using row_base::row_base; + + auto Parent() const + { + return get_coded_index(0); + } }; struct TypeSpec : row_base @@ -577,6 +586,9 @@ namespace winmd::reader struct NestedClass : row_base { using row_base::row_base; + + TypeDef NestedType() const; + TypeDef EnclosingType() const; }; struct GenericParam : row_base @@ -659,4 +671,24 @@ namespace winmd::reader { return left.Association() < right; } + + inline bool operator<(NestedClass const& left, TypeDef const& right) noexcept + { + return left.NestedType() < right; + } + + inline bool operator<(TypeDef const& left, NestedClass const& right) noexcept + { + return left < right.NestedType(); + } + + inline bool operator<(coded_index const& left, FieldMarshal const& right) noexcept + { + return left < right.Parent(); + } + + inline bool operator<(FieldMarshal const& left, coded_index const& right) noexcept + { + return left.Parent() < right; + } } diff --git a/src/impl/winmd_reader/signature.h b/src/impl/winmd_reader/signature.h index 13653bf..420f6a6 100644 --- a/src/impl/winmd_reader/signature.h +++ b/src/impl/winmd_reader/signature.h @@ -149,6 +149,43 @@ namespace winmd::reader return false; } + inline bool parse_array(table_base const*, byte_view& data) + { + auto cursor = data; + if (uncompress_enum(cursor) == ElementType::Array) + { + data = cursor; + return true; + } + return false; + } + + inline std::pair> parse_array_sizes(table_base const*, byte_view& data) + { + uint32_t const rank = uncompress_unsigned(data); + uint32_t const num_sizes = uncompress_unsigned(data); + std::vector sizes; + sizes.reserve(num_sizes); + for (uint32_t i = 0; i < num_sizes; ++i) + { + auto size = uncompress_unsigned(data); + sizes.push_back(size); + } + return { rank, sizes }; + } + + inline int parse_ptr(table_base const*, byte_view& data) + { + auto cursor = data; + int result = 0; + while(uncompress_enum(cursor) == ElementType::Ptr) + { + ++result; + data = cursor; + } + return result; + } + struct GenericTypeIndex { uint32_t index; @@ -164,10 +201,17 @@ namespace winmd::reader using value_type = std::variant, GenericTypeIndex, GenericTypeInstSig, GenericMethodTypeIndex>; TypeSig(table_base const* table, byte_view& data) : m_is_szarray(parse_szarray(table, data)) + , m_is_array(parse_array(table, data)) + , m_ptr_count(parse_ptr(table, data)) , m_cmod(parse_cmods(table, data)) , m_element_type(parse_element_type(data)) , m_type(ParseType(table, data)) - {} + { + if (m_is_array) + { + std::tie(m_array_rank, m_array_sizes) = parse_array_sizes(table, data); + } + } value_type const& Type() const noexcept { @@ -184,6 +228,26 @@ namespace winmd::reader return m_is_szarray; } + bool is_array() const noexcept + { + return m_is_array; + } + + uint32_t array_rank() const noexcept + { + return m_array_rank; + } + + std::vector const& array_sizes() const noexcept + { + return m_array_sizes; + } + + int ptr_count() const noexcept + { + return m_ptr_count; + } + private: static ElementType parse_element_type(byte_view& data) { @@ -193,9 +257,13 @@ namespace winmd::reader static value_type ParseType(table_base const* table, byte_view& data); bool m_is_szarray; + bool m_is_array; + int m_ptr_count; std::vector m_cmod; ElementType m_element_type; value_type m_type; + uint32_t m_array_rank{}; + std::vector m_array_sizes; }; inline bool is_by_ref(byte_view& data) @@ -478,6 +546,7 @@ namespace winmd::reader case ElementType::Object: case ElementType::U: case ElementType::I: + case ElementType::Void: return element_type; break; diff --git a/src/impl/winmd_reader/type_helpers.h b/src/impl/winmd_reader/type_helpers.h index 4ac14a4..0d891bb 100644 --- a/src/impl/winmd_reader/type_helpers.h +++ b/src/impl/winmd_reader/type_helpers.h @@ -30,39 +30,14 @@ namespace winmd::reader return get_base_class_namespace_and_name(type) == std::pair(typeNamespace, typeName); } - enum class category + inline bool is_nested(TypeDef const& type) { - interface_type, - class_type, - enum_type, - struct_type, - delegate_type - }; + const auto visibility = type.Flags().Visibility(); + return !(visibility == TypeVisibility::Public || visibility == TypeVisibility::NotPublic); + } - inline category get_category(TypeDef const& type) + inline bool is_nested(TypeRef const& type) { - if (type.Flags().Semantics() == TypeSemantics::Interface) - { - return category::interface_type; - } - - auto const& [extends_namespace, extends_name] = get_base_class_namespace_and_name(type); - - if (extends_name == "Enum"sv && extends_namespace == "System"sv) - { - return category::enum_type; - } - - if (extends_name == "ValueType"sv && extends_namespace == "System"sv) - { - return category::struct_type; - } - - if (extends_name == "MulticastDelegate"sv && extends_namespace == "System"sv) - { - return category::delegate_type; - } - - return category::class_type; + return type.ResolutionScope().type() == ResolutionScope::TypeRef; } } diff --git a/src/impl/winmd_reader/view.h b/src/impl/winmd_reader/view.h index 454a0ad..91d8414 100644 --- a/src/impl/winmd_reader/view.h +++ b/src/impl/winmd_reader/view.h @@ -118,6 +118,11 @@ namespace winmd::reader } } + std::u16string_view as_u16string_constant() const + { + return { reinterpret_cast(m_first), size() / 2 }; + } + template auto as_array(uint32_t const offset, uint32_t const count) const {